The branch main has been updated by ivy:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=8f4a0d2f7b96099001dbc51e06114df1a0e6d291

commit 8f4a0d2f7b96099001dbc51e06114df1a0e6d291
Author:     Lexi Winter <[email protected]>
AuthorDate: 2025-10-18 14:18:40 +0000
Commit:     Lexi Winter <[email protected]>
CommitDate: 2025-10-18 14:45:42 +0000

    libc: Import OpenBSD's inet_net_{ntop,pton}
    
    Our versions of these functions (originally taken from BIND) simply
    don't work correctly for AF_INET6.  These were removed from BIND itself
    quite a while ago, but OpenBSD has made several fixes in the mean time,
    so import their code.
    
    Add tests for both functions.
    
    PR:             289198
    Reported by:    Nico Sonack <[email protected]>
    MFC after:      1 week
    Reviewed by:    des
    Obtained from:  OpenBSD (lib/libc/net)
    Sponsored by:   https://www.patreon.com/bsdivy
    Differential Revision:  https://reviews.freebsd.org/D52629
---
 lib/libc/inet/inet_net_ntop.c       | 206 ++++++--------------
 lib/libc/inet/inet_net_pton.c       | 363 +++++++++++-------------------------
 lib/libc/tests/net/Makefile         |   2 +
 lib/libc/tests/net/inet_net_test.cc | 333 +++++++++++++++++++++++++++++++++
 4 files changed, 492 insertions(+), 412 deletions(-)

diff --git a/lib/libc/inet/inet_net_ntop.c b/lib/libc/inet/inet_net_ntop.c
index 9d98dbb5ca99..30dd5c0571f2 100644
--- a/lib/libc/inet/inet_net_ntop.c
+++ b/lib/libc/inet/inet_net_ntop.c
@@ -1,20 +1,23 @@
-/*-
- * SPDX-License-Identifier: ISC
+/*     $OpenBSD: inet_net_ntop.c,v 1.9 2019/07/03 03:24:04 deraadt Exp $       
*/
+
+/*
+ * Copyright (c) 2012 by Gilles Chehade <[email protected]>
+ * Copyright (c) 1996 by Internet Software Consortium.
  *
- * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996,1999 by Internet Software Consortium.
+ * SPDX-License-Identifier: ISC
  *
  * 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.
+ * 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.
  */
 
 #include "port_before.h"
@@ -31,18 +34,10 @@
 
 #include "port_after.h"
 
-#ifdef SPRINTF_CHAR
-# define SPRINTF(x) strlen(sprintf/**/x)
-#else
-# define SPRINTF(x) ((size_t)sprintf x)
-#endif
-
-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);
+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 *
  * inet_net_ntop(af, src, bits, dst, size)
  *     convert network number from network to presentation format.
@@ -66,7 +61,7 @@ inet_net_ntop(int af, const void *src, int bits, char *dst, 
size_t size)
        }
 }
 
