Re: IPV4 addresses on IPV6 machines in pg_hba.conf - Mailing list pgsql-patches

From Tom Lane
Subject Re: IPV4 addresses on IPV6 machines in pg_hba.conf
Date
Msg-id 14993.1062794082@sss.pgh.pa.us
Whole thread Raw
In response to Re: IPV4 addresses on IPV6 machines in pg_hba.conf  (Andreas Pflug <pgadmin@pse-consulting.de>)
Responses Re: IPV4 addresses on IPV6 machines in pg_hba.conf
List pgsql-patches
Andreas Pflug <pgadmin@pse-consulting.de> writes:
> Andrew Dunstan wrote:
>> You should check that the CIDR mask is a valid integer. You would need
>> to use strtol() rather than atoi() to do that. Perhaps this should be
>> hoisted out of ip.c:SockAddr_cidr_mask() and put in hba.c.

> Right, I added this.

I thought this was still really messy, so I modified it to use a
separate "promote v4 address to v6" subroutine.  I've applied the
attached patch (plus docs).  It's not very well tested since I don't
have an IPv6 setup here; please check that it does what you want.

            regards, tom lane

*** src/backend/libpq/hba.c.orig    Fri Sep  5 10:35:54 2003
--- src/backend/libpq/hba.c    Fri Sep  5 16:24:41 2003
***************
*** 673,685 ****
          if (cidr_slash)
              *cidr_slash = '/';

-         if (file_ip_addr->ai_family != port->raddr.addr.ss_family)
-         {
-             /* Wrong address family. */
-             freeaddrinfo_all(hints.ai_family, file_ip_addr);
-             return;
-         }
-
          /* Get the netmask */
          if (cidr_slash)
          {
--- 673,678 ----
***************
*** 703,708 ****
--- 696,723 ----

              if (file_ip_addr->ai_family != mask->ss_family)
                  goto hba_syntax;
+         }
+
+         if (file_ip_addr->ai_family != port->raddr.addr.ss_family)
+         {
+             /*
+              * 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.
+              */
+ #ifdef HAVE_IPV6
+             if (file_ip_addr->ai_family == AF_INET &&
+                 port->raddr.addr.ss_family == AF_INET6)
+             {
+                 promote_v4_to_v6_addr((struct sockaddr_storage *) file_ip_addr->ai_addr);
+                 promote_v4_to_v6_mask(mask);
+             }
+             else
+ #endif /* HAVE_IPV6 */
+             {
+                 freeaddrinfo_all(hints.ai_family, file_ip_addr);
+                 return;
+             }
          }

          /* Read the rest of the line. */
*** src/backend/libpq/ip.c.orig    Sun Aug  3 23:00:36 2003
--- src/backend/libpq/ip.c    Fri Sep  5 16:24:42 2003
***************
*** 34,40 ****
  #endif
  #include <arpa/inet.h>
  #include <sys/file.h>
! #endif

  #include "libpq/ip.h"

--- 34,41 ----
  #endif
  #include <arpa/inet.h>
  #include <sys/file.h>
!
! #endif /* !defined(_MSC_VER) && !defined(__BORLANDC__) */

  #include "libpq/ip.h"

***************
*** 265,273 ****
--- 266,281 ----

      return 0;
  }
+
  #endif   /* HAVE_UNIX_SOCKETS */


+ /*
+  * rangeSockAddr - is addr within the subnet specified by netaddr/netmask ?
+  *
+  * Note: caller must already have verified that all three addresses are
+  * in the same address family; and AF_UNIX addresses are not supported.
+  */
  int
  rangeSockAddr(const struct sockaddr_storage * addr,
                const struct sockaddr_storage * netaddr,
***************
*** 287,292 ****
--- 295,333 ----
          return 0;
  }

