New functions in evutil to clone inet_pton and inet_ntop, with tests.

Adapted from Tor code.

svn:r983
This commit is contained in:
Nick Mathewson 2009-01-02 20:46:12 +00:00
parent 172b657567
commit 0d9d5cfe22
8 changed files with 465 additions and 3 deletions

View File

@ -133,6 +133,7 @@ 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.

View 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)

228
evutil.c
View File

@ -50,6 +50,14 @@
#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
}

View 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

View 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

View File

@ -2307,6 +2307,7 @@ main (int argc, char **argv)
test_evutil_strtoll();
test_evutil_snprintf();
util_suite();
test_periodictimeout();

View 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);

226
test/regress_util.c Normal file
View File

@ -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();
}