-/*%
+/*
  * static char *
  * inet_net_ntop_ipv4(src, bits, dst, size)
  *     convert IPv4 network number from network to presentation format.
@@ -83,53 +78,63 @@ static char *
 inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size)
 {
        char *odst = dst;
-       char *t;
        u_int m;
        int b;
+       char *ep;
+       int advance;
+
+       ep = dst + size;
+       if (ep <= dst)
+               goto emsgsize;
 
        if (bits < 0 || bits > 32) {
                errno = EINVAL;
                return (NULL);
        }
-
        if (bits == 0) {
-               if (size < sizeof "0")
+               if (ep - dst < sizeof "0")
                        goto emsgsize;
                *dst++ = '0';
-               size--;
                *dst = '\0';
        }
 
        /* Format whole octets. */
        for (b = bits / 8; b > 0; b--) {
-               if (size <= sizeof "255.")
+               if (ep - dst < sizeof "255.")
+                       goto emsgsize;
+               advance = snprintf(dst, ep - dst, "%u", *src++);
+               if (advance <= 0 || advance >= ep - dst)
                        goto emsgsize;
-               t = dst;
-               dst += SPRINTF((dst, "%u", *src++));
+               dst += advance;
                if (b > 1) {
+                       if (dst + 1 >= ep)
+                               goto emsgsize;
                        *dst++ = '.';
                        *dst = '\0';
                }
-               size -= (size_t)(dst - t);
        }
 
        /* Format partial octet. */
        b = bits % 8;
        if (b > 0) {
-               if (size <= sizeof ".255")
+               if (ep - dst < sizeof ".255")
                        goto emsgsize;
-               t = dst;
                if (dst != odst)
                        *dst++ = '.';
                m = ((1 << b) - 1) << (8 - b);
-               dst += SPRINTF((dst, "%u", *src & m));
-               size -= (size_t)(dst - t);
+               advance = snprintf(dst, ep - dst, "%u", *src & m);
+               if (advance <= 0 || advance >= ep - dst)
+                       goto emsgsize;
+               dst += advance;
        }
 
        /* Format CIDR /width. */
-       if (size <= sizeof "/32")
+       if (ep - dst < sizeof "/32")
+               goto emsgsize;
+       advance = snprintf(dst, ep - dst, "/%u", bits);
+       if (advance <= 0 || advance >= ep - dst)
                goto emsgsize;
-       dst += SPRINTF((dst, "/%u", bits));
+       dst += advance;
        return (odst);
 
  emsgsize:
@@ -137,132 +142,27 @@ inet_net_ntop_ipv4(const u_char *src, int bits, char 
*dst, size_t size)
        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
- *     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;
+inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
+{
+       int     ret;
+       char    
buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")];
 
        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 (inet_ntop(AF_INET6, src, buf, size) == NULL)
+               return (NULL);
 
-                       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;
-                       }
-               }
+       ret = snprintf(dst, size, "%s/%d", buf, bits);
+       if (ret < 0 || ret >= size) {
+               errno = EMSGSIZE;
+               return (NULL);
        }
-       /* Format CIDR /width. */
-       sprintf(cp, "/%u", bits);
-       if (strlen(outbuf) + 1 > size)
-               goto emsgsize;
-       strcpy(dst, outbuf);
 
        return (dst);
-
-emsgsize:
-       errno = EMSGSIZE;
-       return (NULL);
 }
 
 /*
@@ -271,5 +171,3 @@ emsgsize:
  */
 #undef inet_net_ntop
 __weak_reference(__inet_net_ntop, inet_net_ntop);
-
-/*! \file */
diff --git a/lib/libc/inet/inet_net_pton.c b/lib/libc/inet/inet_net_pton.c
index d566a0e1d3c3..14c88eb72014 100644
--- a/lib/libc/inet/inet_net_pton.c
+++ b/lib/libc/inet/inet_net_pton.c
@@ -1,20 +1,23 @@
-/*-
- * SPDX-License-Identifier: ISC
+/*     $OpenBSD: inet_net_pton.c,v 1.14 2022/12/27 17:10:06 jmc Exp $  */
+
+/*
+ * Copyright (c) 2012 by Gilles Chehade <[email protected]>
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
  *
- * Copyright (C) 2004, 2005, 2008  Internet Systems Consortium, Inc. ("ISC")
- * Copyright (C) 1996, 1998, 1999, 2001, 2003  Internet Software Consortium.
+ * SPDX-License-Identifier: ISC
  *
- * Permission to use, copy, modify, and/or distribute this software for any
+ * 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.
+ * 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.
  */
 
 #include "port_before.h"
@@ -22,7 +25,6 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
-#include <arpa/nameser.h>
 #include <arpa/inet.h>
 
 #include <assert.h>
@@ -34,13 +36,37 @@
 
 #include "port_after.h"
 