+ static int
+ rangeSockAddrAF_INET(const struct sockaddr_in * addr,
+                      const struct sockaddr_in * netaddr,
+                      const struct sockaddr_in * netmask)
+ {
+     if (((addr->sin_addr.s_addr ^ netaddr->sin_addr.s_addr) &
+          netmask->sin_addr.s_addr) == 0)
+         return 1;
+     else
+         return 0;
+ }
+
+
+ #ifdef HAVE_IPV6
+ static int
+ rangeSockAddrAF_INET6(const struct sockaddr_in6 * addr,
+                       const struct sockaddr_in6 * netaddr,
+                       const struct sockaddr_in6 * netmask)
+ {
+     int            i;
+
+     for (i = 0; i < 16; i++)
+     {
+         if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) &
+              netmask->sin6_addr.s6_addr[i]) != 0)
+             return 0;
+     }
+
+     return 1;
+ }
+
+ #endif
+
  /*
   *    SockAddr_cidr_mask - make a network mask of the appropriate family
   *      and required number of significant bits
***************
*** 358,391 ****
      return 0;
  }

! static int
! rangeSockAddrAF_INET(const struct sockaddr_in * addr, const struct sockaddr_in * netaddr,
!                      const struct sockaddr_in * netmask)
  {
!     if (((addr->sin_addr.s_addr ^ netaddr->sin_addr.s_addr) &
!          netmask->sin_addr.s_addr) == 0)
!         return 1;
!     else
!         return 0;
! }


! #ifdef HAVE_IPV6
! static int
! rangeSockAddrAF_INET6(const struct sockaddr_in6 * addr,
!                       const struct sockaddr_in6 * netaddr,
!                       const struct sockaddr_in6 * netmask)
  {
      int            i;

!     for (i = 0; i < 16; i++)
!     {
!         if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) &
!              netmask->sin6_addr.s6_addr[i]) != 0)
!             return 0;
!     }

!     return 1;
  }

! #endif
--- 399,472 ----
      return 0;
  }

!
! #ifdef HAVE_IPV6
!
! /*
!  * promote_v4_to_v6_addr --- convert an AF_INET addr to AF_INET6, using
!  *        the standard convention for IPv4 addresses mapped into IPv6 world
!  *
!  * The passed addr is modified in place.  Note that we only worry about
!  * setting the fields that rangeSockAddr will look at.
!  */
! void
! promote_v4_to_v6_addr(struct sockaddr_storage * addr)
  {
!     struct sockaddr_in addr4;
!     struct sockaddr_in6 addr6;
!     uint32        s_addr;

+     memcpy(&addr4, addr, sizeof(addr4));
+     s_addr = ntohl(addr4.sin_addr.s_addr);

!     memset(&addr6, 0, sizeof(addr6));
!
!     addr6.sin6_family = AF_INET6;
!
!     addr6.sin6_addr.s6_addr[10] = 0xff;
!     addr6.sin6_addr.s6_addr[11] = 0xff;
!     addr6.sin6_addr.s6_addr[12] = (s_addr >> 24) & 0xFF;
!     addr6.sin6_addr.s6_addr[13] = (s_addr >> 16) & 0xFF;
!     addr6.sin6_addr.s6_addr[14] = (s_addr >> 8) & 0xFF;
!     addr6.sin6_addr.s6_addr[15] = (s_addr) & 0xFF;
!
!     memcpy(addr, &addr6, sizeof(addr6));
! }
!
! /*
!  * promote_v4_to_v6_mask --- convert an AF_INET netmask to AF_INET6, using
!  *        the standard convention for IPv4 addresses mapped into IPv6 world
!  *
!  * This must be different from promote_v4_to_v6_addr because we want to
!  * set the high-order bits to 1's not 0's.
!  *
!  * The passed addr is modified in place.  Note that we only worry about
!  * setting the fields that rangeSockAddr will look at.
!  */
! void
! promote_v4_to_v6_mask(struct sockaddr_storage * addr)
  {
+     struct sockaddr_in addr4;
+     struct sockaddr_in6 addr6;
+     uint32        s_addr;
      int            i;

!     memcpy(&addr4, addr, sizeof(addr4));
!     s_addr = ntohl(addr4.sin_addr.s_addr);

!     memset(&addr6, 0, sizeof(addr6));
!
!     addr6.sin6_family = AF_INET6;
!
!     for (i = 0; i < 12; i++)
!         addr6.sin6_addr.s6_addr[i] = 0xff;
!
!     addr6.sin6_addr.s6_addr[12] = (s_addr >> 24) & 0xFF;
!     addr6.sin6_addr.s6_addr[13] = (s_addr >> 16) & 0xFF;
!     addr6.sin6_addr.s6_addr[14] = (s_addr >> 8) & 0xFF;
!     addr6.sin6_addr.s6_addr[15] = (s_addr) & 0xFF;
!
!     memcpy(addr, &addr6, sizeof(addr6));
  }

! #endif /* HAVE_IPV6 */
*** src/include/libpq/ip.h.orig    Sun Aug  3 23:01:33 2003
--- src/include/libpq/ip.h    Fri Sep  5 16:24:36 2003
***************
*** 33,38 ****
--- 33,43 ----
  extern int SockAddr_cidr_mask(struct sockaddr_storage ** mask,
                     char *numbits, int family);

+ #ifdef HAVE_IPV6
+ extern void promote_v4_to_v6_addr(struct sockaddr_storage * addr);
+ extern void promote_v4_to_v6_mask(struct sockaddr_storage * addr);
+ #endif
+
  #ifdef    HAVE_UNIX_SOCKETS
  #define IS_AF_UNIX(fam) ((fam) == AF_UNIX)
  #else

pgsql-patches by date:

Previous
From: Peter Eisentraut
Date:
Subject: Re: minor documentation improvements
Next
From: Peter Eisentraut
Date:
Subject: Re: Warning for missing createlang