]> granicus.if.org Git - libevent/commitdiff
New functions in evutil to clone inet_pton and inet_ntop, with tests.
authorNick Mathewson <nickm@torproject.org>
Fri, 2 Jan 2009 20:46:12 +0000 (20:46 +0000)
committerNick Mathewson <nickm@torproject.org>
Fri, 2 Jan 2009 20:46:12 +0000 (20:46 +0000)
Adapted from Tor code.

svn:r983

ChangeLog
configure.in
evutil.c
include/event2/util.h
test/Makefile.am
test/regress.c
test/regress.h
test/regress_util.c [new file with mode: 0644]

index c89e46f0ee7ed290549e80ff5e0fdc8c7a5c6672..b6b4ee1b9bdb8d56133bddcfbbee472be5fa5afe 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -133,7 +133,8 @@ Changes in current version:
  o Set the 0x20 bit on outgoing alphabetic characters in DNS requests randomly, and insist on a match in replies.  This helps resist DNS poisoning attacks.
  o Make the http connection close detection work properly with bufferevents and fix a potential memory leak associated with it.
  o Restructure the event backends so that they do not need to keep track of events themselves, as a side effect multiple events can use the same fd or signal.
-       
+ o Add generic implementations for parsing and emiting IPv6 addresses on platforms that do not have inet_ntop and/or inet_pton.
+
 Changes in 1.4.0:
  o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.
  o demote most http warnings to debug messages
index bd274a6ad914f4d0657d2a43838a2e9a048d11ae..3d69625e43c48da1b070d022278dc9dcdef4aa95 100644 (file)
@@ -53,7 +53,7 @@ AM_CONDITIONAL(ZLIB_REGRESS, [test "$have_zlib" != "no"])
 
 dnl Checks for header files.
 AC_HEADER_STDC
-AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in6.h sys/socket.h sys/uio.h)
+AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h)
 if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
        AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
        AC_EGREP_CPP(yes,
@@ -146,7 +146,7 @@ AC_C_INLINE
 AC_HEADER_TIME
 
 dnl Checks for library functions.
-AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop signal sigaction strtoll)
+AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton)
 
 AC_CHECK_SIZEOF(long)
 
index 33f42e1b74725e22cdb7c85eb9bc5f9d405d878c..bf161be230afad7adbdce4ad20a3289888662f26 100644 (file)
--- a/evutil.c
+++ b/evutil.c
 #endif
 #include <errno.h>
 #include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
 
 #ifndef _EVENT_HAVE_GETTIMEOFDAY
 #include <sys/timeb.h>
@@ -59,6 +67,8 @@
 #include "event2/util.h"
 #include "log.h"
 
+#include "strlcpy-internal.h"
+
 int
 evutil_socketpair(int family, int type, int protocol, evutil_socket_t fd[2])
 {
@@ -339,3 +349,221 @@ evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap)
        return r;
 #endif
 }
