Re: pg_hba.conf: samehost and samenet [REVIEW] - Mailing list pgsql-hackers

From Stef Walter
Subject Re: pg_hba.conf: samehost and samenet [REVIEW]
Date
Msg-id 4AC38479.2030206@memberwebs.com
Whole thread Raw
In response to Re: pg_hba.conf: samehost and samenet [REVIEW]  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: pg_hba.conf: samehost and samenet [REVIEW]
Re: pg_hba.conf: samehost and samenet [REVIEW]
List pgsql-hackers
Tom Lane wrote:
> I was just poking at this.

Thanks for trying it out.

It seems to need rather a lot of
> editorialization (eg to fix the lack of consistency about whether
> nonstandard headers have configure tests, or bother to make use of the
> tests that did get added).

I've now added tests for sys/ioctl.h and net/if.h even though these
headers seemed to be common to all the unixes investigated.

The test for ifaddrs.h is to allow the test for getifaddrs() later in
configure.in to work. This is how other open source projects have
handled this situation, but if you'd like me to do it differently for
postgres I can.

> However, it does actually compile and appear
> to work on HPUX 10.20, which is my personal benchmark for hopeless
> obsolescence ;-).

Good news.

So modulo the issue about how much we trust the
> system-reported netmasks, it seems we could adopt this.

FWIW, there are checks for various bad netmasks. I incorporated these
techniques after seeing them in the corresponding postfix code.

BTW, there's also fallback code. If none of the methods work on a given
OS, then the ifaddrs code just lists 127.0.0.1/8 and ::1/128.

Cheers,

Stef
diff --git a/configure.in b/configure.in
index e545a1f..8b42684 100644
*** a/configure.in
--- b/configure.in
*************** AC_SUBST(OSSP_UUID_LIBS)
*** 969,975 ****
  ##

  dnl sys/socket.h is required by AC_FUNC_ACCEPT_ARGTYPES
! AC_CHECK_HEADERS([crypt.h dld.h fp_class.h getopt.h ieeefp.h langinfo.h poll.h pwd.h sys/ipc.h sys/poll.h sys/pstat.h
sys/resource.hsys/select.h sys/sem.h sys/socket.h sys/shm.h sys/tas.h sys/time.h sys/un.h termios.h ucred.h utime.h
wchar.hwctype.h kernel/OS.h kernel/image.h SupportDefs.h]) 

  # At least on IRIX, cpp test for netinet/tcp.h will fail unless
  # netinet/in.h is included first.
--- 969,984 ----
  ##

  dnl sys/socket.h is required by AC_FUNC_ACCEPT_ARGTYPES
! AC_CHECK_HEADERS([crypt.h dld.h fp_class.h getopt.h ieeefp.h langinfo.h poll.h pwd.h sys/ipc.h sys/poll.h sys/pstat.h
sys/resource.hsys/select.h sys/sem.h sys/socket.h sys/shm.h sys/tas.h sys/time.h sys/un.h termios.h ucred.h utime.h
wchar.hwctype.h kernel/OS.h kernel/image.h SupportDefs.h ifaddrs.h sys/ioctl.h sys/sockio.h]) 
!
! # On BSD, cpp test for net/if.h will fail unless sys/socket.h
! # is included first, it's checked above.
! AC_CHECK_HEADERS(net/if.h, [], [],
! [AC_INCLUDES_DEFAULT
! #ifdef HAVE_SYS_SOCKET_H
! #include <sys/socket.h>
! #endif
! ])

  # At least on IRIX, cpp test for netinet/tcp.h will fail unless
  # netinet/in.h is included first.
*************** PGAC_VAR_INT_TIMEZONE
*** 1148,1154 ****
  AC_FUNC_ACCEPT_ARGTYPES
  PGAC_FUNC_GETTIMEOFDAY_1ARG

! AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getpeereid getpeerucred getrlimit memmove poll pstat readlink setproctitle
setsidsigprocmask symlink sysconf towlower utime utimes waitpid wcstombs]) 

  # posix_fadvise() is a no-op on Solaris, so don't incur function overhead
  # by calling it, 2009-04-02
--- 1157,1163 ----
  AC_FUNC_ACCEPT_ARGTYPES
  PGAC_FUNC_GETTIMEOFDAY_1ARG

! AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getpeereid getpeerucred getrlimit memmove poll pstat readlink setproctitle
setsidsigprocmask symlink sysconf towlower utime utimes waitpid wcstombs getifaddrs]) 

  # posix_fadvise() is a no-op on Solaris, so don't incur function overhead
  # by calling it, 2009-04-02
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index ad4d084..e5152f4 100644
*** a/doc/src/sgml/client-auth.sgml
--- b/doc/src/sgml/client-auth.sgml
*************** hostnossl  <replaceable>database</replac
*** 244,249 ****
--- 244,255 ----
         support for IPv6 addresses.
        </para>

