Convert irc_in_addr_* to macros. Add test program for that code. Fix
authorMichael Poole <mdpoole@troilus.org>
Tue, 28 Dec 2004 21:12:06 +0000 (21:12 +0000)
committerMichael Poole <mdpoole@troilus.org>
Tue, 28 Dec 2004 21:12:06 +0000 (21:12 +0000)
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
configure
configure.in
include/res.h
ircd/ircd_res.c
ircd/ircd_string.c
ircd/test/Makefile [deleted file]
ircd/test/Makefile.in [new file with mode: 0644]
ircd/test/ircd_in_addr_t.c [new file with mode: 0644]
ircd/test/test_stub.c [new file with mode: 0644]

index c02fdcc6801ecde32141b1b95eef23c29daa9967..c5053faa129a1eb8a587e00d4bb05fd557528b4e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2004-12-28  Michael Poole <mdpoole@troilus.org>
+
+       * 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 <mdpoole@troilus.org>
 
        * include/client.h: Move unreg, privs, capab and active fields
index 014757abebd2cf5da41975938014e8f5bc93b80b..2e114da6b166c0497df6b3aa45c8e81934e72c6e 100755 (executable)
--- 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" ;;
index 4d3450efdbe6df52c109c934508be0c45df93860..88ec8c71f848bd8de6622551009f10f8f35d9da4 100644 (file)
@@ -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 "
index 9f5533517249205c95ba8a310a500af842630eef..1029af09043087bb152559f11d130e218927a0a1 100644 (file)
@@ -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
index 0ce4fd60b4c86e32407efd359d3c4c467889d0ef..962e211b70e7f530a49d99bbee962e02ebde3df7 100644 (file)
@@ -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;
-}
index 6b18f7cbbfc8e40fd7b8ea8d7a4682d1ed5c711b..fab7c715331c4c799de6055fa5f52daaa7157d3c 100644 (file)
@@ -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 (file)
index 460e3a6..0000000
+++ /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 (file)
index 0000000..8e6a05d
--- /dev/null
@@ -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 (file)
index 0000000..90dd319
--- /dev/null
@@ -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 <stdio.h>
+#include <string.h>
+#include <netinet/in.h>
+
+/** 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 (file)
index 0000000..470e0b0
--- /dev/null
@@ -0,0 +1,39 @@
+/* test_stub.c - support stubs for test programs */
+
+#include "client.h"
+#include "ircd_log.h"
+#include "s_debug.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+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;
+}