Message ID | Y8Z+w8qtwaeNR7jt@snel |
---|---|
State | New |
Headers | show |
Series | resolv: add IPv6 support to inet_net_pton() | expand |
Hi all, I hope you don't mind me drawing some attention to this patch: I'd love for this to be merged! :-) Kind regards, Job On Tue, Jan 17, 2023 at 11:56:03AM +0100, Job Snijders wrote: > Dear all, > > On Thu, Dec 22, 2022 at 06:54:58PM +0100, Job Snijders wrote: > > This changeset adds support to inet_net_pton() to convert IPv6 network > > numbers (IPv6 prefixes with CIDR notation) from presentation format to > > network format. > > > > The starting point of this changeset was OpenBSD's > > libc/net/inet_net_pton.c (r1.13) implementation of inet_net_pton_ipv6(). > > https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/net/inet_net_pton.c?annotate=1.13 > > The OpenBSD implementation was adapted to glibc as following: > > > > 1) Use strncpy() instead of strlcpy() > > 2) Use strtol() instead of strtonum() > > 3) Updated comments > > I'm resending the patch (no changes, other than a/ and b/ are now > prefixed to the referenced paths), to hopefully trigger the build robot. > > Kind regards, > > Job > > Signed-off: Job Snijders <job@fastly.com> > --- > resolv/inet_net_pton.c | 76 +++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 72 insertions(+), 4 deletions(-) > > diff --git a/resolv/inet_net_pton.c b/resolv/inet_net_pton.c > index aab9b7b582..163e76e1a5 100644 > --- a/resolv/inet_net_pton.c > +++ b/resolv/inet_net_pton.c > @@ -1,4 +1,6 @@ > /* > + * Copyright (c) 2022 Job Snijders <job@fastly.com> > + * Copyright (c) 2012 by Gilles Chehade <gilles@openbsd.org> > * Copyright (c) 1996,1999 by Internet Software Consortium. > * > * Permission to use, copy, modify, and distribute this software for any > @@ -35,13 +37,16 @@ > > static int inet_net_pton_ipv4 (const char *src, u_char *dst, > size_t size) __THROW; > +static int inet_net_pton_ipv6 (const char *src, u_char *dst, > + size_t size) __THROW; > > /* > - * static int > + * 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". > + * Convert network number from presentation format to network format. > + * If "af" is set to AF_INET, accept various formats like hex octets, > + * hex strings, or decimal octets. If "af" is set to AF_INET6, accept > + * IPv6 addresses. "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 > @@ -55,6 +60,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: > __set_errno (EAFNOSUPPORT); > return (-1); > @@ -196,3 +203,64 @@ inet_net_pton_ipv4 (const char *src, u_char *dst, size_t size) > __set_errno (EMSGSIZE); > return (-1); > } > + > + > +/* > + * Convert an IPv6 prefix from presentation format to network format. > + * Return the number of bits specified, or -1 as error (check errno). > + */ > +static int > +inet_net_pton_ipv6 (const char *src, u_char *dst, size_t size) > +{ > + struct in6_addr in6; > + int bits; > + long lbits; > + size_t bytes; > + char buf[INET6_ADDRSTRLEN + sizeof("/128")]; > + char *ep, *sep; > + > + strncpy(buf, src, sizeof(buf) - 1); > + buf[sizeof(buf) - 1] = '\0'; > + > + sep = strchr(buf, '/'); > + if (sep != NULL) > + *sep++ = '\0'; > + > + if (inet_pton(AF_INET6, buf, &in6) != 1) { > + __set_errno (ENOENT); > + return (-1); > + } > + > + if (sep == NULL) { > + bits = 128; > + goto out; > + } > + > + if (sep[0] == '\0' || !isascii(sep[0]) || !isdigit(sep[0])) { > + __set_errno (ENOENT); > + return (-1); > + } > + > + errno = 0; > + lbits = strtol(sep, &ep, 10); > + if (sep[0] == '\0' || *ep != '\0') { > + __set_errno (ENOENT); > + return (-1); > + } > + if ((errno == ERANGE && (lbits == LONG_MAX || lbits == LONG_MIN)) > + || (lbits > 128 || lbits < 0)) { > + __set_errno (EMSGSIZE); > + return (-1); > + } > + bits = lbits; > + > + out: > + bytes = (bits + 7) / 8; > + if (bytes > size) { > + __set_errno (EMSGSIZE); > + return (-1); > + } > + > + memcpy(dst, &in6.s6_addr, bytes); > + return (bits); > +}
diff --git a/resolv/inet_net_pton.c b/resolv/inet_net_pton.c index aab9b7b582..163e76e1a5 100644 --- a/resolv/inet_net_pton.c +++ b/resolv/inet_net_pton.c @@ -1,4 +1,6 @@ /* + * Copyright (c) 2022 Job Snijders <job@fastly.com> + * Copyright (c) 2012 by Gilles Chehade <gilles@openbsd.org> * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any @@ -35,13 +37,16 @@ static int inet_net_pton_ipv4 (const char *src, u_char *dst, size_t size) __THROW; +static int inet_net_pton_ipv6 (const char *src, u_char *dst, + size_t size) __THROW; /* - * static int + * 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". + * Convert network number from presentation format to network format. + * If "af" is set to AF_INET, accept various formats like hex octets, + * hex strings, or decimal octets. If "af" is set to AF_INET6, accept + * IPv6 addresses. "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 @@ -55,6 +60,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: __set_errno (EAFNOSUPPORT); return (-1); @@ -196,3 +203,64 @@ inet_net_pton_ipv4 (const char *src, u_char *dst, size_t size) __set_errno (EMSGSIZE); return (-1); } + + +/* + * Convert an IPv6 prefix from presentation format to network format. + * Return the number of bits specified, or -1 as error (check errno). + */ +static int +inet_net_pton_ipv6 (const char *src, u_char *dst, size_t size) +{ + struct in6_addr in6; + int bits; + long lbits; + size_t bytes; + char buf[INET6_ADDRSTRLEN + sizeof("/128")]; + char *ep, *sep; + + strncpy(buf, src, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + + sep = strchr(buf, '/'); + if (sep != NULL) + *sep++ = '\0'; + + if (inet_pton(AF_INET6, buf, &in6) != 1) { + __set_errno (ENOENT); + return (-1); + } + + if (sep == NULL) { + bits = 128; + goto out; + } + + if (sep[0] == '\0' || !isascii(sep[0]) || !isdigit(sep[0])) { + __set_errno (ENOENT); + return (-1); + } + + errno = 0; + lbits = strtol(sep, &ep, 10); + if (sep[0] == '\0' || *ep != '\0') { + __set_errno (ENOENT); + return (-1); + } + if ((errno == ERANGE && (lbits == LONG_MAX || lbits == LONG_MIN)) + || (lbits > 128 || lbits < 0)) { + __set_errno (EMSGSIZE); + return (-1); + } + bits = lbits; + + out: + bytes = (bits + 7) / 8; + if (bytes > size) { + __set_errno (EMSGSIZE); + return (-1); + } + + memcpy(dst, &in6.s6_addr, bytes); + return (bits); +}