You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Jeff Trawick <tr...@bellsouth.net> on 2001/03/14 14:13:37 UTC

[PRE-PATCH] move ip subnet details from mod_access to APR

(and handle IPv4-mapped IPv6 addresses as well as normal IPv6
addresses properly)

Various programs which communicate over an IP network must evaluate an
incoming connection against a configured IP subnet to determine
whether or not the client can access data.  mod_access and TCP
wrappers are examples of such programs.

The current mod_access implementation has problems with IPv6.  It
seems appropriate to move the logic to APR as IPv6 support is added so
that other applications can use the same logic and so that less
IPv4/IPv6 handling is required in the application.

The APR routines will handle the same type of subnet definitions as
the current mod_access:

  a.b.c.d
  a.b.c
  a.b
  a
  a.b.c.d/w.x.y.z
  a.b.c.d/n

In addition, IPv6 subnet definitions will be allowed:

  v6-address-string
  v6-address-string/n

This patch doesn't include the APR implementation nor the APR test
program for IP subnet define/check, as they are not finished yet.

Comments?

Index: modules/aaa/mod_access.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/aaa/mod_access.c,v
retrieving revision 1.30
diff -u -r1.30 mod_access.c
--- modules/aaa/mod_access.c	2001/03/09 20:30:31	1.30
+++ modules/aaa/mod_access.c	2001/03/14 12:50:03
@@ -94,10 +94,7 @@
     int limited;
     union {
 	char *from;
-	struct {
-	    unsigned long net;
-	    unsigned long mask;
-	} ip;
+        apr_ipsubnet_t *ip;
     } x;
     enum allowdeny_type type;
 } allowdeny;
@@ -150,20 +147,14 @@
     return NULL;
 }
 
