You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@apr.apache.org by tr...@apache.org on 2001/03/15 19:28:10 UTC

cvs commit: apr/network_io/unix sa_common.c

trawick     01/03/15 10:28:09

  Modified:    .        CHANGES
               test     .cvsignore Makefile.in
               include  apr_errno.h apr_network_io.h
               misc/unix errorcodes.c
               network_io/unix sa_common.c
  Added:       test     testipsub.c
  Log:
  Add apr_ipsubnet_create() and apr_ipsubnet_test() for testing
  whether or not an address is within a subnet.
  
  Revision  Changes    Path
  1.74      +3 -0      apr/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /home/cvs/apr/CHANGES,v
  retrieving revision 1.73
  retrieving revision 1.74
  diff -u -r1.73 -r1.74
  --- CHANGES	2001/03/08 20:06:57	1.73
  +++ CHANGES	2001/03/15 18:27:41	1.74
  @@ -1,5 +1,8 @@
   Changes with APR b1  
   
  +  *) Add apr_ipsubnet_create() and apr_ipsubnet_test() for testing
  +     whether or not an address is within a subnet.  [Jeff Trawick]
  +
     *) Add apr_sendto and apr_recvfrom for Unix.  Start of adding UDP
        support.  [David Reid]
   
  
  
  
  1.14      +2 -0      apr/test/.cvsignore
  
  Index: .cvsignore
  ===================================================================
  RCS file: /home/cvs/apr/test/.cvsignore,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- .cvsignore	2001/01/25 12:20:29	1.13
  +++ .cvsignore	2001/03/15 18:27:46	1.14
  @@ -28,3 +28,5 @@
   testfile.tmp
   testflock
   testsockopt
  +testipsub
  +
  
  
  
  1.42      +4 -0      apr/test/Makefile.in
  
  Index: Makefile.in
  ===================================================================
  RCS file: /home/cvs/apr/test/Makefile.in,v
  retrieving revision 1.41
  retrieving revision 1.42
  diff -u -r1.41 -r1.42
  --- Makefile.in	2001/02/19 18:42:18	1.41
  +++ Makefile.in	2001/03/15 18:27:47	1.42
  @@ -18,6 +18,7 @@
   	testoc@EXEEXT@ \
   	testuuid@EXEEXT@ \
           testsockopt@EXEEXT@ \
  +        testipsub@EXEEXT@ \
   	occhild@EXEEXT@ \
   	mod_test.so
   
  @@ -96,5 +97,8 @@
   
   testsockopt@EXEEXT@: testsockopt.lo ../libapr.la
   	$(LINK) testsockopt.lo $(ALL_LIBS)
  +
  +testipsub@EXEEXT@: testipsub.lo ../libapr.la
  +	$(LINK) testipsub.lo $(ALL_LIBS)
   
   # DO NOT REMOVE
  
  
  
  1.1                  apr/test/testipsub.c
  
  Index: testipsub.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  #include <assert.h>
  #include <stdlib.h>
  
  #include "apr_general.h"
  #include "apr_network_io.h"
  #include "apr_errno.h"
  
  static void closeapr(void)
  {
      apr_terminate();
  }
  
  static void test_bad_input(apr_pool_t *p)
  {
      struct {
          const char *ipstr;
          const char *mask;
          apr_status_t expected_rv;
      } testcases[] =
      {
          /* so we have a few good inputs in here; sue me */
          {"my.host.name",       NULL,               APR_EINVAL}
          ,{"127.0.0.256",       NULL,               APR_EBADIP}
          ,{"127.0.0.1",         NULL,               APR_SUCCESS}
          ,{"127.0.0.1",         "32",               APR_SUCCESS}
          ,{"127.0.0.1",         "1",                APR_SUCCESS}
          ,{"127.0.0.1",         "15",               APR_SUCCESS}
          ,{"127.0.0.1",         "-1",               APR_EBADMASK}
          ,{"127.0.0.1",         "0",                APR_EBADMASK}
          ,{"127.0.0.1",         "33",               APR_EBADMASK}
          ,{"127.0.0.1",         "255.0.0.0",        APR_SUCCESS}
          ,{"127.0.0.1",         "255.0",            APR_EBADMASK}
          ,{"127.0.0.1",         "255.255.256.0",    APR_EBADMASK}
          ,{"127.0.0.1",         "abc",              APR_EBADMASK}
          ,{"127",               NULL,               APR_SUCCESS}
          ,{"127.0.0.1.2",       NULL,               APR_EBADIP}
          ,{"127.0.0.1.2",       "8",                APR_EBADIP}
          ,{"127",               "255.0.0.0",        APR_EBADIP} /* either EBADIP or EBADMASK seems fine */
  #if APR_HAVE_IPV6
          ,{"::1",               NULL,               APR_SUCCESS}
          ,{"::1",               "20",               APR_SUCCESS}
          ,{"fe80::",            "16",               APR_SUCCESS}
          ,{"fe80::",            "255.0.0.0",        APR_EBADMASK}
          ,{"fe80::1",           "0",                APR_EBADMASK}
          ,{"fe80::1",           "-1",               APR_EBADMASK}
          ,{"fe80::1",           "1",                APR_SUCCESS}
          ,{"fe80::1",           "33",               APR_SUCCESS}
          ,{"fe80::1",           "128",              APR_SUCCESS}
          ,{"fe80::1",           "129",              APR_EBADMASK}
  #else
          /* do some IPv6 stuff and verify that it fails with APR_EBADIP */
  #endif
      };
      int i;
      apr_ipsubnet_t *ipsub;
      apr_status_t rv;
  
      for (i = 0; i < (sizeof testcases / sizeof testcases[0]); i++) {
          rv = apr_ipsubnet_create(&ipsub, testcases[i].ipstr, testcases[i].mask, p);
          assert(rv == testcases[i].expected_rv);
      }
  }
  
  static void test_singleton_subnets(apr_pool_t *p)
  {
      const char *v4addrs[] = {
          "127.0.0.1", "129.42.18.99", "63.161.155.20", "207.46.230.229", "64.208.42.36",
          "198.144.203.195", "192.18.97.241", "198.137.240.91", "62.156.179.119", 
          "204.177.92.181"
      };
      apr_ipsubnet_t *ipsub;
      apr_sockaddr_t *sa;
      apr_status_t rv;
      int i, j, rc;
  
      for (i = 0; i < sizeof v4addrs / sizeof v4addrs[0]; i++) {
          rv = apr_ipsubnet_create(&ipsub, v4addrs[i], NULL, p);
          assert(rv == APR_SUCCESS);
          for (j = 0; j < sizeof v4addrs / sizeof v4addrs[0]; j++) {
              rv = apr_sockaddr_info_get(&sa, v4addrs[j], APR_INET, 0, 0, p);
              assert(rv == APR_SUCCESS);
              rc = apr_ipsubnet_test(ipsub, sa);
              if (!strcmp(v4addrs[i], v4addrs[j])) {
                  assert(rc != 0);
              }
              else {
                  assert(rc == 0);
              }
          }
      }
  
      /* same for v6? */
  }
  
  static void test_interesting_subnets(apr_pool_t *p)
  {
      struct {
          const char *ipstr, *mask;
          int family;
          char *in_subnet, *not_in_subnet;
      } testcases[] =
      {
          {"9.67",              NULL,            APR_INET,  "9.67.113.15",         "10.1.2.3"}
          ,{"9.67.0.0",         "16",            APR_INET,  "9.67.113.15",         "10.1.2.3"}
          ,{"9.67.0.0",         "255.255.0.0",   APR_INET,  "9.67.113.15",         "10.1.2.3"}
          ,{"9.67.113.99",      "16",            APR_INET,  "9.67.113.15",         "10.1.2.3"}
          ,{"9.67.113.99",      "255.255.255.0", APR_INET,  "9.67.113.15",         "10.1.2.3"}
  #if APR_HAVE_IPV6
          ,{"fe80::",           "8",             APR_INET6, "fe80::1",             "ff01::1"}
          ,{"ff01::",           "8",             APR_INET6, "ff01::1",             "fe80::1"}
          ,{"3FFE:8160::",      "28",            APR_INET6, "3ffE:816e:abcd:1234::1", "3ffe:8170::1"}
  #endif
      };
      apr_ipsubnet_t *ipsub;
      apr_sockaddr_t *sa;
      apr_status_t rv;
      int i, rc;
  
      for (i = 0; i < sizeof testcases / sizeof testcases[0]; i++) {
          rv = apr_ipsubnet_create(&ipsub, testcases[i].ipstr, testcases[i].mask, p);
          assert(rv == APR_SUCCESS);
          rv = apr_sockaddr_info_get(&sa, testcases[i].in_subnet, testcases[i].family, 0, 0, p);
          assert(rv == APR_SUCCESS);
          rc = apr_ipsubnet_test(ipsub, sa);
          assert(rc != 0);
          rv = apr_sockaddr_info_get(&sa, testcases[i].not_in_subnet, testcases[i].family, 0, 0, p);
          assert(rv == APR_SUCCESS);
          rc = apr_ipsubnet_test(ipsub, sa);
          assert(rc == 0);
      }
  }
  
  int main(void)
  {
      apr_status_t rv;
      apr_pool_t *p;
      char buf[128];
  
      rv = apr_initialize();
      if (rv != APR_SUCCESS) {
          fprintf(stderr, "apr_initialize()->%d/%s\n",
                  rv,
                  apr_strerror(rv, buf, sizeof buf));
          exit(1);
      }
  
      atexit(closeapr);
  
      rv = apr_pool_create(&p, NULL);
      if (rv != APR_SUCCESS) {
          fprintf(stderr, "apr_pool_create()->%d/%s\n",
                  rv,
                  apr_strerror(rv, buf, sizeof buf));
          exit(1);
      }
  
      test_bad_input(p);
      test_singleton_subnets(p);
      test_interesting_subnets(p);
  
      printf("error strings:\n");
      printf("\tAPR_EBADIP\t`%s'\n", apr_strerror(APR_EBADIP, buf, sizeof buf));
      printf("\tAPR_EBADMASK\t`%s'\n", apr_strerror(APR_EBADMASK, buf, sizeof buf));
  
      return 0;
  }
  
  
  
  1.55      +6 -4      apr/include/apr_errno.h
  
  Index: apr_errno.h
  ===================================================================
  RCS file: /home/cvs/apr/include/apr_errno.h,v
  retrieving revision 1.54
  retrieving revision 1.55
  diff -u -r1.54 -r1.55
  --- apr_errno.h	2001/02/16 04:15:42	1.54
  +++ apr_errno.h	2001/03/15 18:27:53	1.55
  @@ -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)
   
  
  
  
  1.100     +33 -0     apr/include/apr_network_io.h
  
  Index: apr_network_io.h
  ===================================================================
  RCS file: /home/cvs/apr/include/apr_network_io.h,v
  retrieving revision 1.99
  retrieving revision 1.100
  diff -u -r1.99 -r1.100
  --- apr_network_io.h	2001/03/08 19:58:35	1.99
  +++ apr_network_io.h	2001/03/15 18:27:54	1.100
  @@ -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
   }
  
  
  
  1.34      +4 -0      apr/misc/unix/errorcodes.c
  
  Index: errorcodes.c
  ===================================================================
  RCS file: /home/cvs/apr/misc/unix/errorcodes.c,v
  retrieving revision 1.33
  retrieving revision 1.34
  diff -u -r1.33 -r1.34
  --- errorcodes.c	2001/02/25 20:39:34	1.33
  +++ errorcodes.c	2001/03/15 18:28:00	1.34
  @@ -111,6 +111,10 @@
           return "DSO load failed";
   #endif /* HAVE_LIBDL */
   #endif /* APR_HAS_DSO */
  +    case APR_EBADIP:
  +        return "The specified IP address is invalid.";
  +    case APR_EBADMASK:
  +        return "The specified network mask is invalid.";
       case APR_INCHILD:
           return
   	    "Your code just forked, and you are currently executing in the "
  
  
  
  1.30      +231 -0    apr/network_io/unix/sa_common.c
  
  Index: sa_common.c
  ===================================================================
  RCS file: /home/cvs/apr/network_io/unix/sa_common.c,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -u -r1.29 -r1.30
  --- sa_common.c	2001/02/25 20:39:35	1.29
  +++ sa_common.c	2001/03/15 18:28:04	1.30
  @@ -498,3 +498,234 @@
       return errno;
   }
   
  +static apr_status_t parse_network(apr_ipsubnet_t *ipsub, const char *network)
  +{
  +    /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */
  +    int shift;
  +    char *s, *t;
  +    int octet;
  +    char buf[sizeof "255.255.255.255"];
  +
  +    if (strlen(network) < sizeof buf) {
  +        strcpy(buf, network);
  +    }
  +    else {
  +        return APR_EBADIP;
  +    }
  +
  +    /* parse components */
  +    s = buf;
  +    ipsub->sub[0] = 0;
  +    ipsub->mask[0] = 0;
  +    shift = 24;
  +    while (*s) {
  +        t = s;
  +        if (!apr_isdigit(*t)) {
  +            return APR_EBADIP;
  +        }
  +        while (apr_isdigit(*t)) {
  +            ++t;
  +        }
  +        if (*t == '.') {
  +            *t++ = 0;
  +        }
  +        else if (*t) {
  +            return APR_EBADIP;
  +        }
  +        if (shift < 0) {
  +            return APR_EBADIP;
  +        }
  +        octet = atoi(s);
  +        if (octet < 0 || octet > 255) {
  +            return APR_EBADIP;
  +        }
  +        ipsub->sub[0] |= octet << shift;
  +        ipsub->mask[0] |= 0xFFUL << shift;
  +        s = t;
  +        shift -= 8;
  +    }
  +    ipsub->sub[0] = ntohl(ipsub->sub[0]);
  +    ipsub->mask[0] = ntohl(ipsub->mask[0]);
  +    ipsub->family = AF_INET;
  +    return APR_SUCCESS;
  +}
  +
  +/* return values:
  + * APR_EINVAL     not an IP address; caller should see if it is something else
  + * APR_BADIP      IP address portion is is not valid
  + * APR_BADMASK    mask portion is not valid
  + */
  +
  +static apr_status_t parse_ip(apr_ipsubnet_t *ipsub, const char *ipstr, int network_allowed)
  +{
  +    /* supported flavors of IP:
  +     *
  +     * . IPv6 numeric address string (e.g., "fe80::1")
  +     *
  +     * . IPv4 numeric address string (e.g., "127.0.0.1")
  +     *
  +     * . IPv4 network string (e.g., "9.67")
  +     *
  +     *   IMPORTANT: This network form is only allowed if network_allowed is on.
  +     */
  +    int rc;
  +
  +#if APR_HAVE_IPV6
  +    rc = apr_inet_pton(AF_INET6, ipstr, ipsub->sub);
  +    if (rc == 1) {
  +        ipsub->family = AF_INET6;
  +    }
  +    else
  +#endif
  +    {
  +        rc = apr_inet_pton(AF_INET, ipstr, ipsub->sub);
  +        if (rc == 1) {
  +            if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ipsub->sub)) {
  +                /* apr_ipsubnet_test() assumes that we don't create IPv4-mapped IPv6
  +                 * addresses; this of course forces the user to specify IPv4 addresses
  +                 * in a.b.c.d style instead of ::ffff:a.b.c.d style.
  +                 */
  +                return APR_EBADIP;
  +            }
  +            ipsub->family = AF_INET;
  +        }
  +    }
  +    if (rc != 1) {
  +        if (network_allowed) {
  +            return parse_network(ipsub, ipstr);
  +        }
  +        else {
  +            return APR_EBADIP;
  +        }
  +    }
  +    return APR_SUCCESS;
  +}
  +
  +static int looks_like_ip(const char *ipstr)
  +{
  +    if (strchr(ipstr, ':')) {
  +        /* definitely not a hostname; assume it is intended to be an IPv6 address */
  +        return 1;
  +    }
  +
  +    /* simple IPv4 address string check */
  +    while ((*ipstr == '.') || apr_isdigit(*ipstr))
  +        ipstr++;
  +    return (*ipstr == '\0');
  +}
  +
  +static void fix_subnet(apr_ipsubnet_t *ipsub)
  +{
  +    /* in case caller specified more bits in network address than are
  +     * valid according to the mask, turn off the extra bits
  +     */
  +    int i;
  +
  +    for (i = 0; i < sizeof ipsub->mask / sizeof(apr_int32_t); i++) {
  +        ipsub->sub[i] &= ipsub->mask[i];
  +    }
  +}
  +
  +/* be sure not to store any IPv4 address as a v4-mapped IPv6 address */
  +APR_DECLARE(apr_status_t) apr_ipsubnet_create(apr_ipsubnet_t **ipsub, const char *ipstr, 
  +                                              const char *mask_or_numbits, apr_pool_t *p)
  +{
  +    apr_status_t rv;
  +    char *endptr;
  +    long bits, maxbits;
  +
  +    /* filter out stuff which doesn't look remotely like an IP address; this helps 
  +     * callers like mod_access which have a syntax allowing hostname or IP address;
  +     * APR_EINVAL tells the caller that it was probably not intended to be an IP
  +     * address
  +     */
  +    if (!looks_like_ip(ipstr)) {
  +        return APR_EINVAL;
  +    }
  +
  +    *ipsub = apr_pcalloc(p, sizeof(apr_ipsubnet_t));
  +
  +    /* assume ipstr is an individual IP address, not a subnet */
  +    memset((*ipsub)->mask, 0xFF, sizeof (*ipsub)->mask);
  +
  +    rv = parse_ip(*ipsub, ipstr, mask_or_numbits == NULL);
  +    if (rv != APR_SUCCESS) {
  +        return rv;
  +    }
  +
  +    if (mask_or_numbits) {
  +        if ((*ipsub)->family == AF_INET) {
  +            maxbits = 32;
  +        }
  +#if APR_HAVE_IPV6
  +        else {
  +            maxbits = 128;
  +        }
  +#endif
  +        bits = strtol(mask_or_numbits, &endptr, 10);
  +        if (*endptr == '\0' && bits > 0 && bits <= maxbits) {
  +            /* valid num-bits string; fill in mask appropriately */
  +            int cur_entry = 0;
  +            apr_int32_t cur_bit_value;
  +
  +            memset((*ipsub)->mask, 0, sizeof (*ipsub)->mask);
  +            while (bits > 32) {
  +                (*ipsub)->mask[cur_entry] = 0xFFFFFFFF; /* all 32 bits */
  +                bits -= 32;
  +                ++cur_entry;
  +            }
  +            cur_bit_value = 0x80000000;
  +            while (bits) {
  +                (*ipsub)->mask[cur_entry] |= cur_bit_value;
  +                --bits;
  +                cur_bit_value /= 2;
  +            }
  +            (*ipsub)->mask[cur_entry] = htonl((*ipsub)->mask[cur_entry]);
  +        }
  +        else if (apr_inet_pton(AF_INET, mask_or_numbits, (*ipsub)->mask) == 1 &&
  +            (*ipsub)->family == AF_INET) {
  +            /* valid IPv4 netmask */
  +        }
  +        else {
  +            return APR_EBADMASK;
  +        }
  +    }
  +
  +    fix_subnet(*ipsub);
  +
  +    return APR_SUCCESS;
  +}
  +
  +APR_DECLARE(int) apr_ipsubnet_test(apr_ipsubnet_t *ipsub, apr_sockaddr_t *sa)
  +{
  +#if APR_HAVE_IPV6
  +    if (sa->sa.sin.sin_family == AF_INET) {
  +        if (ipsub->family == AF_INET &&
  +            ((sa->sa.sin.sin_addr.s_addr & ipsub->mask[0]) == ipsub->sub[0])) {
  +            return 1;
  +        }
  +    }
  +    else if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)sa->ipaddr_ptr)) {
  +        if (ipsub->family == AF_INET &&
  +            (((apr_uint32_t *)sa->ipaddr_ptr)[3] & ipsub->mask[0]) == ipsub->sub[0]) {
  +            return 1;
  +        }
  +    }
  +    else {
  +        apr_uint32_t *addr = (apr_uint32_t *)sa->ipaddr_ptr;
  +
  +        if ((addr[0] & ipsub->mask[0]) == ipsub->sub[0] &&
  +            (addr[1] & ipsub->mask[1]) == ipsub->sub[1] &&
  +            (addr[2] & ipsub->mask[2]) == ipsub->sub[2] &&
  +            (addr[3] & ipsub->mask[3]) == ipsub->sub[3]) {
  +            return 1;
  +        }
  +    }
  +#else
  +    if ((sa->sa.sin.sin_addr.s_addr & ipsub->mask[0]) == ipsub->sub[0]) {
  +        return 1;
  +    }
  +#endif /* APR_HAVE_IPV6 */
  +    return 0; /* no match */
  +}
  +