aboutsummaryrefslogtreecommitdiff
path: root/lib/libc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc')
-rw-r--r--lib/libc/inet/inet_net_ntop.c206
-rw-r--r--lib/libc/inet/inet_net_pton.c363
-rw-r--r--lib/libc/stdlib/realpath.312
-rw-r--r--lib/libc/stdlib/realpath.c14
-rw-r--r--lib/libc/stdtime/strptime.32
-rw-r--r--lib/libc/stdtime/strptime.c3
-rw-r--r--lib/libc/tests/gen/realpath2_test.c106
-rw-r--r--lib/libc/tests/net/Makefile2
-rw-r--r--lib/libc/tests/net/inet_net_test.cc333
9 files changed, 607 insertions, 434 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 <gilles@openbsd.org>
+ * 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 <gilles@openbsd.org>
+ * 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/stdlib/realpath.3 b/lib/libc/stdlib/realpath.3
index 065ba312c2ef..76f40249963b 100644
--- a/lib/libc/stdlib/realpath.3
+++ b/lib/libc/stdlib/realpath.3
@@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 11, 2012
+.Dd October 10, 2025
.Dt REALPATH 3
.Os
.Sh NAME
@@ -108,11 +108,11 @@ and
.Xr getcwd 3 .
.Sh SEE ALSO
.Xr getcwd 3
-.\" .Sh STANDARDS
-.\" The
-.\" .Fn realpath
-.\" function conforms to
-.\" .St -p1003.1-2001 .
+.Sh STANDARDS
+The
+.Fn realpath
+function conforms to
+.St -p1003.1-2001 .
.Sh HISTORY
The
.Fn realpath
diff --git a/lib/libc/stdlib/realpath.c b/lib/libc/stdlib/realpath.c
index 4c52b73319ab..18f29e95ee6b 100644
--- a/lib/libc/stdlib/realpath.c
+++ b/lib/libc/stdlib/realpath.c
@@ -49,7 +49,7 @@ realpath1(const char *path, char *resolved)
{
struct stat sb;
char *p, *q;
- size_t left_len, resolved_len, next_token_len;
+ size_t left_len, prev_len, resolved_len, next_token_len;
unsigned symlinks;
ssize_t slen;
char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
@@ -98,6 +98,7 @@ realpath1(const char *path, char *resolved)
left_len = 0;
}
+ prev_len = resolved_len;
if (resolved[resolved_len - 1] != '/') {
if (resolved_len + 1 >= PATH_MAX) {
errno = ENAMETOOLONG;
@@ -133,8 +134,17 @@ realpath1(const char *path, char *resolved)
errno = ENAMETOOLONG;
return (NULL);
}
- if (lstat(resolved, &sb) != 0)
+ if (lstat(resolved, &sb) != 0) {
+ /*
+ * EACCES means the parent directory is not
+ * readable, while ENOTDIR means the parent
+ * directory is not a directory. Rewind the path
+ * to correctly indicate where the error lies.
+ */
+ if (errno == EACCES || errno == ENOTDIR)
+ resolved[prev_len] = '\0';
return (NULL);
+ }
if (S_ISLNK(sb.st_mode)) {
if (symlinks++ > MAXSYMLINKS) {
errno = ELOOP;
diff --git a/lib/libc/stdtime/strptime.3 b/lib/libc/stdtime/strptime.3
index 7df73d2d080a..9456fa757b85 100644
--- a/lib/libc/stdtime/strptime.3
+++ b/lib/libc/stdtime/strptime.3
@@ -171,7 +171,7 @@ is taken as noon.
The
.Fa %Z
format specifier only accepts time zone abbreviations of the local time zone,
-or the value "GMT".
+and the values "GMT", "UTC", or "Z".
This limitation is because of ambiguity due to of the over loading of time
zone abbreviations.
One such example is
diff --git a/lib/libc/stdtime/strptime.c b/lib/libc/stdtime/strptime.c
index 5f1293c7a267..375e49146639 100644
--- a/lib/libc/stdtime/strptime.c
+++ b/lib/libc/stdtime/strptime.c
@@ -546,7 +546,8 @@ label:
zonestr[cp - buf] = '\0';
tzset();
if (0 == strcmp(zonestr, "GMT") ||
- 0 == strcmp(zonestr, "UTC")) {
+ 0 == strcmp(zonestr, "UTC") ||
+ 0 == strcmp(zonestr, "Z")) {
*GMTp = 1;
} else if (0 == strcmp(zonestr, tzname[0])) {
tm->tm_isdst = 0;
diff --git a/lib/libc/tests/gen/realpath2_test.c b/lib/libc/tests/gen/realpath2_test.c
index f89dd99cbb72..431df8721ae0 100644
--- a/lib/libc/tests/gen/realpath2_test.c
+++ b/lib/libc/tests/gen/realpath2_test.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2017 Jan Kokemüller
* All rights reserved.
+ * Copyright (c) 2025 Klara, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,6 +26,8 @@
*/
#include <sys/param.h>
+#include <sys/stat.h>
+
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@@ -34,6 +37,31 @@
#include <atf-c.h>
+ATF_TC(realpath_null);
+ATF_TC_HEAD(realpath_null, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test null input");
+}
+ATF_TC_BODY(realpath_null, tc)
+{
+ ATF_REQUIRE_ERRNO(EINVAL, realpath(NULL, NULL) == NULL);
+}
+
+ATF_TC(realpath_empty);
+ATF_TC_HEAD(realpath_empty, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test empty input");
+}
+ATF_TC_BODY(realpath_empty, tc)
+{
+ char resb[PATH_MAX] = "";
+
+ ATF_REQUIRE_EQ(0, mkdir("foo", 0755));
+ ATF_REQUIRE_EQ(0, chdir("foo"));
+ ATF_REQUIRE_ERRNO(ENOENT, realpath("", resb) == NULL);
+ ATF_REQUIRE_STREQ("", resb);
+}
+
ATF_TC(realpath_buffer_overflow);
ATF_TC_HEAD(realpath_buffer_overflow, tc)
{
@@ -44,16 +72,11 @@ ATF_TC_HEAD(realpath_buffer_overflow, tc)
ATF_TC_BODY(realpath_buffer_overflow, tc)
{
- char path[MAXPATHLEN] = { 0 };
- char resb[MAXPATHLEN] = { 0 };
- size_t i;
+ char path[PATH_MAX] = "";
+ char resb[PATH_MAX] = "";
- path[0] = 'a';
+ memset(path, 'a', sizeof(path) - 1);
path[1] = '/';
- for (i = 2; i < sizeof(path) - 1; ++i) {
- path[i] = 'a';
- }
-
ATF_REQUIRE(realpath(path, resb) == NULL);
}
@@ -66,9 +89,9 @@ ATF_TC_HEAD(realpath_empty_symlink, tc)
ATF_TC_BODY(realpath_empty_symlink, tc)
{
- char path[MAXPATHLEN] = { 0 };
- char slnk[MAXPATHLEN] = { 0 };
- char resb[MAXPATHLEN] = { 0 };
+ char path[PATH_MAX] = "";
+ char slnk[PATH_MAX] = "";
+ char resb[PATH_MAX] = "";
int fd;
(void)strlcat(slnk, "empty_symlink", sizeof(slnk));
@@ -89,11 +112,70 @@ ATF_TC_BODY(realpath_empty_symlink, tc)
ATF_REQUIRE(unlink(slnk) == 0);
}
-ATF_TP_ADD_TCS(tp)
+ATF_TC(realpath_partial);
+ATF_TC_HEAD(realpath_partial, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test that failure leaves a partial result");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+
+ATF_TC_BODY(realpath_partial, tc)
{
+ char resb[PATH_MAX] = "";
+ size_t len;
+
+ /* scenario 1: missing directory */
+ ATF_REQUIRE_EQ(0, mkdir("foo", 0755));
+ ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 8 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/foo/bar", resb + len - 8);
+
+ /* scenario 2: dead link 1 */
+ ATF_REQUIRE_EQ(0, symlink("nix", "foo/bar"));
+ ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 8 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/foo/nix", resb + len - 8);
+
+ /* scenario 3: missing file */
+ ATF_REQUIRE_EQ(0, unlink("foo/bar"));
+ ATF_REQUIRE_EQ(0, mkdir("foo/bar", 0755));
+ ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 12 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/foo/bar/baz", resb + len - 12);
+
+ /* scenario 4: dead link 2 */
+ ATF_REQUIRE_EQ(0, symlink("nix", "foo/bar/baz"));
+ ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 12 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/foo/bar/nix", resb + len - 12);
+
+ /* scenario 5: unreadable directory */
+ ATF_REQUIRE_EQ(0, chmod("foo", 000));
+ ATF_REQUIRE_ERRNO(EACCES, realpath("foo/bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 4 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/foo", resb + len - 4);
+
+ /* scenario 6: not a directory */
+ ATF_REQUIRE_EQ(0, close(creat("bar", 0644)));
+ ATF_REQUIRE_ERRNO(ENOTDIR, realpath("bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 4 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/bar", resb + len - 4);
+}
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, realpath_null);
+ ATF_TP_ADD_TC(tp, realpath_empty);
ATF_TP_ADD_TC(tp, realpath_buffer_overflow);
ATF_TP_ADD_TC(tp, realpath_empty_symlink);
+ ATF_TP_ADD_TC(tp, realpath_partial);
return atf_no_error();
}
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 <ivy@FreeBSD.org>
+ *
+ * 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" },
+
+ // The same prefix but with the IPv4 legacy form as input.
+ { "::ffff:0.0.0.0/96", 96, "::ffff:0.0.0.0/96" },
+
+ // A prefix with an infix ::.
+ { "2001:db8::1/128", 128, "2001:db8::1/128" },
+
+ // A prefix with bits set which are outside the prefix;
+ // these should be silently ignored.
+ { "2001:db8:1:1:1:1:1:1/32", 32, "2001:db8::/32" },
+
+ // As above but with infix ::.
+ { "2001:db8::1/32", 32, "2001:db8::/32" },
+
+ // A prefix with only ::, commonly used to represent the
+ // entire address space.
+ { "::/0", 0, "::/0" },
+
+ // A single address with no ::.
+ { "2001:db8:1:1:1:1:1:1/128", 128, "2001:db8:1:1:1:1:1:1/128" },
+
+ // A prefix with no ::.
+ { "2001:db8:1:1:0:0:0:0/64", 64, "2001:db8:1:1::/64" },
+
+ // A prefix which isn't on a 16-bit boundary.
+ { "2001:db8:c000::/56", 56, "2001:db8:c000::/56" },
+
+ // A prefix which isn't on a nibble boundary.
+ { "2001:db8:c100::/57", 57, "2001:db8:c100::/57" },
+
+ // An address without a prefix length, which should be treated
+ // as a /128.
+ { "2001:db8::", 128, "2001:db8::/128" },
+ { "2001:db8::1", 128, "2001:db8::1/128" },
+
+ // Test vectors provided in PR bin/289198.
+ { "fe80::1/64", 64, "fe80::/64" },
+ { "fe80::f000:74ff:fe54:bed2/64",
+ 64, "fe80::/64" },
+ { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64",
+ 64, "ffff:ffff:ffff:ffff::/64" },
+ { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128,
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" },
+ };
+
+ for (auto const &addr: test_addrs) {
+ /*
+ * Convert the input string to an in6_addr + bits, and make
+ * sure the result produces the number of bits we expected.
+ */
+
+ auto in6 = in6_addr{};
+ errno = 0;
+ auto bits = inet_net_pton(AF_INET6, addr.input.c_str(),
+ &in6, sizeof(in6));
+ ATF_REQUIRE(bits != -1);
+ ATF_REQUIRE_EQ(bits, addr.bits);
+
+ /*
+ * Convert the in6_addr back to a string
+ */
+
+ /*
+ * XXX: Should there be a constant for the size of the result
+ * buffer? For now, use ADDRSTRLEN + 4 ("/128") + 1 (NUL).
+ *
+ * Fill the buffer with 'Z', so we can check the result was
+ * properly terminated.
+ */
+ auto strbuf = std::vector<char>(INET6_ADDRSTRLEN + 4 + 1, 'Z');
+ auto ret = inet_net_ntop(AF_INET6, &in6, 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);
+ }
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(inet_net_pton_invalid)
+ATF_TEST_CASE_BODY(inet_net_pton_invalid)
+{
+ auto ret = int{};
+ auto addr4 = in_addr{};
+ auto str4 = "10.0.0.0"s;
+ auto addr6 = in6_addr{};
+ auto str6 = "2001:db8::"s;
+
+ /* Passing an address which is too short should be an error */
+ ret = inet_net_pton(AF_INET6, str6.c_str(), &addr6, sizeof(addr6) - 1);
+ ATF_REQUIRE_EQ(ret, -1);
+
+ ret = inet_net_pton(AF_INET, str4.c_str(), &addr4, sizeof(addr4) - 1);
+ ATF_REQUIRE_EQ(ret, -1);
+
+ /* Test some generally invalid addresses. */
+ auto invalid4 = std::vector<std::string>{
+ // Prefix length too big
+ "10.0.0.0/33",
+ // Prefix length is negative
+ "10.0.0.0/-1",
+ // Prefix length is not a number
+ "10.0.0.0/foo",
+ // Input is not a network prefix
+ "this is not an IP address",
+ };
+
+ for (auto const &addr: invalid4) {
+ auto ret = inet_net_pton(AF_INET, addr.c_str(), &addr4,
+ sizeof(addr4));
+ ATF_REQUIRE_EQ(ret, -1);
+ }
+
+ auto invalid6 = std::vector<std::string>{
+ // Prefix length too big
+ "2001:db8::/129",
+ // Prefix length is negative
+ "2001:db8::/-1",
+ // Prefix length is not a number
+ "2001:db8::/foo",
+ // Input is not a network prefix
+ "this is not an IP address",
+ };
+
+ for (auto const &addr: invalid6) {
+ auto ret = inet_net_pton(AF_INET6, addr.c_str(), &addr6,
+ sizeof(addr6));
+ ATF_REQUIRE_EQ(ret, -1);
+ }
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(inet_net_ntop_invalid)
+ATF_TEST_CASE_BODY(inet_net_ntop_invalid)
+{
+ auto addr4 = in_addr{};
+ auto addr6 = in6_addr{};
+ auto strbuf = std::vector<char>(INET6_ADDRSTRLEN + 4 + 1);
+
+ /*
+ * Passing a buffer which is too small should not overrun the buffer.
+ * Test this by initialising the buffer to 'Z', and only providing
+ * part of it to the function.
+ */
+
+ std::ranges::fill(strbuf, 'Z');
+ auto ret = inet_net_ntop(AF_INET6, &addr6, 128, strbuf.data(), 1);
+ ATF_REQUIRE_EQ(ret, NULL);
+ ATF_REQUIRE_EQ(strbuf[1], 'Z');
+
+ std::ranges::fill(strbuf, 'Z');
+ ret = inet_net_ntop(AF_INET, &addr4, 32, strbuf.data(), 1);
+ ATF_REQUIRE_EQ(ret, NULL);
+ ATF_REQUIRE_EQ(strbuf[1], 'Z');
+
+ /* Check that invalid prefix lengths return an error */
+
+ ret = inet_net_ntop(AF_INET6, &addr6, 129, strbuf.data(), strbuf.size());
+ ATF_REQUIRE_EQ(ret, NULL);
+ ret = inet_net_ntop(AF_INET6, &addr6, -1, strbuf.data(), strbuf.size());
+ ATF_REQUIRE_EQ(ret, NULL);
+
+ ret = inet_net_ntop(AF_INET, &addr4, 33, strbuf.data(), strbuf.size());
+ ATF_REQUIRE_EQ(ret, NULL);
+ ret = inet_net_ntop(AF_INET, &addr4, -1, strbuf.data(), strbuf.size());
+ ATF_REQUIRE_EQ(ret, NULL);
+}
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, inet_net_inet4);
+ ATF_ADD_TEST_CASE(tcs, inet_net_inet6);
+ ATF_ADD_TEST_CASE(tcs, inet_net_pton_invalid);
+ ATF_ADD_TEST_CASE(tcs, inet_net_ntop_invalid);
+}