-static int is_ip(const char *host)
-{
-    while ((*host == '.') || apr_isdigit(*host))
-	host++;
-    return (*host == '\0');
-}
-
 static const char *allow_cmd(cmd_parms *cmd, void *dv, const char *from, 
                              const char *where_c)
 {
     access_dir_conf *d = (access_dir_conf *) dv;
     allowdeny *a;
-    char *s;
     char *where = apr_pstrdup(cmd->pool, where_c);
+    char *s;
+    apr_status_t rv;
 
     if (strcasecmp(from, "from"))
 	return "allow and deny must be followed by 'from'";
@@ -179,90 +170,25 @@
     }
     else if (!strcasecmp(where, "all")) {
 	a->type = T_ALL;
-
     }
     else if ((s = strchr(where, '/'))) {
-	unsigned long mask;
-
-	a->type = T_IP;
-	/* trample on where, we won't be using it any more */
-	*s++ = '\0';
-
-	if (!is_ip(where)
-	    || (a->x.ip.net = apr_inet_addr(where)) == APR_INADDR_NONE) {
-	    a->type = T_FAIL;
-	    return "syntax error in network portion of network/netmask";
-	}
-
-	/* is_ip just tests if it matches [\d.]+ */
-	if (!is_ip(s)) {
-	    a->type = T_FAIL;
-	    return "syntax error in mask portion of network/netmask";
-	}
-	/* is it in /a.b.c.d form? */
-	if (strchr(s, '.')) {
-	    mask = apr_inet_addr(s);
-	    if (mask == APR_INADDR_NONE) {
-		a->type = T_FAIL;
-		return "syntax error in mask portion of network/netmask";
-	    }
-	}
-	else {
-	    /* assume it's in /nnn form */
-	    mask = atoi(s);
-	    if (mask > 32 || mask <= 0) {
-		a->type = T_FAIL;
-		return "invalid mask in network/netmask";
-	    }
-	    mask = 0xFFFFFFFFUL << (32 - mask);
-	    mask = htonl(mask);
-	}
-	a->x.ip.mask = mask;
-        a->x.ip.net  = (a->x.ip.net & mask);   /* pjr - This fixes PR 4770 */
-    }
-    else if (apr_isdigit(*where) && is_ip(where)) {
-	/* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */
-	int shift;
-	char *t;
-	int octet;
-
-	a->type = T_IP;
-	/* parse components */
-	s = where;
-	a->x.ip.net = 0;
-	a->x.ip.mask = 0;
-	shift = 24;
-	while (*s) {
-	    t = s;
-	    if (!apr_isdigit(*t)) {
-		a->type = T_FAIL;
-		return "invalid ip address";
-	    }
-	    while (apr_isdigit(*t)) {
-		++t;
-	    }
-	    if (*t == '.') {
-		*t++ = 0;
-	    }
-	    else if (*t) {
-		a->type = T_FAIL;
-		return "invalid ip address";
-	    }
-	    if (shift < 0) {
-		return "invalid ip address, only 4 octets allowed";
-	    }
-	    octet = atoi(s);
-	    if (octet < 0 || octet > 255) {
-		a->type = T_FAIL;
-		return "each octet must be between 0 and 255 inclusive";
-	    }
-	    a->x.ip.net |= octet << shift;
-	    a->x.ip.mask |= 0xFFUL << shift;
-	    s = t;
-	    shift -= 8;
-	}
-	a->x.ip.net = ntohl(a->x.ip.net);
-	a->x.ip.mask = ntohl(a->x.ip.mask);
+        *s++ = '\0';
+        rv = apr_ipsubnet_create(&a->x.ip, where, s, cmd->pool);
+        if (rv != APR_SUCCESS) {
+            /* XXX build APR error string from rv and return it */
+            return "some APR error occurred";
+        }
+        a->type = T_IP;
+    }
+    /* XXX not sure what to do here; apr_ipsubnet_create() would need to decide that
+     *     it isn't an IP address at all and tell the caller somehow
+     */
+    else if ((rv = apr_ipsubnet_create(&a->x.ip, where, NULL, cmd->pool)) != APR_EINVAL) {
+        if (rv != APR_SUCCESS) {
+            /* XXX build APR error string from rv and return it */
+            return "some APR error occurred";
+        }
+        a->type = T_IP;
     }
     else {
 	a->type = T_HOST;
@@ -330,14 +256,8 @@
 	    return 1;
 
 	case T_IP:
-            /* XXX handle IPv6 with separate T_IP6 type or add common 
-             *     address masking operations to APR */
-	    if (ap[i].x.ip.net != APR_INADDR_NONE
-		&& (r->connection->remote_addr->sa.sin.sin_addr.s_addr
-		    & ap[i].x.ip.mask) == ap[i].x.ip.net) {
-		return 1;
-	    }
-	    break;
+            return apr_ipsubnet_test(ap[i].x.ip, r->connection->remote_addr);
+            break;
 
 	case T_HOST:
 	    if (!gothost) {
Index: srclib/apr/include/apr_errno.h
===================================================================
RCS file: /home/cvspublic/apr/include/apr_errno.h,v
retrieving revision 1.54
diff -u -r1.54 apr_errno.h
--- srclib/apr/include/apr_errno.h	2001/02/16 04:15:42	1.54
+++ srclib/apr/include/apr_errno.h	2001/03/14 12:51:47
@@ -177,6 +177,8 @@
  * APR_EDSOOPEN     APR was unable to open the dso object.  For more 
  *                  information call apr_dso_error().
  * APR_EGENERAL     General failure (specific information not available)
+ * APR_EBADIP       The specified IP address is invalid
+ * APR_EBADMASK     The specified netmask is invalid
  * </PRE>
  *
  * <PRE>
@@ -232,8 +234,8 @@
 #define APR_ENOTHDKEY      (APR_OS_START_ERROR + 13)
 #define APR_EGENERAL       (APR_OS_START_ERROR + 14)
 #define APR_ENOSHMAVAIL    (APR_OS_START_ERROR + 15)
-/* empty slot: +16 */
-/* empty slot: +17 */
+#define APR_EBADIP         (APR_OS_START_ERROR + 16)
+#define APR_EBADMASK       (APR_OS_START_ERROR + 17)
 /* empty slot: +18 */
 #define APR_EDSOOPEN       (APR_OS_START_ERROR + 19)
 
@@ -254,8 +256,8 @@
 #define APR_STATUS_IS_ENOTHDKEY(s)      ((s) == APR_ENOTHDKEY)
 #define APR_STATUS_IS_EGENERAL(s)       ((s) == APR_EGENERAL)
 #define APR_STATUS_IS_ENOSHMAVAIL(s)    ((s) == APR_ENOSHMAVAIL)
-/* empty slot: +16 */
-/* empty slot: +17 */
+#define APR_STATUS_IS_EBADIP(s)         ((s) == APR_EBADIP)
+#define APR_STATUS_IS_EBADMASK(s)       ((s) == APR_EBADMASK)
 /* empty slot: +18 */
 #define APR_STATUS_IS_EDSOOPEN(s)       ((s) == APR_EDSOOPEN)
 
Index: srclib/apr/include/apr_network_io.h
===================================================================
RCS file: /home/cvspublic/apr/include/apr_network_io.h,v
retrieving revision 1.99
diff -u -r1.99 apr_network_io.h
--- srclib/apr/include/apr_network_io.h	2001/03/08 19:58:35	1.99
+++ srclib/apr/include/apr_network_io.h	2001/03/14 12:51:48
@@ -218,6 +218,19 @@
     int numtrailers;
 };
 
+/** A structure to represent an IP subnet */
+typedef struct apr_ipsubnet_t apr_ipsubnet_t;
+struct apr_ipsubnet_t {
+    int family;
+#if APR_HAVE_IPV6
+    apr_uint32_t sub[4]; /* big enough for IPv4 and IPv6 addresses */
+    apr_uint32_t mask[4];
+#else
+    apr_uint32_t sub[1];
+    apr_uint32_t mask[1];
+#endif
+};
+
 /* function definitions */
 
 /**
@@ -753,6 +766,26 @@
  */
 APR_DECLARE(apr_status_t) apr_getservbyname(apr_sockaddr_t *sockaddr, 
                                             const char *servname);
+
+/**
+ * Build an ip-subnet representation from an IP address and optional netmask or
+ * number-of-bits.
+ * @param ipsub The new ip-subnet representation
+ * @param ipstr The input IP address string
+ * @param mask_or_numbits The input netmask or number-of-bits string, or NULL
+ * @param p The pool to allocate from
+ */
+APR_DECLARE(apr_status_t) apr_ipsubnet_create(apr_ipsubnet_t **ipsub, const char *ipstr, 
+                                              const char *mask_or_numbits, apr_pool_t *p);
+
+/**
+ * Test the IP address in an apr_sockaddr_t against a pre-built ip-subnet
+ * representation.
+ * @param ipsub The ip-subnet representation
+ * @param sa The socket address to test
+ * @return non-zero if the socket address is within the subnet, 0 otherwise
+ */
+APR_DECLARE(int) apr_ipsubnet_test(apr_ipsubnet_t *ipsub, apr_sockaddr_t *sa);
 
 #ifdef __cplusplus
 }
 


-- 
Jeff Trawick | trawickj@bellsouth.net | PGP public key at web site:
       http://www.geocities.com/SiliconValley/Park/9289/
             Born in Roswell... married an alien...