+       <para>Instead of a <replaceable>CIDR-address</replaceable>, you can specify
+        <literal>samehost</literal> to match any of the server's own IP addresses,
+        or <literal>samenet</literal> to match any address in a subnet that the
+        server belongs to.
+       </para>
+
        <para>
         This field only applies to <literal>host</literal>,
         <literal>hostssl</literal>, and <literal>hostnossl</> records.
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index e6f7db2..702971a 100644
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
*************** check_db(const char *dbname, const char
*** 512,517 ****
--- 512,608 ----
      return false;
  }

+ /*
+  * Check to see if a connecting IP matches the address and netmask.
+  */
+ static bool
+ check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
+ {
+     if (raddr->addr.ss_family == addr->sa_family)
+     {
+         /* Same address family */
+         if (!pg_range_sockaddr(&raddr->addr, (struct sockaddr_storage*)addr,
+                                (struct sockaddr_storage*)mask))
+             return false;
+     }
+ #ifdef HAVE_IPV6
+     else if (addr->sa_family == AF_INET &&
+              raddr->addr.ss_family == AF_INET6)
+     {
+         /*
+          * Wrong address family.  We allow only one case: if the file
+          * has IPv4 and the port is IPv6, promote the file address to
+          * IPv6 and try to match that way.
+          */
+         struct sockaddr_storage addrcopy,
+                     maskcopy;
+
+         memcpy(&addrcopy, &addr, sizeof(addrcopy));
+         memcpy(&maskcopy, &mask, sizeof(maskcopy));
+         pg_promote_v4_to_v6_addr(&addrcopy);
+         pg_promote_v4_to_v6_mask(&maskcopy);
+
+         if (!pg_range_sockaddr(&raddr->addr, &addrcopy, &maskcopy))
+             return false;
+     }
+ #endif   /* HAVE_IPV6 */
+     else
+     {
+         /* Wrong address family, no IPV6 */
+         return false;
+     }
+
+     return true;
+ }
+
+ typedef struct CheckNetwork {
+     NetMethod method;
+     SockAddr *raddr;
+     bool result;
+ } CheckNetwork;
+
+ static void
+ callback_check_network(struct sockaddr *addr, struct sockaddr *netmask, void *data)
+ {
+     CheckNetwork *cn = data;
+     struct sockaddr_storage mask;
+
+     /* Already found a match */
+     if (cn->result)
+         return;
+
+     /* Make a fully 1's netmask of appropriate length */
+     if (cn->method == nmSameHost)
+     {
+         pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
+         cn->result = check_ip(cn->raddr, addr, (struct sockaddr*)&mask);
+     }
+
+     /* Use the netmask of the interface itself */
+     else
+     {
+         cn->result = check_ip(cn->raddr, addr, netmask);
+     }
+ }
+
+ static bool
+ check_same_host_or_net(SockAddr *raddr, NetMethod method)
+ {
+     CheckNetwork cn;
+     cn.method = method;
+     cn.raddr = raddr;
+     cn.result = false;
+
+     if (pg_foreach_ifaddr(callback_check_network, &cn) < 0)
+     {
+         ereport(LOG,
+                 (errcode(ERRCODE_WARNING),
+                  errmsg("Error enumerating network interfaces")));
+         return false;
+     }
+
+     return cn.result;
+ }

  /*
   * Macros used to check and report on invalid configuration options.
*************** parse_hba_line(List *line, int line_num,
*** 658,756 ****
                                  line_num, HbaFileName)));
              return false;
          }
-         token = pstrdup(lfirst(line_item));

!         /* Check if it has a CIDR suffix and if so isolate it */
!         cidr_slash = strchr(token, '/');
!         if (cidr_slash)
!             *cidr_slash = '\0';
!
!         /* Get the IP address either way */
!         hints.ai_flags = AI_NUMERICHOST;
!         hints.ai_family = PF_UNSPEC;
!         hints.ai_socktype = 0;
!         hints.ai_protocol = 0;
!         hints.ai_addrlen = 0;
!         hints.ai_canonname = NULL;
!         hints.ai_addr = NULL;
!         hints.ai_next = NULL;

!         ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
!         if (ret || !gai_result)
          {
!             ereport(LOG,
!                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
!                      errmsg("invalid IP address \"%s\": %s",
!                             token, gai_strerror(ret)),
!                      errcontext("line %d of configuration file \"%s\"",
!                                 line_num, HbaFileName)));
!             if (cidr_slash)
!                 *cidr_slash = '/';
!             if (gai_result)
!                 pg_freeaddrinfo_all(hints.ai_family, gai_result);
!             return false;
          }

