summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortrawick <trawick@13f79535-47bb-0310-9956-ffa450edef68>2001-03-15 18:28:04 +0000
committertrawick <trawick@13f79535-47bb-0310-9956-ffa450edef68>2001-03-15 18:28:04 +0000
commit001dd420d9c72f5c1f530425de0f938a40175684 (patch)
tree46b345d8e4569bce2361adbb2bff42cecccc7d75
parent6e4a5ec7ac9e6405eb3ddb184fc5eacf2b2ba800 (diff)
downloadlibapr-001dd420d9c72f5c1f530425de0f938a40175684.tar.gz
Add apr_ipsubnet_create() and apr_ipsubnet_test() for testing
whether or not an address is within a subnet. git-svn-id: http://svn.apache.org/repos/asf/apr/apr/trunk@61361 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--CHANGES3
-rw-r--r--include/apr_errno.h10
-rw-r--r--include/apr_network_io.h33
-rw-r--r--misc/unix/errorcodes.c4
-rw-r--r--network_io/unix/sa_common.c231
-rw-r--r--test/.cvsignore2
-rw-r--r--test/Makefile.in4
-rw-r--r--test/testipsub.c220
8 files changed, 503 insertions, 4 deletions
diff --git a/CHANGES b/CHANGES
index c4c5ae026..0e256569f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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]
diff --git a/include/apr_errno.h b/include/apr_errno.h
index fa5184dda..148817a58 100644
--- a/include/apr_errno.h
+++ b/include/apr_errno.h
@@ -177,6 +177,8 @@ APR_DECLARE(char *) apr_strerror(apr_status_t statcode, char *buf,
* 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 @@ APR_DECLARE(char *) apr_strerror(apr_status_t statcode, char *buf,
#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 @@ APR_DECLARE(char *) apr_strerror(apr_status_t statcode, char *buf,
#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)
diff --git a/include/apr_network_io.h b/include/apr_network_io.h
index 585d64245..aecd41c44 100644
--- a/include/apr_network_io.h
+++ b/include/apr_network_io.h
@@ -218,6 +218,19 @@ struct apr_hdtr_t {
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 */
/**
@@ -754,6 +767,26 @@ APR_DECLARE(apr_status_t) apr_socket_from_file(apr_socket_t **newsock,
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
}
#endif
diff --git a/misc/unix/errorcodes.c b/misc/unix/errorcodes.c
index 5a7fcf6c6..adc6395c7 100644
--- a/misc/unix/errorcodes.c
+++ b/misc/unix/errorcodes.c
@@ -111,6 +111,10 @@ static char *apr_error_string(apr_status_t statcode)
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 "
diff --git a/network_io/unix/sa_common.c b/network_io/unix/sa_common.c
index f84ce52ac..6d9389944 100644
--- a/network_io/unix/sa_common.c
+++ b/network_io/unix/sa_common.c
@@ -498,3 +498,234 @@ APR_DECLARE(apr_status_t) apr_getservbyname(apr_sockaddr_t *sockaddr,
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 */
+}
+
diff --git a/test/.cvsignore b/test/.cvsignore
index a2faf05d0..ba4cd7407 100644
--- a/test/.cvsignore
+++ b/test/.cvsignore
@@ -28,3 +28,5 @@ testsuite.ncb
testfile.tmp
testflock
testsockopt
+testipsub
+
diff --git a/test/Makefile.in b/test/Makefile.in
index 1be7055ac..e4866e800 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -18,6 +18,7 @@ PROGRAMS = \
testoc@EXEEXT@ \
testuuid@EXEEXT@ \
testsockopt@EXEEXT@ \
+ testipsub@EXEEXT@ \
occhild@EXEEXT@ \
mod_test.so
@@ -97,4 +98,7 @@ testuuid@EXEEXT@: testuuid.lo ../libapr.la
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
diff --git a/test/testipsub.c b/test/testipsub.c
new file mode 100644
index 000000000..a8477f383
--- /dev/null
+++ b/test/testipsub.c
@@ -0,0 +1,220 @@
+/* ====================================================================
+ * 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;
+}