Implement AF_INET6 back-ends for inet_net_ntop() and inet_net_pton(), from

the BIND 8.3.0-T2A sources.  Thanks to Paul Vixie for the pointer to it.
This commit is contained in:
lukem 2001-12-08 12:06:12 +00:00
parent 32a96141e4
commit 686d221d91
3 changed files with 370 additions and 16 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: inet_net.3,v 1.6 2001/09/16 02:54:17 wiz Exp $
.\" $NetBSD: inet_net.3,v 1.7 2001/12/08 12:06:12 lukem Exp $
.\"
.\" Copyright (c) 1997 The NetBSD Foundation, Inc.
.\" All rights reserved.
@ -34,7 +34,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd June 18, 1997
.Dd December 8, 2001
.Dt INET_NET 3
.Os
.Sh NAME
@ -81,9 +81,12 @@ It will be set to
.Er ENOENT
if the Internet network number was not valid).
.Pp
The currently supported value for
The currently supported values for
.Fa af
is: AF_INET.
are
.Dv AF_INET
and
.Dv AF_INET6 .
.Fa size
is the size of the result buffer
.Fa dst .
@ -137,6 +140,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 ,
@ -148,3 +155,7 @@ and
.Nm inet_net_pton
functions appeared in BIND 4.9.4 and thence
.Nx 1.3 .
Support for
.Dv AF_INET6
appeared in
.Nx 1.6 .

View File

@ -1,4 +1,4 @@
/* $NetBSD: inet_net_ntop.c,v 1.12 2001/12/08 11:47:04 lukem Exp $ */
/* $NetBSD: inet_net_ntop.c,v 1.13 2001/12/08 12:06:12 lukem Exp $ */
/*
* Copyright (c) 1996,1999 by Internet Software Consortium.
@ -20,9 +20,9 @@
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static const char rcsid[] = "Id: inet_net_ntop.c,v 8.2 1996/08/08 06:54:44 vixie Exp ";
static const char rcsid[] = "Id: inet_net_ntop.c,v 1.8 2001/09/27 15:08:36 marka Exp ";
#else
__RCSID("$NetBSD: inet_net_ntop.c,v 1.12 2001/12/08 11:47:04 lukem Exp $");
__RCSID("$NetBSD: inet_net_ntop.c,v 1.13 2001/12/08 12:06:12 lukem Exp $");
#endif
#endif
@ -49,6 +49,7 @@ __weak_alias(inet_net_ntop,_inet_net_ntop)
#endif
static char * inet_net_ntop_ipv4(const u_char *, int, char *, size_t);
static char * inet_net_ntop_ipv6(const u_char *, int, char *, size_t);
/*
* char *
@ -70,6 +71,8 @@ inet_net_ntop(int af, const void *src, int bits, char *dst, size_t 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);
@ -114,7 +117,7 @@ inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size)
/* 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++));
@ -128,7 +131,7 @@ inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t 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)
@ -139,7 +142,7 @@ inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size)
}
/* Format CIDR /width. */
if (size < sizeof "/32")
if (size <= sizeof "/32")
goto emsgsize;
dst += SPRINTF((dst, "/%u", bits));
return (odst);
@ -148,3 +151,135 @@ inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size)
errno = EMSGSIZE;
return (NULL);
}
/*
* 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
* 0x11110000 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;
_DIAGASSERT(src != NULL);
_DIAGASSERT(dst != NULL);
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);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: inet_net_pton.c,v 1.15 2001/12/01 04:43:24 lukem Exp $ */
/* $NetBSD: inet_net_pton.c,v 1.16 2001/12/08 12:06:12 lukem Exp $ */
/*
* Copyright (c) 1996,1999 by Internet Software Consortium.
@ -20,9 +20,9 @@
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static const char rcsid[] = "Id: inet_net_pton.c,v 8.3 1996/11/11 06:36:52 vixie Exp ";
static const char rcsid[] = "Id: inet_net_pton.c,v 1.13 2001/09/27 15:08:38 marka Exp ";
#else
__RCSID("$NetBSD: inet_net_pton.c,v 1.15 2001/12/01 04:43:24 lukem Exp $");
__RCSID("$NetBSD: inet_net_pton.c,v 1.16 2001/12/08 12:06:12 lukem Exp $");
#endif
#endif
@ -31,6 +31,7 @@ __RCSID("$NetBSD: inet_net_pton.c,v 1.15 2001/12/01 04:43:24 lukem Exp $");
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <assert.h>
#include <ctype.h>
@ -43,7 +44,10 @@ __RCSID("$NetBSD: inet_net_pton.c,v 1.15 2001/12/01 04:43:24 lukem Exp $");
__weak_alias(inet_net_pton,_inet_net_pton)
#endif
static int inet_net_pton_ipv4(const char *src, u_char *dst, size_t size);
static int inet_net_pton_ipv4(const char *, u_char *, size_t);
static int inet_net_pton_ipv6(const char *, u_char *, size_t);
static int getbits(const char *, int *);
static int getv4(const char *, u_char *, int *);
/*
* static int
@ -68,6 +72,8 @@ 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);
@ -104,7 +110,7 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
ch = (u_char) *src++;
if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
&& isascii(src[1]) && isxdigit((u_char) src[1])) {
&& isascii((u_char) src[1]) && isxdigit((u_char) src[1])) {
/* Hexadecimal: Eat nybble string. */
/* size is unsigned */
if (size == 0)
@ -122,11 +128,15 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
else
tmp = (tmp << 4) | n;
if (++dirty == 2) {
if (size-- == 0)
goto emsgsize;
*dst++ = (u_char) tmp;
dirty = 0;
}
}
if (dirty) { /* Odd trailing nybble? */
if (size-- == 0)
goto emsgsize;
*dst++ = (u_char) (tmp << 4);
}
} else if (isascii(ch) && isdigit(ch)) {
@ -157,7 +167,7 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
goto enoent;
bits = -1;
if (ch == '/' && isascii(src[0]) && isdigit((u_char) src[0])
if (ch == '/' && isascii((u_char) src[0]) && isdigit((u_char) src[0])
&& dst > odst) {
/* CIDR width specifier. Nothing can follow it. */
ch = (u_char) *src++; /* Skip over the /. */
@ -215,3 +225,201 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
errno = EMSGSIZE;
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[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;
_DIAGASSERT(src != NULL);
_DIAGASSERT(dst != NULL);
memset((tp = tmp), '\0', IN6ADDRSZ);
endp = tp + 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 + 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 + INADDRSZ) <= endp) &&
getv4(curtok, tp, &bits) > 0) {
tp += 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 + 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);
}