+
+#define USE_INTERNAL_NTOP
+#define USE_INTERNAL_PTON
+
+const char *
+evutil_inet_ntop(int af, const void *src, char *dst, size_t len)
+{
+#if defined(_EVENT_HAVE_INET_NTOP) && !defined(USE_INTERNAL_NTOP)
+       return inet_ntop(af, src, dst, len);
+#else
+       if (af == AF_INET) {
+               const struct in_addr *in = src;
+               const ev_uint32_t a = ntohl(in->s_addr);
+               int r;
+               r = evutil_snprintf(dst, len, "%d.%d.%d.%d",
+                                                       (int)(ev_uint8_t)((a>>24)&0xff),
+                                                       (int)(ev_uint8_t)((a>>16)&0xff),
+                                                       (int)(ev_uint8_t)((a>>8 )&0xff),
+                                                       (int)(ev_uint8_t)((a    )&0xff));
+               if (r<0||r>=len)
+                       return NULL;
+               else
+                       return dst;
+#ifdef AF_INET6
+       } else if (af == AF_INET6) {
+               const struct in6_addr *addr = src;
+               char buf[64], *cp;
+               int longestGapLen = 0, longestGapPos = -1, i,
+                       curGapPos = -1, curGapLen = 0;
+               ev_uint16_t words[8];
+               for (i = 0; i < 8; ++i) {
+                       words[i] =
+                               (((ev_uint16_t)addr->s6_addr[2*i])<<8) + addr->s6_addr[2*i+1];
+               }
+               if (words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 &&
+                       words[4] == 0 && ((words[5] == 0 && words[6] && words[7]) ||
+                                                         (words[5] == 0xffff))) {
+                       /* This is an IPv4 address. */
+                       if (words[5] == 0) {
+                               evutil_snprintf(buf, sizeof(buf), "::%d.%d.%d.%d",
+                                                               addr->s6_addr[12], addr->s6_addr[13],
+                                                               addr->s6_addr[14], addr->s6_addr[15]);
+                       } else {
+                               evutil_snprintf(buf, sizeof(buf), "::%x:%d.%d.%d.%d", words[5],
+                                                               addr->s6_addr[12], addr->s6_addr[13],
+                                                               addr->s6_addr[14], addr->s6_addr[15]);
+                       }
+                       if (strlen(buf) > len)
+                               return NULL;
+                       strlcpy(dst, buf, len);
+                       return dst;
+               }
+               i = 0;
+               while (i < 8) {
+                       if (words[i] == 0) {
+                               curGapPos = i++;
+                               curGapLen = 1;
+                               while (i<8 && words[i] == 0) {
+                                       ++i; ++curGapLen;
+                               }
+                               if (curGapLen > longestGapLen) {
+                                       longestGapPos = curGapPos;
+                                       longestGapLen = curGapLen;
+                               }
+                       } else {
+                               ++i;
+                       }
+               }
+               if (longestGapLen<=1)
+                       longestGapPos = -1;
+
+               cp = buf;
+               for (i = 0; i < 8; ++i) {
+                       if (words[i] == 0 && longestGapPos == i) {
+                               if (i == 0)
+                                       *cp++ = ':';
+                               *cp++ = ':';
+                               while (i < 8 && words[i] == 0)
+                                       ++i;
+                               --i; /* to compensate for loop increment. */
+                       } else {
+                               evutil_snprintf(cp,
+                                                               sizeof(buf)-(cp-buf), "%x", (unsigned)words[i]);
+                               cp += strlen(cp);
+                               if (i != 7)
+                                       *cp++ = ':';
+                       }
+               }
+               *cp = '\0';
+               if (strlen(buf) > len)
+                       return NULL;
+               strlcpy(dst, buf, len);
+               return dst;
+#endif
+       } else {
+               return NULL;
+       }
+#endif
+}
+
+#define ISDIGIT(c) isdigit((int)(unsigned char)(c))
+#define ISXDIGIT(c) isxdigit((int)(unsigned char)(c))
+
+int
+evutil_inet_pton(int af, const char *src, void *dst)
+{
+#if defined(_EVENT_HAVE_INET_PTON) && !defined(USE_INTERNAL_PTON)
+       return inet_pton(af, src, dst);
+#else
+       if (af == AF_INET) {
+#ifdef _EVENT_HAVE_INET_ATON
+               return inet_aton(src, dst);
+#else
+               ev_uint32_t r;
+               struct in_addr *out = dst;
+               if (strcmp(c, "255.255.255.255") == 0) {
+                       out->s_addr = 0xffffffffu;
+               } else {
+                 r = inet_addr(c);
+                 if (r == INADDR_NONE)
+                         return 0;
+                 addr->s_addr = r;
+         }
+         return 1;
+#endif
+#ifdef AF_INET6
+       } else if (af == AF_INET6) {
+               struct in6_addr *out = dst;
+               uint16_t words[8];
+               int gapPos = -1, i, setWords=0;
+               const char *dot = strchr(src, '.');
+               const char *eow; /* end of words. */
+               if (dot == src)
+                       return 0;
+               else if (!dot)
+                       eow = src+strlen(src);
+               else {
+                       int byte1,byte2,byte3,byte4;
+                       char more;
+                       for (eow = dot-1; eow >= src && ISDIGIT(*eow); --eow)
+                               ;
+                       ++eow;
+
+                       /* We use "scanf" because some platform inet_aton()s are too lax
+                        * about IPv4 addresses of the form "1.2.3" */
+                       if (sscanf(eow, "%d.%d.%d.%d%c",
+                                          &byte1,&byte2,&byte3,&byte4,&more) != 4)
+                               return 0;
+
+                       if (byte1 > 255 || byte1 < 0 ||
+                               byte2 > 255 || byte2 < 0 ||
+                               byte3 > 255 || byte3 < 0 ||
+                               byte4 > 255 || byte4 < 0)
+                               return 0;
+
+                       words[6] = (byte1<<8) | byte2;
+                       words[7] = (byte3<<8) | byte4;
+                       setWords += 2;
+               }
+
+               i = 0;
+               while (src < eow) {
+                       if (i > 7)
+                               return 0;
+                       if (ISXDIGIT(*src)) {
+                               char *next;
+                               long r = strtol(src, &next, 16);
+                               if (next > 4+src)
+                                       return 0;
+                               if (next == src)
+                                       return 0;
+                               if (r<0 || r>65536)
+                                       return 0;
+
+                               words[i++] = (uint16_t)r;
+                               setWords++;
+                               src = next;
+                               if (*src != ':' && src != eow)
+                                       return 0;
+                               ++src;
+                       } else if (*src == ':' && i > 0 && gapPos==-1) {
+                               gapPos = i;
+                               ++src;
+                       } else if (*src == ':' && i == 0 && src[1] == ':' && gapPos==-1) {
+                               gapPos = i;
+                               src += 2;
+                       } else {
+                               return 0;
+                       }
+               }
+
+               if (setWords > 8 ||
+                       (setWords == 8 && gapPos != -1) ||
+                       (setWords < 8 && gapPos == -1))
+                       return 0;
+
+               if (gapPos >= 0) {
+                       int nToMove = setWords - (dot ? 2 : 0) - gapPos;
+                       int gapLen = 8 - setWords;
+                       // assert(nToMove >= 0);
+                       if (nToMove < 0)
+                               return -1; /* should be impossible */
+                       memmove(&words[gapPos+gapLen], &words[gapPos],
+                                       sizeof(uint16_t)*nToMove);
+                       memset(&words[gapPos], 0, sizeof(uint16_t)*gapLen);
+               }
+               for (i = 0; i < 8; ++i) {
+                       out->s6_addr[2*i  ] = words[i] >> 8;
+                       out->s6_addr[2*i+1] = words[i] & 0xff;
+               }
+
+               return 1;
+#endif
+       } else {
+               return -1;
+       }
+#endif
+}
index e082b49cc1f82599281e824ea05325b9423fc83f..ddbe689f825cf0568a0c9e48cf7b8712ee575e35 100644 (file)
@@ -213,6 +213,9 @@ int evutil_snprintf(char *buf, size_t buflen, const char *format, ...)
        ;
 int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap);
 