!         if (cidr_slash)
!             *cidr_slash = '/';
!
!         memcpy(&parsedline->addr, gai_result->ai_addr, gai_result->ai_addrlen);
!         pg_freeaddrinfo_all(hints.ai_family, gai_result);
!
!         /* Get the netmask */
!         if (cidr_slash)
          {
!             if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
!                                       parsedline->addr.ss_family) < 0)
!             {
!                 ereport(LOG,
!                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
!                          errmsg("invalid CIDR mask in address \"%s\"",
!                                 token),
!                          errcontext("line %d of configuration file \"%s\"",
!                                     line_num, HbaFileName)));
!                 return false;
!             }
          }
          else
          {
!             /* Read the mask field. */
!             line_item = lnext(line_item);
!             if (!line_item)
!             {
!                 ereport(LOG,
!                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
!                          errmsg("end-of-line before netmask specification"),
!                          errcontext("line %d of configuration file \"%s\"",
!                                     line_num, HbaFileName)));
!                 return false;
!             }
!             token = lfirst(line_item);

              ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
              if (ret || !gai_result)
              {
                  ereport(LOG,
                          (errcode(ERRCODE_CONFIG_FILE_ERROR),
!                          errmsg("invalid IP mask \"%s\": %s",
                                  token, gai_strerror(ret)),
                           errcontext("line %d of configuration file \"%s\"",
                                      line_num, HbaFileName)));
                  if (gai_result)
                      pg_freeaddrinfo_all(hints.ai_family, gai_result);
                  return false;
              }

!             memcpy(&parsedline->mask, gai_result->ai_addr, gai_result->ai_addrlen);
              pg_freeaddrinfo_all(hints.ai_family, gai_result);

!             if (parsedline->addr.ss_family != parsedline->mask.ss_family)
              {
!                 ereport(LOG,
!                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
!                          errmsg("IP address and mask do not match in file \"%s\" line %d",
!                                 HbaFileName, line_num)));
!                 return false;
              }
          }
      }                            /* != ctLocal */
--- 749,868 ----
                                  line_num, HbaFileName)));
              return false;
          }

!         /* Is it equal to 'samehost' or 'samenet'? */
!         token = lfirst(line_item);

!         /* Any IP on this host is allowed to connect */
!         if (strcmp(token, "samehost") == 0)
          {
!             parsedline->net_method = nmSameHost;
          }

!         /* Any IP on the host's subnets is allowed to connect */
!         else if (strcmp(token, "samenet") == 0)
          {
!             parsedline->net_method = nmSameNet;
          }
+
+         /* IP and netmask are specified */
          else
          {
!             parsedline->net_method = nmCompare;
!             token = pstrdup(token);
!
!             /* Check if it has a CIDR suffix and if so isolate it */
!             cidr_slash = strchr(token, '/');
!             if (cidr_slash)
!                 *cidr_slash = '\0';
!
!             /* Get the IP address either way */
!             hints.ai_flags = AI_NUMERICHOST;
!             hints.ai_family = PF_UNSPEC;
!             hints.ai_socktype = 0;
!             hints.ai_protocol = 0;
!             hints.ai_addrlen = 0;
!             hints.ai_canonname = NULL;
!             hints.ai_addr = NULL;
!             hints.ai_next = NULL;

              ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
              if (ret || !gai_result)
              {
                  ereport(LOG,
                          (errcode(ERRCODE_CONFIG_FILE_ERROR),
!                          errmsg("invalid IP address \"%s\": %s",
                                  token, gai_strerror(ret)),
                           errcontext("line %d of configuration file \"%s\"",
                                      line_num, HbaFileName)));
+                 if (cidr_slash)
+                     *cidr_slash = '/';
                  if (gai_result)
                      pg_freeaddrinfo_all(hints.ai_family, gai_result);
                  return false;
              }

!             if (cidr_slash)
!                 *cidr_slash = '/';
!
!             memcpy(&parsedline->addr, gai_result->ai_addr, gai_result->ai_addrlen);
              pg_freeaddrinfo_all(hints.ai_family, gai_result);

!             /* Get the netmask */
!             if (cidr_slash)
              {
!                 if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
!                                           parsedline->addr.ss_family) < 0)
!                 {
!                     ereport(LOG,
!                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
!                              errmsg("invalid CIDR mask in address \"%s\"",
!                                     token),
!                              errcontext("line %d of configuration file \"%s\"",
!                                         line_num, HbaFileName)));
!                     return false;
!                 }
!             }
!             else
!             {
!                 /* Read the mask field. */
!                 line_item = lnext(line_item);
!                 if (!line_item)
!                 {
!                     ereport(LOG,
!                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
!                              errmsg("end-of-line before netmask specification"),
!                              errcontext("line %d of configuration file \"%s\"",
!                                         line_num, HbaFileName)));
!                     return false;
!                 }
!                 token = lfirst(line_item);
!
!                 ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
!                 if (ret || !gai_result)
!                 {
!                     ereport(LOG,
!                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
!                              errmsg("invalid IP mask \"%s\": %s",
!                                     token, gai_strerror(ret)),
!                              errcontext("line %d of configuration file \"%s\"",
!                                         line_num, HbaFileName)));
!                     if (gai_result)
!                         pg_freeaddrinfo_all(hints.ai_family, gai_result);
!                     return false;
!                 }
!
!                 memcpy(&parsedline->mask, gai_result->ai_addr, gai_result->ai_addrlen);
!                 pg_freeaddrinfo_all(hints.ai_family, gai_result);
!
!                 if (parsedline->addr.ss_family != parsedline->mask.ss_family)
!                 {
!                     ereport(LOG,
!                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
!                              errmsg("IP address and mask do not match in file \"%s\" line %d",
!                                     HbaFileName, line_num)));
!                     return false;
!                 }
              }
          }
      }                            /* != ctLocal */