-#ifdef SPRINTF_CHAR
-# define SPRINTF(x) strlen(sprintf/**/x)
-#else
-# define SPRINTF(x) ((size_t)sprintf x)
-#endif
+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
+ * 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);
+       }
+}
+
+/*
  * static int
  * inet_net_pton_ipv4(src, dst, size)
  *     convert IPv4 network number from presentation to network format.
@@ -57,22 +83,24 @@
  *     Paul Vixie (ISC), June 1996
  */
 static int
-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;
+inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
+{
+       static const char
+               xdigits[] = "0123456789abcdef",
+               digits[] = "0123456789";
+       int n, ch, tmp, dirty, bits;
        const u_char *odst = dst;
 
-       ch = *src++;
+       ch = (unsigned char)*src++;
        if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
-           && isascii((unsigned char)(src[1]))
-           && isxdigit((unsigned char)(src[1]))) {
+           && isascii((unsigned char)src[1]) && isxdigit((unsigned 
char)src[1])) {
                /* Hexadecimal: Eat nybble string. */
-               if (size <= 0U)
+               if (size == 0)
                        goto emsgsize;
-               dirty = 0;
-               src++;  /*%< skip x or X. */
-               while ((ch = *src++) != '\0' && isascii(ch) && isxdigit(ch)) {
+               tmp = 0, dirty = 0;
+               src++;  /* skip x or X. */
+               while ((ch = (unsigned char)*src++) != '\0' &&
+                   isascii(ch) && isxdigit(ch)) {
                        if (isupper(ch))
                                ch = tolower(ch);
                        n = strchr(xdigits, ch) - xdigits;
@@ -82,14 +110,14 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t 
size) {
                        else
                                tmp = (tmp << 4) | n;
                        if (++dirty == 2) {
-                               if (size-- <= 0U)
+                               if (size-- == 0)
                                        goto emsgsize;
                                *dst++ = (u_char) tmp;
                                dirty = 0;
                        }
                }
-               if (dirty) {  /*%< Odd trailing nybble? */
-                       if (size-- <= 0U)
+               if (dirty) {  /* Odd trailing nybble? */
+                       if (size-- == 0)
                                goto emsgsize;
                        *dst++ = (u_char) (tmp << 4);
                }
@@ -104,16 +132,16 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t 
size) {
                                tmp += n;
                                if (tmp > 255)
                                        goto enoent;
-                       } while ((ch = *src++) != '\0' &&
+                       } while ((ch = (unsigned char)*src++) != '\0' &&
                                 isascii(ch) && isdigit(ch));
-                       if (size-- <= 0U)
+                       if (size-- == 0)
                                goto emsgsize;
                        *dst++ = (u_char) tmp;
                        if (ch == '\0' || ch == '/')
                                break;
                        if (ch != '.')
                                goto enoent;
-                       ch = *src++;
+                       ch = (unsigned char)*src++;
                        if (!isascii(ch) || !isdigit(ch))
                                goto enoent;
                }
@@ -121,10 +149,10 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t 
size) {
                goto enoent;
 
        bits = -1;
-       if (ch == '/' && isascii((unsigned char)(src[0])) &&
-           isdigit((unsigned char)(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 /. */
+               ch = (unsigned char)*src++;     /* Skip over the /. */
                bits = 0;
                do {
                        n = strchr(digits, ch) - digits;
@@ -132,8 +160,9 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t 
size) {
                        bits *= 10;
                        bits += n;
                        if (bits > 32)
-                               goto enoent;
-               } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch));
+                               goto emsgsize;
+               } while ((ch = (unsigned char)*src++) != '\0' &&
+                        isascii(ch) && isdigit(ch));
                if (ch != '\0')
                        goto enoent;
        }