+const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len);
+int evutil_inet_pton(int af, const char *src, void *dst);
+
 #ifdef __cplusplus
 }
 #endif
index 7b24502f9cf7ace73a68e60d7156523e117550ac..8b7ee326c9c6ec360c009859665df377bbfbd827 100644 (file)
@@ -18,6 +18,7 @@ test_time_SOURCES = test-time.c
 test_time_LDADD = ../libevent_core.la
 regress_SOURCES = regress.c regress.h regress_http.c regress_dns.c \
        regress_rpc.c regress.gen.c regress.gen.h regress_et.c \
+       regress_util.c \
        $(regress_pthread_SOURCES) $(regress_zlib_SOURCES)
 if PTHREAD_REGRESS
 regress_pthread_SOURCES = regress_pthread.c
index bbb89582499d48d229092a9d8dc3a40f8a6cb216..b5da2ec11fa826144537a26dea4d4abdca8923ff 100644 (file)
@@ -2307,6 +2307,7 @@ main (int argc, char **argv)
 
        test_evutil_strtoll();
        test_evutil_snprintf();
+       util_suite();
 
        test_periodictimeout();
 
index cb92c9ea3249eb1270ce9c078158e1d0ae82780e..c84b1c43d254a4ff5d68737eb9a184f189e06cde 100644 (file)
@@ -38,6 +38,8 @@ void rpc_suite(void);
 
 void dns_suite(void);
 