*************** check_hba(hbaPort *port)
*** 1096,1131 ****
                  continue;
  #endif

!             /* Check IP address */
!             if (port->raddr.addr.ss_family == hba->addr.ss_family)
              {
!                 if (!pg_range_sockaddr(&port->raddr.addr, &hba->addr, &hba->mask))
                      continue;
!             }
! #ifdef HAVE_IPV6
!             else if (hba->addr.ss_family == AF_INET &&
!                      port->raddr.addr.ss_family == AF_INET6)
!             {
!                 /*
!                  * Wrong address family.  We allow only one case: if the file
!                  * has IPv4 and the port is IPv6, promote the file address to
!                  * IPv6 and try to match that way.
!                  */
!                 struct sockaddr_storage addrcopy,
!                             maskcopy;
!
!                 memcpy(&addrcopy, &hba->addr, sizeof(addrcopy));
!                 memcpy(&maskcopy, &hba->mask, sizeof(maskcopy));
!                 pg_promote_v4_to_v6_addr(&addrcopy);
!                 pg_promote_v4_to_v6_mask(&maskcopy);
!
!                 if (!pg_range_sockaddr(&port->raddr.addr, &addrcopy, &maskcopy))
                      continue;
!             }
! #endif   /* HAVE_IPV6 */
!             else
!                 /* Wrong address family, no IPV6 */
                  continue;
          }                        /* != ctLocal */

          /* Check database and role */
--- 1208,1228 ----
                  continue;
  #endif

!             switch (hba->net_method)
              {
!             case nmCompare:
!                 if (!check_ip(&port->raddr, (struct sockaddr*)&hba->addr,
!                               (struct sockaddr*)&hba->mask))
                      continue;
!                 break;
!             case nmSameHost:
!             case nmSameNet:
!                 if (!check_same_host_or_net(&port->raddr, hba->net_method))
                      continue;
!                 break;
!             default:
                  continue;
+             }
          }                        /* != ctLocal */

          /* Check database and role */
