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 4AC12656.3060600@memberwebs.com
Whole thread Raw
In response to Re: pg_hba.conf: samehost and samenet [REVIEW]  (Robert Haas <robertmhaas@gmail.com>)
Responses Re: pg_hba.conf: samehost and samenet [REVIEW]
List pgsql-hackers
Whoops I missed this email...

Robert Haas wrote:
> Rereading the thread, it seems that the main question is whether there
> are any platforms that we support that have neither getifaddrs or
> SIOCGIFCONF, or where they don't work properly.

As far as I can tell, there are no non-ancient mainstream platforms that
we're missing here. As Tom suggested, I've looked over postfix, bind and
pcap and merged what I've learned into the (attached) samenet patch. I
believe we're hitting all the majors here:

 * Win32 using win_wsa2.dll
 * Modern versions of: Linux, BSD, Mac OS X, AIX using getifaddrs
 * Modern Solaris and HPUX using ioctl/SIOCGLIFCONF
 * Older unixes (BSD, Linux, Solaris, AIX) using ioctl/SIOCGIFCONF

SIOCGIFCONF doesn't return IPv6 information on certain platforms (such
as modern Solaris, or older Linux).

I believe we're covering every single Unix in use out there. I have
however only verified this assertion on open source OS's. I've also
verified that the SIOCGIFCONF method on Linux, BSD and Solaris, even
though they use more modern methods by default.

If a problem occurs with this code the src/tools/ifaddrs tool can be
used to diagnose the problem, and send in debugging feedback.

> By the way, in foreach_ifaddr_ifconf, what happens if the number of
> addresses is too large to fit in the arbitrary-size buffer you've
> chosen here?

The old approach was not a security vulnerability, and I find it hard to
believe that anyone would have had more than 10K of addresses. However
for the sake of completeness attached is a patch with dynamically sized
buffers. This adds some code complexity, but maybe someone out there
would have run into this (extremely) edge case.

I believe this patch to be complete, and am looking forward to review.

Cheers,

Stef

diff --git a/configure.in b/configure.in
index e545a1f..5182714 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,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 ifaddrs.h sys/sockio.h]) 

  # 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
--- 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 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..25098a8 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,820 ----
  }

  #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 */
+
+ #include <sys/ioctl.h>
+ #include <net/if.h>
+
+ #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.
+  */
+
+ #ifdef 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) */
+
+ int
+ pg_foreach_ifaddr(PgIfAddrCallback callback, void * cb_data)
+ {
+     return -1;
+ }
+
+ #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: Josh Berkus
Date:
Subject: Re: [PATCH] DefaultACLs
Next
From: Devrim GÜNDÜZ
Date:
Subject: Small patch for README