diff --git a/LICENSE b/LICENSE index f62d7f19..57279fc8 100644 --- a/LICENSE +++ b/LICENSE @@ -40,3 +40,7 @@ The externally maintained libraries used by libuv are: - libeio, located at eio/ is copyright Marc Alexander Lehmann, and dual-licensed under the MIT license and GPL2. + + - inet_pton and inet_ntop implementations, contained in src/inet.c, are + copyright the Internet Systems Consortium, Inc., and licensed under the ISC + license. diff --git a/config-mingw.mk b/config-mingw.mk index a4cc037c..8a253bae 100644 --- a/config-mingw.mk +++ b/config-mingw.mk @@ -37,7 +37,7 @@ RUNNER_LINKFLAGS=$(LINKFLAGS) RUNNER_LIBS=-lws2_32 -lpsapi -liphlpapi RUNNER_SRC=test/runner-win.c -uv.a: $(WIN_OBJS) src/cares.o src/fs-poll.o src/uv-common.o $(CARES_OBJS) +uv.a: $(WIN_OBJS) src/cares.o src/fs-poll.o src/inet.o src/uv-common.o $(CARES_OBJS) $(AR) rcs uv.a $^ src/%.o: src/%.c include/uv.h include/uv-private/uv-win.h diff --git a/config-unix.mk b/config-unix.mk index 0581b511..667e7b94 100644 --- a/config-unix.mk +++ b/config-unix.mk @@ -131,7 +131,7 @@ endif RUNNER_LIBS= RUNNER_SRC=test/runner-unix.c -uv.a: $(OBJS) src/cares.o src/fs-poll.o src/uv-common.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS) +uv.a: $(OBJS) src/cares.o src/fs-poll.o src/inet.o src/uv-common.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS) $(AR) rcs uv.a $^ src/%.o: src/%.c include/uv.h include/uv-private/uv-unix.h diff --git a/include/uv.h b/include/uv.h index 4298489b..1f8becab 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1622,6 +1622,12 @@ UV_EXTERN struct sockaddr_in6 uv_ip6_addr(const char* ip, int port); UV_EXTERN int uv_ip4_name(struct sockaddr_in* src, char* dst, size_t size); UV_EXTERN int uv_ip6_name(struct sockaddr_in6* src, char* dst, size_t size); +/* Cross-platform IPv6-capable implementation of the 'standard' inet_ntop */ +/* and inet_pton functions. On success they return UV_OK. If an error */ +/* the target of the `dst` pointer is unmodified. */ +uv_err_t uv_inet_ntop(int af, const void* src, char* dst, size_t size); +uv_err_t uv_inet_pton(int af, const char* src, void* dst); + /* Gets the executable path */ UV_EXTERN int uv_exepath(char* buffer, size_t* size); diff --git a/src/inet.c b/src/inet.c new file mode 100644 index 00000000..293fbf6d --- /dev/null +++ b/src/inet.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "uv.h" +#include "uv-common.h" + + +static const uv_err_t uv_eafnosupport_ = { UV_EAFNOSUPPORT, 0 }; +static const uv_err_t uv_enospc_ = { UV_ENOSPC, 0 }; +static const uv_err_t uv_einval_ = { UV_EINVAL, 0 }; + +static uv_err_t inet_ntop4(const unsigned char *src, char *dst, size_t size); +static uv_err_t inet_ntop6(const unsigned char *src, char *dst, size_t size); +static uv_err_t inet_pton4(const char *src, unsigned char *dst); +static uv_err_t inet_pton6(const char *src, unsigned char *dst); + + +uv_err_t uv_inet_ntop(int af, const void* src, char* dst, size_t size) { + switch (af) { + case AF_INET: + return (inet_ntop4(src, dst, size)); + case AF_INET6: + return (inet_ntop6(src, dst, size)); + default: + return uv_eafnosupport_; + } + /* NOTREACHED */ +} + + +static uv_err_t inet_ntop4(const unsigned char *src, char *dst, size_t size) { + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + size_t l; + +#ifndef _WIN32 + l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); +#else + l = _snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); +#endif + if (l <= 0 || l >= size) { + return uv_enospc_; + } + strncpy(dst, tmp, size); + dst[size - 1] = '\0'; + return uv_ok_; +} + + +static uv_err_t inet_ntop6(const unsigned char *src, char *dst, size_t size) { + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { int base, len; } best, cur; + unsigned int words[sizeof(struct in6_addr) / sizeof(uint16_t)]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < (int) sizeof(struct in6_addr); i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; + cur.base = -1; + cur.len = 0; + for (i = 0; i < (int) ARRAY_SIZE(words); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (int) ARRAY_SIZE(words); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) { + uv_err_t err = inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)); + if (err.code != UV_OK) + return err; + tp += strlen(tp); + break; + } + tp += sprintf(tp, "%x", words[i]); + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + return uv_enospc_; + } + strcpy(dst, tmp); + return uv_ok_; +} + + +uv_err_t uv_inet_pton(int af, const char* src, void* dst) { + switch (af) { + case AF_INET: + return (inet_pton4(src, dst)); + case AF_INET6: + return (inet_pton6(src, dst)); + default: + return uv_eafnosupport_; + } + /* NOTREACHED */ +} + + +static uv_err_t inet_pton4(const char *src, unsigned char *dst) { + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + unsigned char tmp[sizeof(struct in_addr)], *tp; + + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr(digits, ch)) != NULL) { + unsigned int nw = *tp * 10 + (pch - digits); + + if (saw_digit && *tp == 0) + return uv_einval_; + if (nw > 255) + return uv_einval_; + *tp = nw; + if (!saw_digit) { + if (++octets > 4) + return uv_einval_; + saw_digit = 1; + } + } else if (ch == '.' && saw_digit) { + if (octets == 4) + return uv_einval_; + *++tp = 0; + saw_digit = 0; + } else + return uv_einval_; + } + if (octets < 4) + return uv_einval_; + memcpy(dst, tmp, sizeof(struct in_addr)); + return uv_ok_; +} + + +static uv_err_t inet_pton6(const char *src, unsigned char *dst) { + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + unsigned char tmp[sizeof(struct in6_addr)], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, seen_xdigits; + unsigned int val; + + memset((tp = tmp), '\0', sizeof tmp); + endp = tp + sizeof tmp; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return uv_einval_; + curtok = src; + seen_xdigits = 0; + val = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (++seen_xdigits > 4) + return uv_einval_; + continue; + } + if (ch == ':') { + curtok = src; + if (!seen_xdigits) { + if (colonp) + return uv_einval_; + colonp = tp; + continue; + } else if (*src == '\0') { + return uv_einval_; + } + if (tp + sizeof(uint16_t) > endp) + return uv_einval_; + *tp++ = (unsigned char) (val >> 8) & 0xff; + *tp++ = (unsigned char) val & 0xff; + seen_xdigits = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + sizeof(struct in_addr)) <= endp)) { + uv_err_t err = inet_pton4(curtok, tp); + if (err.code == 0) { + tp += sizeof(struct in_addr); + seen_xdigits = 0; + break; /*%< '\\0' was seen by inet_pton4(). */ + } + } + return uv_einval_; + } + if (seen_xdigits) { + if (tp + sizeof(uint16_t) > endp) + return uv_einval_; + *tp++ = (unsigned char) (val >> 8) & 0xff; + *tp++ = (unsigned char) val & 0xff; + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + if (tp == endp) + return uv_einval_; + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return uv_einval_; + memcpy(dst, tmp, sizeof tmp); + return uv_ok_; +} diff --git a/uv.gyp b/uv.gyp index 3e57f12c..d6a97541 100644 --- a/uv.gyp +++ b/uv.gyp @@ -52,6 +52,7 @@ 'include/uv-private/tree.h', 'src/cares.c', 'src/fs-poll.c', + 'src/inet.c', 'src/uv-common.c', 'src/uv-common.h', 'src/ares/ares_cancel.c',