diff --git a/src/backend/libpq/ip.c b/src/backend/libpq/ip.c
index 0c35ddd..c9c332b 100644
*** a/src/backend/libpq/ip.c
--- b/src/backend/libpq/ip.c
*************** range_sockaddr_AF_INET6(const struct soc
*** 333,338 ****
--- 333,340 ----
   *    pg_sockaddr_cidr_mask - make a network mask of the appropriate family
   *      and required number of significant bits
   *
+  * numbits can be null, in which case the mask is fully set.
+  *
   * The resulting mask is placed in *mask, which had better be big enough.
   *
   * Return value is 0 if okay, -1 if not.
*************** pg_sockaddr_cidr_mask(struct sockaddr_st
*** 343,352 ****
      long        bits;
      char       *endptr;

!     bits = strtol(numbits, &endptr, 10);
!
!     if (*numbits == '\0' || *endptr != '\0')
!         return -1;

      switch (family)
      {
--- 345,360 ----
      long        bits;
      char       *endptr;

!     if (numbits == NULL)
!     {
!         bits = (family == AF_INET) ? 32 : 128;
!     }
!     else
!     {
!         bits = strtol(numbits, &endptr, 10);
!         if (*numbits == '\0' || *endptr != '\0')
!             return -1;
!     }

      switch (family)
      {
*************** pg_promote_v4_to_v6_mask(struct sockaddr
*** 476,478 ****
--- 484,858 ----
  }

  #endif   /* HAVE_IPV6 */
+
+
+ static void
+ run_ifaddr_callback(PgIfAddrCallback callback, void * cb_data,
+                     struct sockaddr * addr, struct sockaddr * mask)
+ {
+     struct sockaddr_storage full;
+
+     if (!addr)
+         return;
+
+     /* Check that the mask is valid */
+     if (mask)
+     {
+         if (mask->sa_family != addr->sa_family)
+         {
+             mask = NULL;
+         }
+
+         else if (mask->sa_family == AF_INET)
+         {
+             if (((struct sockaddr_in*)mask)->sin_addr.s_addr == INADDR_ANY)
+                 mask = NULL;
+         }
+ #ifdef HAVE_IPV6
+         else if (mask->sa_family == AF_INET6)
+         {
+             if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*)mask)->sin6_addr))
+                 mask = NULL;
+         }
+ #endif
+     }
+
+     /* If mask is invalid, generate our own fully set mask */
+     if (!mask)
+     {
+         pg_sockaddr_cidr_mask(&full, NULL, addr->sa_family);
+         mask = (struct sockaddr*)&full;
+     }
+
+     (callback)(addr, mask, cb_data);
+ }
+
+ /*
+  * Enumerate network interface addresses on Win32. This uses
+  * the Winsock 2 functions (ie: ws2_32.dll)
+  */
+ #ifdef WIN32
+
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+
+ int
+ pg_foreach_ifaddr(PgIfAddrCallback callback, void * cb_data)
+ {
+     INTERFACE_INFO *ptr, *ii = NULL;
+     unsigned long length, i;
+     unsigned long n_ii = 0;
+     SOCKET sock;
+     int error;
+
+     sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
+     if (sock == SOCKET_ERROR)
+         return -1;
+
+     while (n_ii < 1024)
+     {
+         n_ii += 64;
+         ptr = realloc(ii, sizeof (INTERFACE_INFO) * n_ii);
+         if (!ptr)
+         {
+             free(ii);
+             closesocket(sock);
+             errno = ENOMEM;
+             return -1;
+         }
+
+         ii = ptr;
+         if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, 0, 0,
+                      ii, n_ii * sizeof (INTERFACE_INFO),
+                      &length, 0, 0) == SOCKET_ERROR)
+         {
+             error = WSAGetLastError();
+             if (error == WSAEFAULT || error == WSAENOBUFS)
+                 continue;
+             closesocket(sock);
+             free(ii);
+             return -1;
+         }
+
+         break;
+     }
+
+     for (i = 0; i < length / sizeof(INTERFACE_INFO); ++i)
+         run_ifaddr_callback(callback, cb_data,
+                             (struct sockaddr*)&ii[i].iiAddress,
+                             (struct sockaddr*)&ii[i].iiNetmask);
+
+     closesocket(sock);
+     free(ii);
+     return 0;
+ }
+
+ /*
+  * Enumerate interfaces on BSDs, AIX and modern Linux.
+  * Uses the getifaddrs() interface, clean and simple.
+  */
+
+ #elif HAVE_GETIFADDRS /* && !WIN32 */
+
+ #include <ifaddrs.h>
+
+ int
+ pg_foreach_ifaddr(PgIfAddrCallback callback, void * cb_data)
+ {
+     struct ifaddrs *ifa, *l;
+
+     if (getifaddrs(&ifa) < 0)
+         return -1;
+
+     for (l = ifa; l; l = l->ifa_next)
+         run_ifaddr_callback(callback, cb_data,
+                             l->ifa_addr, l->ifa_netmask);
+
+     freeifaddrs(ifa);
+     return 0;
+ }
+
+ #else /* !HAVE_GETIFADDRS && !WIN32 */
+
+ #ifdef HAVE_SYS_IOCTL_H
+ #include <sys/ioctl.h>
+ #endif
+
+ #ifdef HAVE_NET_IF_H
+ #include <net/if.h>
+ #endif
+
+ #ifdef HAVE_SYS_SOCKIO_H
+ #include <sys/sockio.h>
+ #endif
+
+ /*
+  * SIOCGIFCONF does not return IPv6 addresses on Solaris,
+  * and HP/UX. So we use SIOCGLIFCONF if it's available.
+  */
+
+ #if defined(SIOCGLIFCONF)
+
+ int
+ pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
+ {
+     struct lifconf lifc;
+     struct lifreq *lifr, lmask;
+     struct sockaddr *addr, *mask;
+     char *ptr, *buffer = NULL;
+     size_t n_buffer = 1024;
+     int sock, fd;
+ #ifdef HAVE_IPV6
+     int sock6;
+ #endif
+     int i, total;
+
+     sock = socket(AF_INET, SOCK_DGRAM, 0);
+     if (sock == -1)
+         return -1;
+
+     while(n_buffer < 1024 * 100)
+     {
+         n_buffer += 1024;
+         ptr = realloc(buffer, n_buffer);
+         if (!ptr)
+         {
+             free(buffer);
+             close(sock);
+             errno = ENOMEM;
+             return -1;
+         }
+
+         memset(&lifc, 0, sizeof (lifc));
+         lifc.lifc_family = AF_UNSPEC;
+         lifc.lifc_len = n_buffer;
+         lifc.lifc_buf = buffer = ptr;
+
+         if (ioctl(sock, SIOCGLIFCONF, &lifc) < 0)
+         {
+             if (errno == EINVAL)
+                 continue;
+             free(buffer);
+             close(sock);
+             return -1;
+         }
+
+         /*
+          * Some Unixes try to return as much data as possible,
+          * no indication of whether enough space allocated.
+          */
+         if (lifc.lifc_len < n_buffer - 1024)
+             break;
+     }
+
+ #ifdef HAVE_IPV6
+     sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
+     if (sock6 == -1)
+     {
+         free(buffer);
+         close(sock);
+         return -1;
+     }
+ #endif
+
+     total = lifc.lifc_len / sizeof(struct lifreq);
+     lifr = lifc.lifc_req;
+     for (i = 0; i < total; ++i)
+     {
+         addr = (struct sockaddr*)&lifr[i].lifr_addr;
+         memcpy(&lmask, &lifr[i], sizeof (struct lifreq));
+ #ifdef HAVE_IPV6
+         fd = addr->sa_family == AF_INET6 ? sock6 : sock;
+ #else
+         fd = sock;
+ #endif
+         if (ioctl(fd, SIOCGLIFNETMASK, &lmask) < 0)
+             mask = NULL;
+         else
+             mask = (struct sockaddr*)&lmask.lifr_addr;
+         run_ifaddr_callback(callback, cb_data, addr, mask);
+     }
+
+     free(buffer);
+     close(sock);
+ #ifdef HAVE_IPV6
+     close(sock6);
+ #endif
+     return 0;
+ }
+
+ /*
+  * Remaining Unixes use SIOCGIFCONF. Some only return IPv4 information
+  * here, so this is the least preferred method. Note that there is no
+  * standard way to iterate the struct ifreq returned in the array.
+  * On some OSs the structures are padded large enough for any address,
+  * on others you have to calculate the size of the struct ifreq.
+  */
+
+ #elif defined(SIOCGIFCONF)
+
+ /* Some OSs have _SIZEOF_ADDR_IFREQ, so just use that */
+ #ifndef _SIZEOF_ADDR_IFREQ
+
+ /* Calculate based on sockaddr.sa_len */
+ #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ #define _SIZEOF_ADDR_IFREQ(ifr) \
+         ((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \
+          (sizeof(struct ifreq) - sizeof(struct sockaddr) + \
+           (ifr).ifr_addr.sa_len) : sizeof(struct ifreq))
+
+ /* Padded ifreq structure, simple */
+ #else
+ #define _SIZEOF_ADDR_IFREQ(ifr) \
+     sizeof (struct ifreq)
+
+ #endif
+ #endif
+
+ int
+ pg_foreach_ifaddr(PgIfAddrCallback callback, void * cb_data)
+ {
+     struct ifconf ifc;
+     struct ifreq *ifr, *end, addr, mask;
+     char *ptr, *buffer = NULL;
+     size_t n_buffer = 1024;
+     int sock;
+
+     sock = socket(AF_INET, SOCK_DGRAM, 0);
+     if (sock == -1)
+         return -1;
+
+     while(n_buffer < 1024 * 100)
+     {
+         n_buffer += 1024;
+         ptr = realloc(buffer, n_buffer);
+         if (!ptr)
+         {
+             free(buffer);
+             close(sock);
+             errno = ENOMEM;
+             return -1;
+         }
+
+         memset(&ifc, 0, sizeof (ifc));
+         ifc.ifc_buf = buffer = ptr;
+         ifc.ifc_len = n_buffer;
+
+         if (ioctl(sock, SIOCGIFCONF, &ifc) < 0)
+         {
+             if (errno == EINVAL)
+                 continue;
+             free(buffer);
+             close(sock);
+             return -1;
+         }
+
+         /*
+          * Some Unixes try to return as much data as possible,
+          * no indication of whether enough space allocated.
+          */
+         if (ifc.ifc_len < n_buffer - 1024)
+             break;
+     }
+
+     end = (struct ifreq*)(buffer + ifc.ifc_len);
+     for (ifr = ifc.ifc_req; ifr < end;)
+     {
+         memcpy(&addr, ifr, sizeof(addr));
+         memcpy(&mask, ifr, sizeof(mask));
+         if (ioctl(sock, SIOCGIFADDR, &addr, sizeof(addr)) == 0 &&
+             ioctl(sock, SIOCGIFNETMASK, &mask, sizeof(mask)) == 0)
+             run_ifaddr_callback(callback, cb_data,
+                                 &addr.ifr_addr, &mask.ifr_addr);
+         ifr = (struct ifreq*)((char*)ifr + _SIZEOF_ADDR_IFREQ(*ifr));
+     }
+
+     free(buffer);
+     close(sock);
+     return 0;
+ }
+
+ #else /* !defined(SIOCGIFCONF) */
+
+ /*
+  * No known way to figure out the addresses on the local machine.
+  * So we return the loopbacks for each family.
+  */
+
+ int
+ pg_foreach_ifaddr(PgIfAddrCallback callback, void * cb_data)
+ {
+     struct sockaddr_in addr;
+     struct sockaddr_storage mask;
+ #ifdef HAVE_IPV6
+     struct sockaddr_in6 addr6;
+ #endif
+
+     /* addr 127.0.0.1 mask 255.0.0.0 */
+     memset(&addr, 0, sizeof(addr));
+     addr.sin_family = AF_INET;
+     addr.sin_addr.s_addr = ntohl(0x7f000001);
+     memset(&mask, 0, sizeof(mask));
+     pg_sockaddr_cidr_mask(&mask, "8", AF_INET);
+     run_ifaddr_callback(callback, cb_data,
+                         (struct sockaddr*)&addr,
+                         (struct sockaddr*)&mask);
+
+ #ifdef HAVE_IPV6
+     /* addr ::1/128 */
+     memset(&addr6, 0, sizeof(addr6));
+     addr6.sin6_family = AF_INET6;
+     addr6.sin6_addr.s6_addr[15] = 1;
+     memset(&mask, 0, sizeof(mask));
+     pg_sockaddr_cidr_mask(&mask, "128", AF_INET6);
+     run_ifaddr_callback(callback, cb_data,
+                         (struct sockaddr*)&addr6,
+                         (struct sockaddr*)&mask);
+ #endif
+
+     return 0;
+ }
+
+ #endif /* !defined(SIOCGIFCONF) */
+
+ #endif /* !HAVE_GETIFADDRS */
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index f1c0457..65966da 100644
*** a/src/backend/libpq/pg_hba.conf.sample
--- b/src/backend/libpq/pg_hba.conf.sample
***************
*** 33,38 ****
--- 33,41 ----
  # (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that specifies
  # the number of significant bits in the mask.  Alternatively, you can write
  # an IP address and netmask in separate columns to specify the set of hosts.
+ # Instead of a CIDR-address, you can specify "samehost" to match any of the
+ # server's own IP addresses, or "samenet" to match any address in a subnet that
+ # the server belongs to.
  #
  # METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", "krb5",
  # "ident", "pam", "ldap" or "cert".  Note that "password" sends passwords
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index b538ee4..5193f38 100644
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
*************** typedef enum UserAuth
*** 30,35 ****
--- 30,42 ----
      uaCert
  } UserAuth;

