From 0b0c08bc68c523b72bbd94f23f7e080f287d5623 Mon Sep 17 00:00:00 2001 From: Michael Poole Date: Tue, 28 Dec 2004 21:12:06 +0000 Subject: [PATCH] Convert irc_in_addr_* to macros. Add test program for that code. Fix several bugs uncovered by the test program. git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@1286 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- ChangeLog | 21 +++++++++ configure | 3 +- configure.in | 2 +- include/res.h | 29 ++++++++++-- ircd/ircd_res.c | 52 --------------------- ircd/ircd_string.c | 47 ++++++++----------- ircd/test/Makefile | 20 --------- ircd/test/Makefile.in | 25 +++++++++++ ircd/test/ircd_in_addr_t.c | 92 ++++++++++++++++++++++++++++++++++++++ ircd/test/test_stub.c | 39 ++++++++++++++++ 10 files changed, 223 insertions(+), 107 deletions(-) delete mode 100644 ircd/test/Makefile create mode 100644 ircd/test/Makefile.in create mode 100644 ircd/test/ircd_in_addr_t.c create mode 100644 ircd/test/test_stub.c diff --git a/ChangeLog b/ChangeLog index c02fdcc..c5053fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2004-12-28 Michael Poole + + * include/res.h: Implement irc_in_addr_* as macros. + + * ircd/ircd_res.c: Remove the function bodies. + + * ircd/ircd_string.c (irc_in_addr_is_ipv4): Remove body. + (ircd_ntoa_r): Do not append extra ':' when unparsing 0::. + (ircd_aton): Accept IPv6 addresses with all eight segments + specified (e.g. 0:0:0:0:0:0:0:0). Correctly parse addresses + with IPv4 bits at the end (e.g. ::FFFF:127.0.0.1). + + * ircd/test/ircd_in_addr_t.c, ircd/test/test_stub.c: New files. + + * ircd/test/Makefile: Convert to Makefile.in for proper VPATH + support. Add test_stub.c and ircd_in_addr_t.c references. + + * configure.in: Generate ircd/test/Makefile as an output file. + + * configure: Update. + 2004-12-18 Michael Poole * include/client.h: Move unreg, privs, capab and active fields diff --git a/configure b/configure index 014757a..2e114da 100755 --- a/configure +++ b/configure @@ -10188,7 +10188,7 @@ cat >>confdefs.h <<_ACEOF _ACEOF - ac_config_files="$ac_config_files Makefile ircd/Makefile doc/Makefile" + ac_config_files="$ac_config_files Makefile ircd/Makefile ircd/test/Makefile doc/Makefile" ac_config_commands="$ac_config_commands default" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -10720,6 +10720,7 @@ do # Handling of arguments. "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; "ircd/Makefile" ) CONFIG_FILES="$CONFIG_FILES ircd/Makefile" ;; + "ircd/test/Makefile" ) CONFIG_FILES="$CONFIG_FILES ircd/test/Makefile" ;; "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "default" ) CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;; "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; diff --git a/configure.in b/configure.in index 4d3450e..88ec8c7 100644 --- a/configure.in +++ b/configure.in @@ -745,7 +745,7 @@ AC_DEFINE_UNQUOTED(MAXCONNECTIONS, $unet_cv_with_maxcon, [Maximum number of network connections]) dnl Finally really generate all output files: -AC_OUTPUT(Makefile ircd/Makefile doc/Makefile, [echo timestamp > stamp-h]) +AC_OUTPUT(Makefile ircd/Makefile ircd/test/Makefile doc/Makefile, [echo timestamp > stamp-h]) dnl Report configuration AC_OUTPUT_COMMANDS([echo " diff --git a/include/res.h b/include/res.h index 9f55335..1029af0 100644 --- a/include/res.h +++ b/include/res.h @@ -126,9 +126,30 @@ extern void report_dns_servers(struct Client *source_p, const struct StatDesc *s extern void gethost_byname(const char *name, const struct DNSQuery *query); extern void gethost_byaddr(const struct irc_in_addr *addr, const struct DNSQuery *query); -extern int irc_in_addr_valid(const struct irc_in_addr *addr); -extern int irc_in_addr_cmp(const struct irc_in_addr *a, const struct irc_in_addr *b); -extern int irc_in_addr_is_ipv4(const struct irc_in_addr *addr); -extern int irc_in_addr_is_loopback(const struct irc_in_addr *addr); +/** Evaluate to non-zero if \a ADDR is a valid address (not all 0s and not all 1s). */ +#define irc_in_addr_valid(ADDR) (((ADDR)->in6_16[0] && ~(ADDR)->in6_16[0]) \ + || (ADDR)->in6_16[1] != (ADDR)->in6_16[0] \ + || (ADDR)->in6_16[2] != (ADDR)->in6_16[0] \ + || (ADDR)->in6_16[3] != (ADDR)->in6_16[0] \ + || (ADDR)->in6_16[4] != (ADDR)->in6_16[0] \ + || (ADDR)->in6_16[5] != (ADDR)->in6_16[0] \ + || (ADDR)->in6_16[6] != (ADDR)->in6_16[0] \ + || (ADDR)->in6_16[7] != (ADDR)->in6_16[0]) +/** Evaluate to non-zero if \a ADDR (of type struct irc_in_addr) is an IPv4 address. */ +#define irc_in_addr_is_ipv4(ADDR) (!(ADDR)->in6_16[0] && !(ADDR)->in6_16[1] && !(ADDR)->in6_16[2] \ + && !(ADDR)->in6_16[3] && !(ADDR)->in6_16[4] && (ADDR)->in6_16[6] \ + && (!(ADDR)->in6_16[5] || (ADDR)->in6_16[5] == 65535)) +/** Evaluate to non-zero if \a A is a different IP than \a B. */ +#define irc_in_addr_cmp(A,B) (irc_in_addr_is_ipv4(A) ? ((A)->in6_16[6] != (B)->in6_16[6] \ + || (A)->in6_16[7] != (B)->in6_16[7] || !irc_in_addr_is_ipv4(B)) \ + : memcmp((A), (B), sizeof(struct irc_in_addr))) +/** Evaluate to non-zero if \a ADDR is a loopback address. */ +#define irc_in_addr_is_loopback(ADDR) (!(ADDR)->in6_16[0] && !(ADDR)->in6_16[1] && !(ADDR)->in6_16[2] \ + && !(ADDR)->in6_16[3] && !(ADDR)->in6_16[4] \ + && ((!(ADDR)->in6_16[5] \ + && ((!(ADDR)->in6_16[6] && (ADDR)->in6_16[7] == htons(1)) \ + || (ntohs((ADDR)->in6_16[6]) & 0xff00) == 0x7f00)) \ + || (((ADDR)->in6_16[5] == 65535) \ + && (ntohs((ADDR)->in6_16[6]) & 0xff00) == 0x7f00))) #endif diff --git a/ircd/ircd_res.c b/ircd/ircd_res.c index 0ce4fd6..962e211 100644 --- a/ircd/ircd_res.c +++ b/ircd/ircd_res.c @@ -919,55 +919,3 @@ cres_mem(struct Client* sptr) ":Resolver: requests %d(%d)", request_count, request_mem); return request_mem; } - -/** Check whether an address looks valid. - * This means not all 0s and not all 1s. - * @param[in] addr Address to check for validity. - * @return Non-zero if the address looks valid. - */ -int irc_in_addr_valid(const struct irc_in_addr *addr) -{ - unsigned int ii; - unsigned short val; - - val = addr->in6_16[0]; - if (val != 0 && val != 0xffff) - return 1; - for (ii = 1; ii < 8; ii++) - if (addr->in6_16[ii] != val) - return 1; - return 0; -} - -/** Compare two IP addresses. - * @param[in] a First address to compare. - * @param[in] b Second address to compare. - * @return Non-zero if the two addresses differ, zero if they are identical. - */ -int irc_in_addr_cmp(const struct irc_in_addr *a, const struct irc_in_addr *b) -{ - if (irc_in_addr_is_ipv4(a)) - return a->in6_16[6] != b->in6_16[6] - || a->in6_16[7] != b->in6_16[7] - || !irc_in_addr_is_ipv4(b); - else - return memcmp(a, b, sizeof(*a)); -} - -/** Indicate whether an IP address is a loopback address. - * @param[in] addr Address to check. - * @return Non-zero if the address is loopback; zero if not. - */ -int irc_in_addr_is_loopback(const struct irc_in_addr *addr) -{ - if (addr->in6_16[0] != 0 - || addr->in6_16[1] != 0 - || addr->in6_16[2] != 0 - || addr->in6_16[3] != 0 - || addr->in6_16[4] != 0) - return 0; - if ((addr->in6_16[5] == 0xffff) || (addr->in6_16[5] == 0 && addr->in6_16[6] != 0)) - return (ntohs(addr->in6_16[6]) & 0xff00) == 0x7f00; - else - return addr->in6_16[5] == 0 && addr->in6_16[6] == 0 && htons(addr->in6_16[7]) == 1; -} diff --git a/ircd/ircd_string.c b/ircd/ircd_string.c index 6b18f7c..fab7c71 100644 --- a/ircd/ircd_string.c +++ b/ircd/ircd_string.c @@ -450,22 +450,6 @@ const char* ircd_ntoa(const struct irc_in_addr* in) return ircd_ntoa_r(buf, in); } -/* This doesn't really belong here, but otherwise umkpasswd breaks. */ -/** Check whether an IP address looks like an IPv4 address. - * @param[in] addr Address to check. - * @return Non-zero if the address is a valid IPv4 address, zero if not. - */ -int irc_in_addr_is_ipv4(const struct irc_in_addr *addr) -{ - return addr->in6_16[0] == 0 - && addr->in6_16[1] == 0 - && addr->in6_16[2] == 0 - && addr->in6_16[3] == 0 - && addr->in6_16[4] == 0 - && (addr->in6_16[5] == 0 || addr->in6_16[5] == 0xffff) - && addr->in6_16[6] != 0; -} - /** Convert an IP address to printable ASCII form. * @param[out] buf Output buffer to write to. * @param[in] in Address to format. @@ -536,8 +520,6 @@ const char* ircd_ntoa_r(char* buf, const struct irc_in_addr* in) if (ii < 7) APPEND(':'); } - if (max_zeros + max_start == 8) - APPEND(':'); #undef APPEND /* Nul terminate and return number of characters used. */ @@ -650,24 +632,31 @@ ircd_aton(struct irc_in_addr *ip, const char *input) case '.': { uint32_t ip4; unsigned int len; - len = ircd_aton_ip4(input + pos, &ip4); + len = ircd_aton_ip4(part_start, &ip4); if (!len || (ii > 6)) return 0; ip->in6_16[ii++] = htons(ntohl(ip4) >> 16); ip->in6_16[ii++] = htons(ntohl(ip4) & 65535); - pos += len; - break; + if (colon < 8) { + unsigned int jj; + /* Shift stuff after "::" up and fill middle with zeros. */ + for (jj = 0; jj < ii - colon; jj++) + ip->in6_16[7 - jj] = ip->in6_16[ii - jj - 1]; + for (jj = 0; jj < 8 - ii; jj++) + ip->in6_16[colon + jj] = 0; + } + return part_start - input + len; } default: { - unsigned int jj; - if (colon >= 8) - return 0; - /* Shift stuff after "::" up and fill middle with zeros. */ ip->in6_16[ii++] = htons(part); - for (jj = 0; jj < ii - colon; jj++) - ip->in6_16[7 - jj] = ip->in6_16[ii - jj - 1]; - for (jj = 0; jj < 8 - ii; jj++) - ip->in6_16[colon + jj] = 0; + if (colon < 8) { + unsigned int jj; + /* Shift stuff after "::" up and fill middle with zeros. */ + for (jj = 0; jj < ii - colon; jj++) + ip->in6_16[7 - jj] = ip->in6_16[ii - jj - 1]; + for (jj = 0; jj < 8 - ii; jj++) + ip->in6_16[colon + jj] = 0; + } return pos; } } diff --git a/ircd/test/Makefile b/ircd/test/Makefile deleted file mode 100644 index 460e3a6..0000000 --- a/ircd/test/Makefile +++ /dev/null @@ -1,20 +0,0 @@ - -CPPFLAGS = -I../../include -I../../config -CFLAGS = -g -Wall - -TESTPROGS = \ - ircd_chattr_t \ - ircd_string_t - -all: ${TESTPROGS} - -ircd_chattr_t: ircd_chattr_t.o ../ircd_string.o - ${CC} -o $@ $^ - -ircd_string_t: ircd_string_t.o ../ircd_string.o - ${CC} -o $@ $^ - -.PHONY: clean - -clean: - rm -f core *.o ${TESTPROGS} diff --git a/ircd/test/Makefile.in b/ircd/test/Makefile.in new file mode 100644 index 0000000..8e6a05d --- /dev/null +++ b/ircd/test/Makefile.in @@ -0,0 +1,25 @@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +CPPFLAGS = -I$(top_srcdir)/include -I../.. +CFLAGS = -g -Wall + +TESTPROGS = \ + ircd_chattr_t \ + ircd_string_t \ + ircd_in_addr_t + +all: ${TESTPROGS} + +ircd_chattr_t: ircd_chattr_t.o test_stub.o ../ircd_string.o + ${CC} -o $@ $^ + +ircd_string_t: ircd_string_t.o test_stub.o ../ircd_string.o + ${CC} -o $@ $^ + +ircd_in_addr_t: ircd_in_addr_t.o test_stub.o ../ircd_alloc.o ../ircd_string.o ../match.o ../numnicks.o + $(CC) -o $@ $^ + +.PHONY: clean + +clean: + rm -f core *.o ${TESTPROGS} diff --git a/ircd/test/ircd_in_addr_t.c b/ircd/test/ircd_in_addr_t.c new file mode 100644 index 0000000..90dd319 --- /dev/null +++ b/ircd/test/ircd_in_addr_t.c @@ -0,0 +1,92 @@ +/* ircd_in_addr_t.c - Test file for IP address manipulation */ + +#include "ircd_log.h" +#include "ircd_string.h" +#include "numnicks.h" +#include "res.h" +#include +#include +#include + +/** Structure to describe a test for IP address parsing and unparsing. */ +struct address_test { + const char *text; /**< Textual address to parse. */ + const char *canonical; /**< Canonical form of address. */ + struct irc_in_addr expected; /**< Parsed address. */ + const char *base64_v4; /**< v4-only compatible base64 encoding. */ + const char *base64_v6; /**< v6-compatible base64 encoding. */ + unsigned int is_valid : 1; /**< is address valid? */ + unsigned int is_ipv4 : 1; /**< is address ipv4? */ + unsigned int is_loopback : 1; /**< is address loopback? */ +}; + +/** Array of addresses to test with. */ +static struct address_test test_addrs[] = { + { "::", "0::", + {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, + "AAAAAA", "_", 0, 0, 0 }, + { "::1", "0::1", + {{ 0, 0, 0, 0, 0, 0, 0, 1 }}, + "AAAAAA", "_AAB", 1, 0, 1 }, + { "127.0.0.1", "127.0.0.1", + {{ 0, 0, 0, 0, 0, 0, 0x7f00, 1 }}, + "B]AAAB", "B]AAAB", 1, 1, 1 }, + { "::ffff:127.0.0.3", "127.0.0.3", + {{ 0, 0, 0, 0, 0, 0xffff, 0x7f00, 3 }}, + "B]AAAD", "B]AAAD", 1, 1, 1 }, + { "2002:7f00:3::1", "2002:7f00:3::1", + {{ 0x2002, 0x7f00, 3, 0, 0, 0, 0, 1 }}, + "B]AAAD", "CACH8AAAD_AAB", 1, 0, 0 }, + { "8352:0344:0:0:0:0:2001:1204", "8352:344::2001:1204", + {{ 0x8352, 0x344, 0, 0, 0, 0, 0x2001, 0x1204 }}, + "AAAAAA", "INSANE_CABBIE", 1, 0, 0 }, + { 0 }, +}; + +/** Perform tests for a single IP address. + * @param[in] addr Address test structure. + */ +static void +test_address(struct address_test *addr) +{ + struct irc_in_addr parsed; + unsigned int ii, len, val; + char unparsed[64], base64_v4[64], base64_v6[64]; + + /* Convert expected address to network order. */ + for (ii = 0; ii < 8; ++ii) + addr->expected.in6_16[ii] = htons(addr->expected.in6_16[ii]); + /* Make sure the text form is parsed as expected. */ + len = ircd_aton(&parsed, addr->text); + assert(len == strlen(addr->text)); + assert(!irc_in_addr_cmp(&parsed, &addr->expected)); + /* Make sure it converts back to ASCII. */ + ircd_ntoa_r(unparsed, &parsed); + assert(!strcmp(unparsed, addr->canonical)); + /* Check IP-to-base64 conversion. */ + iptobase64(base64_v4, &parsed, sizeof(base64_v4), 0); + iptobase64(base64_v6, &parsed, sizeof(base64_v6), 1); + if (addr->base64_v4) + assert(!strcmp(base64_v4, addr->base64_v4)); + if (addr->base64_v6) + assert(!strcmp(base64_v6, addr->base64_v6)); + /* Check testable attributes. */ + val = irc_in_addr_valid(&parsed); + assert(!!val == addr->is_valid); + val = irc_in_addr_is_ipv4(&parsed); + assert(!!val == addr->is_ipv4); + val = irc_in_addr_is_loopback(&parsed); + assert(!!val == addr->is_loopback); + printf("Passed: %s (%s/%s)\n", addr->text, base64_v4, base64_v6); +} + +int +main(int argc, char *argv[]) +{ + unsigned int ii; + + for (ii = 0; test_addrs[ii].text; ++ii) + test_address(&test_addrs[ii]); + + return 0; +} diff --git a/ircd/test/test_stub.c b/ircd/test/test_stub.c new file mode 100644 index 0000000..470e0b0 --- /dev/null +++ b/ircd/test/test_stub.c @@ -0,0 +1,39 @@ +/* test_stub.c - support stubs for test programs */ + +#include "client.h" +#include "ircd_log.h" +#include "s_debug.h" +#include +#include + +struct Client me; +int log_inassert; + +void +log_write(enum LogSys subsys, enum LogLevel severity, unsigned int flags, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +void +debug(int level, const char *form, ...) +{ + va_list args; + + va_start(args, form); + vfprintf(stdout, form, args); + va_end(args); +} + +int +exit_client(struct Client *cptr, struct Client *bcptr, struct Client *sptr, + const char *comment) +{ + Debug((DEBUG_LIST, "exit_client(%p, %p, %p, \"%s\")\n", cptr, bcptr, sptr, comment)); + return 0; +} -- 2.20.1