From a8acef0bfb8502573b7e11703dee9d7b7027497d Mon Sep 17 00:00:00 2001 From: ume Date: Sun, 26 Feb 2006 05:44:21 +0000 Subject: Sync inet_net_pton() and inet_net_ntop() with latest BIND9's includes an IPv6 support. PR: kern/93740 Submitted by: Rudolf Cejka Obtained from: BIND9 MFC after: 1 week --- lib/libc/net/inet_net.3 | 14 +- lib/libc/net/inet_net_ntop.c | 161 +++++++++++++++++++-- lib/libc/net/inet_net_pton.c | 330 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 420 insertions(+), 85 deletions(-) diff --git a/lib/libc/net/inet_net.3 b/lib/libc/net/inet_net.3 index 9ca00a6..a9d0781 100644 --- a/lib/libc/net/inet_net.3 +++ b/lib/libc/net/inet_net.3 @@ -36,7 +36,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 18, 1997 +.Dd February 26, 2006 .Dt INET_NET 3 .Os .Sh NAME @@ -88,10 +88,12 @@ It will be set to .Er ENOENT if the Internet network number was not valid). .Pp -The only value for +The currently supported values for .Fa af -currently supported is -.Dv AF_INET . +are +.Dv AF_INET +and +.Dv AF_INET6 . The .Fa size argument @@ -149,6 +151,10 @@ may be decimal, octal, or hexadecimal, as specified in the C language (i.e., a leading 0x or 0X implies hexadecimal; otherwise, a leading 0 implies octal; otherwise, the number is interpreted as decimal). +.\" +.\" .Sh NETWORK NUMBERS (IP VERSION 6) +.\" XXX - document this! +.\" .Sh SEE ALSO .Xr byteorder 3 , .Xr inet 3 , diff --git a/lib/libc/net/inet_net_ntop.c b/lib/libc/net/inet_net_ntop.c index 8f3b1d7..9258273 100644 --- a/lib/libc/net/inet_net_ntop.c +++ b/lib/libc/net/inet_net_ntop.c @@ -1,22 +1,22 @@ /* - * Copyright (c) 1996 by Internet Software Consortium. + * 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM 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. + * 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. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char orig_rcsid[] = "From Id: inet_net_ntop.c,v 8.2 1996/08/08 06:54:44 vixie Exp"; +static const char orig_rcsid[] = "From Id: inet_net_ntop.c,v 1.1.2.1.8.1 2004/03/09 08:33:32 marka Exp"; #endif #include __FBSDID("$FreeBSD$"); @@ -39,6 +39,8 @@ __FBSDID("$FreeBSD$"); static char * inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size); +static char * inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, + size_t size); /* * char * @@ -61,6 +63,8 @@ inet_net_ntop(af, src, bits, dst, size) switch (af) { case AF_INET: return (inet_net_ntop_ipv4(src, bits, dst, size)); + case AF_INET6: + return (inet_net_ntop_ipv6(src, bits, dst, size)); default: errno = EAFNOSUPPORT; return (NULL); @@ -76,7 +80,7 @@ inet_net_ntop(af, src, bits, dst, size) * pointer to dst, or NULL if an error occurred (check errno). * note: * network byte order assumed. this means 192.5.5.240/28 has - * 0x11110000 in its fourth octet. + * 0b11110000 in its fourth octet. * author: * Paul Vixie (ISC), July 1996 */ @@ -96,16 +100,18 @@ inet_net_ntop_ipv4(src, bits, dst, size) errno = EINVAL; return (NULL); } + if (bits == 0) { if (size < sizeof "0") goto emsgsize; *dst++ = '0'; + size--; *dst = '\0'; } /* Format whole octets. */ for (b = bits / 8; b > 0; b--) { - if (size < sizeof "255.") + if (size <= sizeof "255.") goto emsgsize; t = dst; dst += SPRINTF((dst, "%u", *src++)); @@ -119,7 +125,7 @@ inet_net_ntop_ipv4(src, bits, dst, size) /* Format partial octet. */ b = bits % 8; if (b > 0) { - if (size < sizeof ".255") + if (size <= sizeof ".255") goto emsgsize; t = dst; if (dst != odst) @@ -130,7 +136,7 @@ inet_net_ntop_ipv4(src, bits, dst, size) } /* Format CIDR /width. */ - if (size < sizeof "/32") + if (size <= sizeof "/32") goto emsgsize; dst += SPRINTF((dst, "/%u", bits)); return (odst); @@ -141,6 +147,133 @@ inet_net_ntop_ipv4(src, bits, dst, size) } /* + * static char * + * inet_net_ntop_ipv6(src, bits, fakebits, dst, size) + * convert IPv6 network number from network to presentation format. + * generates CIDR style result always. Picks the shortest representation + * unless the IP is really IPv4. + * always prints specified number of bits (bits). + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0b11110000 in its fourth octet. + * author: + * Vadim Kogan (UCB), June 2001 + * Original version (IPv4) by Paul Vixie (ISC), July 1996 + */ + +static char * +inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) { + u_int m; + int b; + int p; + int zero_s, zero_l, tmp_zero_s, tmp_zero_l; + int i; + int is_ipv4 = 0; + unsigned char inbuf[16]; + char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; + char *cp; + int words; + u_char *s; + + if (bits < 0 || bits > 128) { + errno = EINVAL; + return (NULL); + } + + cp = outbuf; + + if (bits == 0) { + *cp++ = ':'; + *cp++ = ':'; + *cp = '\0'; + } else { + /* Copy src to private buffer. Zero host part. */ + p = (bits + 7) / 8; + memcpy(inbuf, src, p); + memset(inbuf + p, 0, 16 - p); + b = bits % 8; + if (b != 0) { + m = ~0 << (8 - b); + inbuf[p-1] &= m; + } + + s = inbuf; + + /* how many words need to be displayed in output */ + words = (bits + 15) / 16; + if (words == 1) + words = 2; + + /* Find the longest substring of zero's */ + zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0; + for (i = 0; i < (words * 2); i += 2) { + if ((s[i] | s[i+1]) == 0) { + if (tmp_zero_l == 0) + tmp_zero_s = i / 2; + tmp_zero_l++; + } else { + if (tmp_zero_l && zero_l < tmp_zero_l) { + zero_s = tmp_zero_s; + zero_l = tmp_zero_l; + tmp_zero_l = 0; + } + } + } + + if (tmp_zero_l && zero_l < tmp_zero_l) { + zero_s = tmp_zero_s; + zero_l = tmp_zero_l; + } + + if (zero_l != words && zero_s == 0 && ((zero_l == 6) || + ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) || + ((zero_l == 7 && s[14] != 0 && s[15] != 1))))) + is_ipv4 = 1; + + /* Format whole words. */ + for (p = 0; p < words; p++) { + if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) { + /* Time to skip some zeros */ + if (p == zero_s) + *cp++ = ':'; + if (p == words - 1) + *cp++ = ':'; + s++; + s++; + continue; + } + + if (is_ipv4 && p > 5 ) { + *cp++ = (p == 6) ? ':' : '.'; + cp += SPRINTF((cp, "%u", *s++)); + /* we can potentially drop the last octet */ + if (p != 7 || bits > 120) { + *cp++ = '.'; + cp += SPRINTF((cp, "%u", *s++)); + } + } else { + if (cp != outbuf) + *cp++ = ':'; + cp += SPRINTF((cp, "%x", *s * 256 + s[1])); + s += 2; + } + } + } + /* Format CIDR /width. */ + SPRINTF((cp, "/%u", bits)); + if (strlen(outbuf) + 1 > size) + goto emsgsize; + strcpy(dst, outbuf); + + return (dst); + +emsgsize: + errno = EMSGSIZE; + return (NULL); +} +/* * Weak aliases for applications that use certain private entry points, * and fail to include . */ diff --git a/lib/libc/net/inet_net_pton.c b/lib/libc/net/inet_net_pton.c index 3a71454..edd4678 100644 --- a/lib/libc/net/inet_net_pton.c +++ b/lib/libc/net/inet_net_pton.c @@ -1,22 +1,22 @@ /* - * Copyright (c) 1996 by Internet Software Consortium. + * 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM 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. + * 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. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char orig_rcsid[] = "From Id: inet_net_pton.c,v 1.8 1996/11/21 10:28:12 vixie Exp $"; +static const char orig_rcsid[] = "From Id: inet_net_pton.c,v 1.4.2.1.8.2 2004/03/17 00:29:47 marka Exp"; #endif #include __FBSDID("$FreeBSD$"); @@ -24,6 +24,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -39,37 +40,6 @@ __FBSDID("$FreeBSD$"); # define SPRINTF(x) ((size_t)sprintf x) #endif -static int inet_net_pton_ipv4(const char *src, u_char *dst, size_t size); - -/* - * static int - * inet_net_pton(af, src, dst, size) - * convert network number from presentation to network format. - * accepts hex octets, hex strings, decimal octets, and /CIDR. - * "size" is in bytes and describes "dst". - * return: - * number of bits, either imputed classfully or specified with /CIDR, - * or -1 if some failure occurred (check errno). ENOENT means it was - * not a valid network specification. - * author: - * Paul Vixie (ISC), June 1996 - */ -int -inet_net_pton(af, src, dst, size) - int af; - const char *src; - void *dst; - size_t size; -{ - switch (af) { - case AF_INET: - return (inet_net_pton_ipv4(src, dst, size)); - default: - errno = EAFNOSUPPORT; - return (-1); - } -} - /* * static int * inet_net_pton_ipv4(src, dst, size) @@ -82,46 +52,47 @@ inet_net_pton(af, src, dst, size) * not an IPv4 network specification. * note: * network byte order assumed. this means 192.5.5.240/28 has - * 0x11110000 in its fourth octet. + * 0b11110000 in its fourth octet. * author: * Paul Vixie (ISC), June 1996 */ static int -inet_net_pton_ipv4(src, dst, size) - const char *src; - u_char *dst; - size_t size; -{ - static const char - xdigits[] = "0123456789abcdef", - digits[] = "0123456789"; - int n, ch, tmp, dirty, bits; +inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) { + static const char xdigits[] = "0123456789abcdef"; + static const char digits[] = "0123456789"; + int n, ch, tmp = 0, dirty, bits; const u_char *odst = dst; ch = *src++; if (ch == '0' && (src[0] == 'x' || src[0] == 'X') - && isascii(src[1]) && isxdigit(src[1])) { + && isascii((unsigned char)(src[1])) + && isxdigit((unsigned char)(src[1]))) { /* Hexadecimal: Eat nybble string. */ - if (size <= 0) + if (size <= 0U) goto emsgsize; - *dst = 0, dirty = 0; + dirty = 0; src++; /* skip x or X. */ - while ((ch = *src++) != '\0' && - isascii(ch) && isxdigit(ch)) { + while ((ch = *src++) != '\0' && isascii(ch) && isxdigit(ch)) { if (isupper(ch)) ch = tolower(ch); n = strchr(xdigits, ch) - xdigits; assert(n >= 0 && n <= 15); - *dst |= n; - if (!dirty++) - *dst <<= 4; - else if (size-- > 0) - *++dst = 0, dirty = 0; + if (dirty == 0) + tmp = n; else + tmp = (tmp << 4) | n; + if (++dirty == 2) { + if (size-- <= 0U) + goto emsgsize; + *dst++ = (u_char) tmp; + dirty = 0; + } + } + if (dirty) { /* Odd trailing nybble? */ + if (size-- <= 0U) goto emsgsize; + *dst++ = (u_char) (tmp << 4); } - if (dirty) - size--; } else if (isascii(ch) && isdigit(ch)) { /* Decimal: eat dotted digit string. */ for (;;) { @@ -135,7 +106,7 @@ inet_net_pton_ipv4(src, dst, size) goto enoent; } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch)); - if (size-- <= 0) + if (size-- <= 0U) goto emsgsize; *dst++ = (u_char) tmp; if (ch == '\0' || ch == '/') @@ -150,7 +121,8 @@ inet_net_pton_ipv4(src, dst, size) goto enoent; bits = -1; - if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst) { + if (ch == '/' && isascii((unsigned char)(src[0])) && + isdigit((unsigned char)(src[0])) && dst > odst) { /* CIDR width specifier. Nothing can follow it. */ ch = *src++; /* Skip over the /. */ bits = 0; @@ -178,7 +150,7 @@ inet_net_pton_ipv4(src, dst, size) if (*odst >= 240) /* Class E */ bits = 32; else if (*odst >= 224) /* Class D */ - bits = 4; + bits = 8; else if (*odst >= 192) /* Class C */ bits = 24; else if (*odst >= 128) /* Class B */ @@ -188,10 +160,16 @@ inet_net_pton_ipv4(src, dst, size) /* If imputed mask is narrower than specified octets, widen. */ if (bits < ((dst - odst) * 8)) bits = (dst - odst) * 8; + /* + * If there are no additional bits specified for a class D + * address adjust bits to 4. + */ + if (bits == 8 && *odst == 224) + bits = 4; } /* Extend network to cover the actual mask. */ while (bits > ((dst - odst) * 8)) { - if (size-- <= 0) + if (size-- <= 0U) goto emsgsize; *dst++ = '\0'; } @@ -206,6 +184,224 @@ inet_net_pton_ipv4(src, dst, size) return (-1); } +static int +getbits(const char *src, int *bitsp) { + static const char digits[] = "0123456789"; + int n; + int val; + char ch; + + val = 0; + n = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + pch = strchr(digits, ch); + if (pch != NULL) { + if (n++ != 0 && val == 0) /* no leading zeros */ + return (0); + val *= 10; + val += (pch - digits); + if (val > 128) /* range */ + return (0); + continue; + } + return (0); + } + if (n == 0) + return (0); + *bitsp = val; + return (1); +} + +static int +getv4(const char *src, u_char *dst, int *bitsp) { + static const char digits[] = "0123456789"; + u_char *odst = dst; + int n; + u_int val; + char ch; + + val = 0; + n = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + pch = strchr(digits, ch); + if (pch != NULL) { + if (n++ != 0 && val == 0) /* no leading zeros */ + return (0); + val *= 10; + val += (pch - digits); + if (val > 255) /* range */ + return (0); + continue; + } + if (ch == '.' || ch == '/') { + if (dst - odst > 3) /* too many octets? */ + return (0); + *dst++ = val; + if (ch == '/') + return (getbits(src, bitsp)); + val = 0; + n = 0; + continue; + } + return (0); + } + if (n == 0) + return (0); + if (dst - odst > 3) /* too many octets? */ + return (0); + *dst++ = val; + return (1); +} + +static int +inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) { + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, saw_xdigit; + u_int val; + int digits; + int bits; + size_t bytes; + int words; + int ipv4; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + goto enoent; + curtok = src; + saw_xdigit = 0; + val = 0; + digits = 0; + bits = -1; + ipv4 = 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 (++digits > 4) + goto enoent; + saw_xdigit = 1; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + goto enoent; + colonp = tp; + continue; + } else if (*src == '\0') + goto enoent; + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + saw_xdigit = 0; + digits = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + getv4(curtok, tp, &bits) > 0) { + tp += NS_INADDRSZ; + saw_xdigit = 0; + ipv4 = 1; + break; /* '\0' was seen by inet_pton4(). */ + } + if (ch == '/' && getbits(src, &bits) > 0) + break; + goto enoent; + } + if (saw_xdigit) { + if (tp + NS_INT16SZ > endp) + goto enoent; + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + } + if (bits == -1) + bits = 128; + + words = (bits + 15) / 16; + if (words < 2) + words = 2; + if (ipv4) + words = 8; + endp = tmp + 2 * words; + + 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) + goto enoent; + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + goto enoent; + + bytes = (bits + 7) / 8; + if (bytes > size) + goto emsgsize; + memcpy(dst, tmp, bytes); + return (bits); + + enoent: + errno = ENOENT; + return (-1); + + emsgsize: + errno = EMSGSIZE; + return (-1); +} + +/* + * int + * inet_net_pton(af, src, dst, size) + * convert network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not a valid network specification. + * author: + * Paul Vixie (ISC), June 1996 + */ +int +inet_net_pton(int af, const char *src, void *dst, size_t size) { + switch (af) { + case AF_INET: + return (inet_net_pton_ipv4(src, dst, size)); + case AF_INET6: + return (inet_net_pton_ipv6(src, dst, size)); + default: + errno = EAFNOSUPPORT; + return (-1); + } +} + /* * Weak aliases for applications that use certain private entry points, * and fail to include . -- cgit v1.1