+ typedef enum NetMethod
+ {
+     nmCompare,
+     nmSameHost,
+     nmSameNet
+ } NetMethod;
+
  typedef enum ConnType
  {
      ctLocal,
*************** typedef struct
*** 44,49 ****
--- 51,57 ----
      ConnType    conntype;
      char       *database;
      char       *role;
+     NetMethod    net_method;
      struct sockaddr_storage addr;
      struct sockaddr_storage mask;
      UserAuth    auth_method;
diff --git a/src/include/libpq/ip.h b/src/include/libpq/ip.h
index 1934957..9bd562c 100644
*** a/src/include/libpq/ip.h
--- b/src/include/libpq/ip.h
*************** extern void pg_promote_v4_to_v6_mask(str
*** 47,50 ****
--- 47,56 ----
  #define IS_AF_UNIX(fam) (0)
  #endif

+ typedef void (*PgIfAddrCallback)(struct sockaddr * addr,
+                                  struct sockaddr * netmask,
+                                  void * cb_data);
+
+ extern int pg_foreach_ifaddr(PgIfAddrCallback callback, void * cb_data);
+
  #endif   /* IP_H */
diff --git a/src/tools/ifaddrs/Makefile b/src/tools/ifaddrs/Makefile
index ...4ec26c1 .
*** a/src/tools/ifaddrs/Makefile
--- b/src/tools/ifaddrs/Makefile
***************
*** 0 ****
--- 1,26 ----
+ #-------------------------------------------------------------------------
+ #
+ # Makefile for src/tools/ifaddrs
+ #
+ # Copyright (c) 2003-2009, PostgreSQL Global Development Group
+ #
+ # $PostgreSQL$
+ #
+ #-------------------------------------------------------------------------
+
+ subdir = src/tools/ifaddrs
+ top_builddir = ../../..
+ include $(top_builddir)/src/Makefile.global
+
+ libpq_backdir = $(top_builddir)/src/backend/libpq/
+ override CPPFLAGS := -I$(libpq_backdir) $(CPPFLAGS)
+
+ OBJS= test_ifaddrs.o
+
+ all: test_ifaddrs
+
+ test_ifaddrs: test_ifaddrs.o $(libpq_backdir)/ip.o
+     $(CC) $(CFLAGS) test_ifaddrs.o $(libpq_backdir)/ip.o $(LDFLAGS) $(LIBS) -o $@$(X)
+
+ clean distclean maintainer-clean:
+     rm -f test_ifaddrs$(X) $(OBJS)
diff --git a/src/tools/ifaddrs/README b/src/tools/ifaddrs/README
index ...98e84ff .
*** a/src/tools/ifaddrs/README
--- b/src/tools/ifaddrs/README
***************
*** 0 ****
--- 1,10 ----
+ $PostgreSQL$
+
+ ifaddrs
+ =======
+
+ This program looks up all the IPv4 and IPv6 interfaces on the local machine. It is useful for testing that this
functionalityworks on various platforms. 
+
+     Usage:    test_ifaddrs
+
+
diff --git a/src/tools/ifaddrs/test_ifaddrs.c b/src/tools/ifaddrs/test_ifaddrs.c
index ...4e77cbd .
*** a/src/tools/ifaddrs/test_ifaddrs.c
--- b/src/tools/ifaddrs/test_ifaddrs.c
***************
*** 0 ****
--- 1,72 ----
+ /*
+  * $PostgreSQL$
+  *
+  *
+  *    test_ifaddrs.c
+  *        test pg_foreach_ifaddr()
+  */
+
+ #include "postgres.h"
+
+ #include "libpq/ip.h"
+
+ #include <sys/types.h>
+ #include <sys/socket.h>
+
+ #include <errno.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+
+ static void
+ print_addr(struct sockaddr *addr)
+ {
+     char buffer[256];
+     int ret, len;
+
+     switch (addr->sa_family)
+     {
+     case AF_INET:
+         len = sizeof (struct sockaddr_in);
+         break;
+     case AF_INET6:
+         len = sizeof (struct sockaddr_in6);
+         break;
+     default:
+         len = sizeof (struct sockaddr_storage);
+         break;
+     }
+
+     ret = getnameinfo(addr, len, buffer, 256, NULL, 0, NI_NUMERICHOST);
+     if (ret != 0)
+         printf ("[unknown:%d]", addr->sa_family);
+     else
+         printf ("%s", buffer);
+ }
+
+ static void
+ callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
+ {
+     printf ("addr: ");
+     print_addr (addr);
+     printf ("  mask: ");
+     print_addr (mask);
+     printf ("\n");
+ }
+
+ int
+ main(int argc, char *argv[])
+ {
+ #ifdef WIN32
+     WSADATA wsaData;
+     if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
+         fprintf (stderr, "WSAStartup failed\n");
+     else
+ #endif
+     if (pg_foreach_ifaddr (callback, NULL) < 0)
+         fprintf (stderr, "pg_foreach_ifaddr failed: %s\n", strerror (errno));
+     return 0;
+ }
+
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index f5a01b3..2187420 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 147,152 ****
--- 147,153 ----
      $libpq->AddIncludeDir('src\port');
      $libpq->AddLibrary('wsock32.lib');
      $libpq->AddLibrary('secur32.lib');
+     $libpq->AddLibrary('ws2_32.lib');
      $libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
      $libpq->UseDef('src\interfaces\libpq\libpqdll.def');
      $libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c','src\interfaces\libpq\libpq.rc');

pgsql-hackers by date:

Previous
From: Andrew Dunstan
Date:
Subject: Re: Unicode UTF-8 table formatting for psql text output
Next
From: Joe Conway
Date:
Subject: Re: CommitFest 2009-09, two weeks on