@@ -147,29 +176,23 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t 
size) {
                goto enoent;
        /* If no CIDR spec was given, infer width from net class. */
        if (bits == -1) {
-               if (*odst >= 240)       /*%< Class E */
+               if (*odst >= 240)       /* Class E */
                        bits = 32;
-               else if (*odst >= 224)  /*%< Class D */
-                       bits = 8;
-               else if (*odst >= 192)  /*%< Class C */
+               else if (*odst >= 224)  /* Class D */
+                       bits = 4;
+               else if (*odst >= 192)  /* Class C */
                        bits = 24;
-               else if (*odst >= 128)  /*%< Class B */
+               else if (*odst >= 128)  /* Class B */
                        bits = 16;
-               else                    /*%< Class A */
+               else                    /* Class A */
                        bits = 8;
                /* 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-- <= 0U)
+               if (size-- == 0)
                        goto emsgsize;
                *dst++ = '\0';
        }
@@ -184,222 +207,48 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t 
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;
+inet_net_pton_ipv6(const char *src, u_char *dst, size_t size)
+{
+       struct in6_addr  in6;
+       int              ret;
+       int              bits;
+       size_t           bytes;
+       char             buf[INET6_ADDRSTRLEN + sizeof("/128")];
+       char            *sep;
+       const char      *errstr;
 
-               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 (strlcpy(buf, src, sizeof buf) >= sizeof buf) {
+               errno = EMSGSIZE;
+               return (-1);
        }
-       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;
+       sep = strchr(buf, '/');
+       if (sep != NULL)
+               *sep++ = '\0';
 
-       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;
+       ret = inet_pton(AF_INET6, buf, &in6);
+       if (ret != 1)
+               return (-1);
 
-               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)
+       if (sep == NULL)
                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;
+       else {
+               bits = strtonum(sep, 0, 128, &errstr);
+               if (errstr) {
+                       errno = EINVAL;
+                       return (-1);
                }
-               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;
+       if (bytes > size) {
+               errno = EMSGSIZE;
                return (-1);
        }
+       memcpy(dst, &in6.s6_addr, bytes);
+       return (bits);
 }
 
 /*
@@ -408,5 +257,3 @@ inet_net_pton(int af, const char *src, void *dst, size_t 
size) {
  */
 #undef inet_net_pton
 __weak_reference(__inet_net_pton, inet_net_pton);
-
-/*! \file */
diff --git a/lib/libc/tests/net/Makefile b/lib/libc/tests/net/Makefile
index 24cff61e8d24..ec0668633508 100644
--- a/lib/libc/tests/net/Makefile
+++ b/lib/libc/tests/net/Makefile
@@ -4,8 +4,10 @@ ATF_TESTS_C+=  ether_test
 ATF_TESTS_C+=  eui64_aton_test
 ATF_TESTS_C+=  eui64_ntoa_test
 ATF_TESTS_CXX+=        link_addr_test
+ATF_TESTS_CXX+=        inet_net_test
 
 CXXSTD.link_addr_test= c++20
+CXXSTD.inet_net_test=  c++20
 
 CFLAGS+=       -I${.CURDIR}
 
diff --git a/lib/libc/tests/net/inet_net_test.cc 
b/lib/libc/tests/net/inet_net_test.cc
new file mode 100644
index 000000000000..4ecf5a3de492
--- /dev/null
+++ b/lib/libc/tests/net/inet_net_test.cc
@@ -0,0 +1,333 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2025 Lexi Winter <[email protected]>
+ *
+ * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/*
+ * Tests for inet_net_pton() and inet_net_ntop().
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ranges>
+#include <string>
+#include <vector>
+
+#include <atf-c++.hpp>
+
+using namespace std::literals;
+
+/*
+ * inet_net_ntop() and inet_net_pton() for IPv4.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(inet_net_inet4)
+ATF_TEST_CASE_BODY(inet_net_inet4)
+{
+       /*
+        * Define a list of addresses we want to check.  Each address is passed
+        * to inet_net_pton() to convert it to an in_addr, then we convert the
+        * in_addr back to a string and compare it with the expected value.  We
+        * want to test over-long prefixes here (such as 10.0.0.1/8), so we also
+        * specify what the result is expected to be.
+        */
+
+       struct test_addr {
+               std::string input;
+               unsigned bits;
+               std::string output;
+       };
+
+       auto test_addrs = std::vector<test_addr>{
+               // Simple prefixes that fall on octet boundaries.
+               { "10.0.0.0/8",         8,      "10/8" },
+               { "10.1.0.0/16",        16,     "10.1/16" },
+               { "10.1.2.0/24",        24,     "10.1.2/24" },
+               { "10.1.2.3/32",        32,     "10.1.2.3/32" },
+
+               // Simple prefixes with the short-form address.
+               { "10/8",               8,      "10/8" },
+               { "10.1/16",            16,     "10.1/16" },
+               { "10.1.2/24",          24,     "10.1.2/24" },
+
+               // A prefix that doesn't fall on an octet boundary.
+               { "10.1.64/18",         18,     "10.1.64/18" },
+
+               // An overlong prefix with bits that aren't part of the prefix.
+               { "10.0.0.1/8",         8,      "10/8" },
+       };
+
+       for (auto const &addr: test_addrs) {
+               /*
+                * Convert the input string to an in_addr + bits, and make
+                * sure the result produces the number of bits we expected.
+                */
+
+               auto in = in_addr{};
+               auto bits = inet_net_pton(AF_INET, addr.input.c_str(),
+                   &in, sizeof(in));
+               ATF_REQUIRE(bits != -1);
+               ATF_REQUIRE_EQ(bits, addr.bits);
+
+               /*
+                * Convert the in_addr back to a string
+                */
+
+               /*
+                * XXX: Should there be a constant for the size of the result
+                * buffer?  For now, use ADDRSTRLEN + 3 ("/32") + 1 (NUL).
+                *
+                * Fill the buffer with 'Z', so we can check the result was
+                * properly terminated.
+                */
+               auto strbuf = std::vector<char>(INET_ADDRSTRLEN + 3 + 1, 'Z');
+               auto ret = inet_net_ntop(AF_INET, &in, bits,
+                   strbuf.data(), strbuf.size());
+               ATF_REQUIRE(ret != NULL);
+               ATF_REQUIRE_EQ(ret, strbuf.data());
+
+               /* Make sure the result was NUL-terminated and find the NUL */
+               ATF_REQUIRE(strbuf.size() >= 1);
+               auto end = std::ranges::find(strbuf, '\0');
+               ATF_REQUIRE(end != strbuf.end());
+
+               /*
+                * Check the result matches what we expect.  Use a temporary
+                * string here instead of std::ranges::equal because this
+                * means ATF can print the mismatch.
+                */
+               auto str = std::string(std::ranges::begin(strbuf), end);
+               ATF_REQUIRE_EQ(str, addr.output);
+       }
+}
+
+/*
+ * inet_net_ntop() and inet_net_pton() for IPv6.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(inet_net_inet6)
+ATF_TEST_CASE_BODY(inet_net_inet6)
+{
+       /*
+        * Define a list of addresses we want to check.  Each address is
+        * passed to inet_net_pton() to convert it to an in6_addr, then we
+        * convert the in6_addr back to a string and compare it with the
+        * expected value.  We want to test over-long prefixes here (such
+        * as 2001:db8::1/32), so we also specify what the result is
+        * expected to be.
+        */
+
+       struct test_addr {
+               std::string input;
+               unsigned bits;
+               std::string output;
+       };
+
+       auto test_addrs = std::vector<test_addr>{
+               // A prefix with a trailing ::
+               { "2001:db8::/32",      32,     "2001:db8::/32" },
+
+               // A prefix with a leading ::.  Note that the output is
+               // different from the input because inet_ntop() renders
+               // this prefix with an IPv4 suffix for legacy reasons.
+               { "::ffff:0:0/96",      96,     "::ffff:0.0.0.0/96" },
+
*** 184 LINES SKIPPED ***

Reply via email to