+void util_suite(void);
+
 void regress_pthread(void);
 void regress_zlib(void);
 
diff --git a/test/regress_util.c b/test/regress_util.c
new file mode 100644 (file)
index 0000000..5d9b459
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2009 Niels Provos <provos@citi.umich.edu>
+ * 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 name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 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 AUTHOR 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.
+ */
+#ifdef WIN32
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "event2/util.h"
+
+void util_suite(void);
+
+enum entry_status { NORMAL, CANONICAL, BAD };
+
+/* This is a big table of results we expect from generating and parsing */
+static struct ipv4_entry {
+       const char *addr;
+       ev_uint32_t res;
+       enum entry_status status;
+} ipv4_entries[] = {
+       { "1.2.3.4", 0x01020304u, CANONICAL },
+       { "255.255.255.255", 0xffffffffu, CANONICAL },
+       { "256.0.0.0", 0, BAD },
+       { "ABC", 0, BAD },
+       { "1.2.3.4.5", 0, BAD },
+       { "176.192.208.244", 0xb0c0d0f4, CANONICAL },
+       { NULL, 0, BAD },
+};
+
+static struct ipv6_entry {
+       const char *addr;
+       ev_uint32_t res[4];
+       enum entry_status status;
+} ipv6_entries[] = {
+       { "::", { 0, 0, 0, 0, }, CANONICAL },
+       { "0:0:0:0:0:0:0:0", { 0, 0, 0, 0, }, NORMAL },
+       { "::1", { 0, 0, 0, 1, }, CANONICAL },
+       { "ffff:1::", { 0xffff0001u, 0, 0, 0, }, CANONICAL },
+       { "ffff:0000::", { 0xffff0000u, 0, 0, 0, }, NORMAL },
+       { "ffff::1234", { 0xffff0000u, 0, 0, 0x1234, }, CANONICAL },
+       { "0102::1.2.3.4", {0x01020000u, 0, 0, 0x01020304u }, NORMAL },
+       { "::9:c0a8:1:1", { 0, 0, 0x0009c0a8u, 0x00010001u }, CANONICAL },
+       { "::ffff:1.2.3.4", { 0, 0, 0x000ffffu, 0x01020304u }, CANONICAL },
+       { "FFFF::", { 0xffff0000u, 0, 0, 0 }, NORMAL },
+       { "foobar.", { 0, 0, 0, 0 }, BAD },
+       { "foobar", { 0, 0, 0, 0 }, BAD },
+       { "fo:obar", { 0, 0, 0, 0 }, BAD },
+       { "ffff", { 0, 0, 0, 0 }, BAD },
+       { "fffff::", { 0, 0, 0, 0 }, BAD },
+       { "fffff::", { 0, 0, 0, 0 }, BAD },
+       { "1:2:33333:4::", { 0, 0, 0, 0 }, BAD },
+       { "1:2:3:4:5:6:7:8:9", { 0, 0, 0, 0 }, BAD },
+       { "1::2::3", { 0, 0, 0, 0 }, BAD },
+       { ":::1", { 0, 0, 0, 0 }, BAD },
+       { NULL, { 0, 0, 0, 0,  }, BAD },
+};
+
+static void
+regress_ipv4_parse(void)
+{
+       int i;
+       int ok = 1;
+       printf("Testing IPv4 parsing...");
+
+       for (i = 0; ipv4_entries[i].addr; ++i) {
+               char written[128];
+               struct ipv4_entry *ent = &ipv4_entries[i];
+               struct in_addr in;
+               int r;
+               r = evutil_inet_pton(AF_INET, ent->addr, &in);
+               if (r == 0) {
+                       if (ent->status != BAD) {
+                               printf("%s did not parse, but it's a good address!\n",
+                                          ent->addr);
+                               ok = 0;
+                       }
+                       continue;
+               }
+               if (ent->status == BAD) {
+                       printf("%s parsed, but we expected an error\n", ent->addr);
+                       ok = 0;
+                       continue;
+               }
+               if (ntohl(in.s_addr) != ent->res) {
+                       printf("%s parsed to %lx, but we expected %lx\n", ent->addr,
+                                  (unsigned long)ntohl(in.s_addr),
+                                  (unsigned long)ent->res);
+                       ok = 0;
+                       continue;
+               }
+               if (ent->status == CANONICAL) {
+                       const char *w = evutil_inet_ntop(AF_INET, &in, written,
+                                                                                        sizeof(written));
+                       if (!w) {
+                               printf("Tried to write out %s; got NULL.\n", ent->addr);
+                               ok = 0;
+                               continue;
+                       }
+                       if (strcmp(written, ent->addr)) {
+                               printf("Tried to write out %s; got %s\n", ent->addr, written);
+                               ok = 0;
+                               continue;
+                       }
+               }
+
+       }
+       if (!ok) {
+               printf("FAILED\n");
+               exit(1);
+       }
+       printf("OK\n");
+}
+
+static void
+regress_ipv6_parse(void)
+{
+#ifdef AF_INET6
+       int i, j;
+       int ok = 1;
+       printf("Testing IPv6 parsing...");
+
+       for (i = 0; ipv6_entries[i].addr; ++i) {
+               char written[128];
+               struct ipv6_entry *ent = &ipv6_entries[i];
+               struct in6_addr in6;
+               int r;
+               r = evutil_inet_pton(AF_INET6, ent->addr, &in6);
+               if (r == 0) {
+                       if (ent->status != BAD) {
+                               printf("%s did not parse, but it's a good address!\n",
+                                          ent->addr);
+                               ok = 0;
+                       }
+                       continue;
+               }
+               if (ent->status == BAD) {
+                       printf("%s parsed, but we expected an error\n", ent->addr);
+                       ok = 0;
+                       continue;
+               }
+               for (j = 0; j < 4; ++j) {
+                       /* Can't use s6_addr32 here; some don't have it. */
+                       ev_uint32_t u =
+                               (in6.s6_addr[j*4  ] << 24) |
+                               (in6.s6_addr[j*4+1] << 16) |
+                               (in6.s6_addr[j*4+2] << 8) |
+                               (in6.s6_addr[j*4+3]);
+                       if (u != ent->res[j]) {
+                               printf("%s did not parse as expected.\n", ent->addr);
+                               ok = 0;
+                               continue;
+                       }
+               }
+               if (ent->status == CANONICAL) {
+                       const char *w = evutil_inet_ntop(AF_INET6, &in6, written,
+                                                                                        sizeof(written));
+                       if (!w) {
+                               printf("Tried to write out %s; got NULL.\n", ent->addr);
+                               ok = 0;
+                               continue;
+                       }
+                       if (strcmp(written, ent->addr)) {
+                               printf("Tried to write out %s; got %s\n", ent->addr, written);
+                               ok = 0;
+                               continue;
+                       }
+               }
+
+       }
+       if (!ok) {
+               printf("FAILED\n");
+               exit(1);
+       }
+       printf("OK\n");
+#else
+       print("Skipping IPv6 address parsing.\n");
+#endif
+}
+
+
+void
+util_suite(void)
+{
+       regress_ipv4_parse();
+       regress_ipv6_parse();
+}