libexport.a: Prepare init_subnetwork() for IPv6 support
authorChuck Lever <chuck.lever@oracle.com>
Tue, 31 Aug 2010 19:29:02 +0000 (15:29 -0400)
committerSteve Dickson <steved@redhat.com>
Tue, 31 Aug 2010 19:34:24 +0000 (15:34 -0400)
Retire the slash32 logic in inet_netmask() in favor of a more generic
netmask parser that can support IPv6 addresses.

If an invalid IP address string is given to inet_addr(3), it returns
INADDR_NONE, which is actually a "valid" address (255.255.255.255).
We're none the wiser to the substitution until something breaks later.

This patch provides better sanity checking of the parsed address, now
that such an error can be reported to client_init()'s callers.
We can also check the prefixlen value a little more carefully as well.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
support/export/client.c

index a89142d..780c74d 100644 (file)
@@ -17,6 +17,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <netdb.h>
+#include <errno.h>
 
 #include "misc.h"
 #include "nfslib.h"
@@ -58,25 +59,58 @@ client_free(nfs_client *clp)
 }
 
 static int
-init_netmask(nfs_client *clp, const char *slash)
+init_netmask(nfs_client *clp, const char *slash, const sa_family_t family)
 {
        struct sockaddr_in sin = {
                .sin_family             = AF_INET,
        };
+       unsigned long prefixlen;
+       uint32_t shift;
 
-       if (strchr(slash + 1, '.') != NULL)
-               sin.sin_addr.s_addr = inet_addr(slash + 1);
-       else {
-               int prefixlen = atoi(slash + 1);
-               if (0 < prefixlen && prefixlen <= 32)
-                       sin.sin_addr.s_addr =
-                                       htonl((uint32_t)~0 << (32 - prefixlen));
-               else
+       /* No slash present; assume netmask is all ones */
+       if (slash == NULL) {
+               switch (family) {
+               case AF_INET:
+                       prefixlen = 32;
+                       break;
+               default:
+                       goto out_badfamily;
+               }
+       } else {
+               char *endptr;
+
+               /* A spelled out netmask address, perhaps? */
+               if (strchr(slash + 1, '.') != NULL) {
+                       if (inet_pton(AF_INET, slash + 1,
+                                               &sin.sin_addr.s_addr) == 0)
+                               goto out_badmask;
+                       set_addrlist_in(clp, 1, &sin);
+                       return 1;
+               }
+
+               /* A prefixlen was given */
+               prefixlen = strtoul(slash + 1, &endptr, 10);
+               if (*endptr != '\0' && prefixlen != ULONG_MAX && errno != ERANGE)
                        goto out_badprefix;
        }
 
-       set_addrlist_in(clp, 1, &sin);
-       return 1;
+       switch (family) {
+       case AF_INET:
+               if (prefixlen > 32)
+                       goto out_badprefix;
+               shift = 32 - (uint32_t)prefixlen;
+               sin.sin_addr.s_addr = htonl((uint32_t)~0 << shift);
+               set_addrlist_in(clp, 1, &sin);
+               return 1;
+       }
+
+out_badfamily:
+       xlog(L_ERROR, "Unsupported address family for %s", clp->m_hostname);
+       return 0;
+
+out_badmask:
+       xlog(L_ERROR, "Invalid netmask `%s' for %s", slash + 1, clp->m_hostname);
+       return 0;
 
 out_badprefix:
        xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname);
@@ -86,25 +120,28 @@ out_badprefix:
 static int
 init_subnetwork(nfs_client *clp)
 {
-       static char slash32[] = "/32";
        struct addrinfo *ai;
-       char *cp;
-
-       cp = strchr(clp->m_hostname, '/');
-       if (cp == NULL)
-               cp = slash32;
-
-       *cp = '\0';
-       ai = host_pton(clp->m_hostname);
-       *cp = '/';
+       sa_family_t family;
+       char *slash;
+
+       slash = strchr(clp->m_hostname, '/');
+       if (slash != NULL) {
+               *slash = '\0';
+               ai = host_pton(clp->m_hostname);
+               *slash = '/';
+       } else
+               ai = host_pton(clp->m_hostname);
        if (ai == NULL) {
                xlog(L_ERROR, "Invalid IP address %s", clp->m_hostname);
                return false;
        }
+
        set_addrlist(clp, 0, ai->ai_addr);
+       family = ai->ai_addr->sa_family;
+
        freeaddrinfo(ai);
 
-       return init_netmask(clp, cp);
+       return init_netmask(clp, slash, family);
 }
 
 static int