13fa54233Schristos /*
23fa54233Schristos * Copyright (c) 1996,1999 by Internet Software Consortium.
33fa54233Schristos *
43fa54233Schristos * Permission to use, copy, modify, and distribute this software for any
53fa54233Schristos * purpose with or without fee is hereby granted, provided that the above
63fa54233Schristos * copyright notice and this permission notice appear in all copies.
73fa54233Schristos *
83fa54233Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
93fa54233Schristos * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
103fa54233Schristos * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
113fa54233Schristos * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
123fa54233Schristos * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
133fa54233Schristos * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
143fa54233Schristos * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
153fa54233Schristos * SOFTWARE.
163fa54233Schristos */
173fa54233Schristos
183fa54233Schristos #include <sys/cdefs.h>
193fa54233Schristos #if defined(LIBC_SCCS) && !defined(lint)
203fa54233Schristos #if 0
213fa54233Schristos static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.1 2002/08/02 02:17:21 marka Exp ";
223fa54233Schristos #else
23*6d9e6faaSmsaitoh __RCSID("$NetBSD: inet_net_pton.c,v 1.7 2024/05/12 23:58:18 msaitoh Exp $");
243fa54233Schristos #endif
253fa54233Schristos #endif
263fa54233Schristos
273fa54233Schristos #include "port_before.h"
283fa54233Schristos
293fa54233Schristos #include "namespace.h"
303fa54233Schristos #include <sys/types.h>
313fa54233Schristos #include <sys/socket.h>
323fa54233Schristos #include <netinet/in.h>
333fa54233Schristos #include <arpa/nameser.h>
343fa54233Schristos #include <arpa/inet.h>
353fa54233Schristos
363fa54233Schristos #include <isc/assertions.h>
37c5e820caSchristos #include <stddef.h>
383fa54233Schristos #include <ctype.h>
393fa54233Schristos #include <errno.h>
403fa54233Schristos #include <stdio.h>
413fa54233Schristos #include <string.h>
423fa54233Schristos #include <stdlib.h>
433fa54233Schristos
443fa54233Schristos #include "port_after.h"
453fa54233Schristos
463fa54233Schristos #ifdef __weak_alias
__weak_alias(inet_net_pton,_inet_net_pton)473fa54233Schristos __weak_alias(inet_net_pton,_inet_net_pton)
483fa54233Schristos #endif
493fa54233Schristos
503fa54233Schristos /*
513fa54233Schristos * static int
523fa54233Schristos * inet_net_pton_ipv4(src, dst, size)
533fa54233Schristos * convert IPv4 network number from presentation to network format.
543fa54233Schristos * accepts hex octets, hex strings, decimal octets, and /CIDR.
553fa54233Schristos * "size" is in bytes and describes "dst".
563fa54233Schristos * return:
5782bba4e9Sandvar * number of bits, either inputed classfully or specified with /CIDR,
583fa54233Schristos * or -1 if some failure occurred (check errno). ENOENT means it was
593fa54233Schristos * not an IPv4 network specification.
603fa54233Schristos * note:
613fa54233Schristos * network byte order assumed. this means 192.5.5.240/28 has
623fa54233Schristos * 0b11110000 in its fourth octet.
633fa54233Schristos * author:
643fa54233Schristos * Paul Vixie (ISC), June 1996
653fa54233Schristos */
663fa54233Schristos static int
671f043722Smatt inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
681f043722Smatt {
693fa54233Schristos static const char xdigits[] = "0123456789abcdef";
703fa54233Schristos static const char digits[] = "0123456789";
71c5e820caSchristos int ch, dirty, bits;
72c5e820caSchristos ptrdiff_t n, tmp;
733fa54233Schristos const u_char *odst = dst;
743fa54233Schristos
75c5e820caSchristos tmp = 0;
763fa54233Schristos ch = *src++;
773fa54233Schristos if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
783fa54233Schristos && isascii((u_char)(src[1]))
793fa54233Schristos && isxdigit((u_char)(src[1]))) {
803fa54233Schristos /* Hexadecimal: Eat nybble string. */
813fa54233Schristos if (size == 0)
823fa54233Schristos goto emsgsize;
833fa54233Schristos dirty = 0;
843fa54233Schristos src++; /* skip x or X. */
853fa54233Schristos while ((ch = *src++) != '\0' && isascii((u_char)ch)
863fa54233Schristos && isxdigit((u_char)ch)) {
873fa54233Schristos if (isupper((u_char)ch))
883fa54233Schristos ch = tolower((u_char)ch);
893fa54233Schristos n = strchr(xdigits, ch) - xdigits;
903fa54233Schristos INSIST(n >= 0 && n <= 15);
913fa54233Schristos if (dirty == 0)
923fa54233Schristos tmp = n;
933fa54233Schristos else
943fa54233Schristos tmp = (tmp << 4) | n;
953fa54233Schristos if (++dirty == 2) {
963fa54233Schristos if (size-- == 0)
973fa54233Schristos goto emsgsize;
983fa54233Schristos *dst++ = (u_char) tmp;
993fa54233Schristos dirty = 0;
1003fa54233Schristos }
1013fa54233Schristos }
1023fa54233Schristos if (dirty) { /* Odd trailing nybble? */
1033fa54233Schristos if (size-- == 0)
1043fa54233Schristos goto emsgsize;
1053fa54233Schristos *dst++ = (u_char) (tmp << 4);
1063fa54233Schristos }
1073fa54233Schristos } else if (isascii((u_char)ch) && isdigit((u_char)ch)) {
1083fa54233Schristos /* Decimal: eat dotted digit string. */
1093fa54233Schristos for (;;) {
1103fa54233Schristos tmp = 0;
1113fa54233Schristos do {
1123fa54233Schristos n = strchr(digits, ch) - digits;
1133fa54233Schristos INSIST(n >= 0 && n <= 9);
1143fa54233Schristos tmp *= 10;
1153fa54233Schristos tmp += n;
1163fa54233Schristos if (tmp > 255)
1173fa54233Schristos goto enoent;
1183fa54233Schristos } while ((ch = *src++) != '\0' &&
1193fa54233Schristos isascii((u_char)ch) && isdigit((u_char)ch));
1203fa54233Schristos if (size-- == 0)
1213fa54233Schristos goto emsgsize;
1223fa54233Schristos *dst++ = (u_char) tmp;
1233fa54233Schristos if (ch == '\0' || ch == '/')
1243fa54233Schristos break;
1253fa54233Schristos if (ch != '.')
1263fa54233Schristos goto enoent;
1273fa54233Schristos ch = *src++;
1283fa54233Schristos if (!isascii((u_char)ch) || !isdigit((u_char)ch))
1293fa54233Schristos goto enoent;
1303fa54233Schristos }
1313fa54233Schristos } else
1323fa54233Schristos goto enoent;
1333fa54233Schristos
1343fa54233Schristos bits = -1;
1353fa54233Schristos if (ch == '/' && isascii((u_char)(src[0])) &&
1363fa54233Schristos isdigit((u_char)(src[0])) && dst > odst) {
1373fa54233Schristos /* CIDR width specifier. Nothing can follow it. */
1383fa54233Schristos ch = *src++; /* Skip over the /. */
1393fa54233Schristos bits = 0;
1403fa54233Schristos do {
1413fa54233Schristos n = strchr(digits, ch) - digits;
1423fa54233Schristos INSIST(n >= 0 && n <= 9);
1433fa54233Schristos bits *= 10;
144c5e820caSchristos bits += (int)n;
145ce009e37Schristos if (bits > 32)
146ce009e37Schristos goto emsgsize;
1473fa54233Schristos } while ((ch = *src++) != '\0' && isascii((u_char)ch)
1483fa54233Schristos && isdigit((u_char)ch));
1493fa54233Schristos if (ch != '\0')
1503fa54233Schristos goto enoent;
1513fa54233Schristos }
1523fa54233Schristos
153*6d9e6faaSmsaitoh /* Fiery death and destruction unless we prefetched EOS. */
1543fa54233Schristos if (ch != '\0')
1553fa54233Schristos goto enoent;
1563fa54233Schristos
1573fa54233Schristos /* If nothing was written to the destination, we found no address. */
1583fa54233Schristos if (dst == odst)
1593fa54233Schristos goto enoent;
1603fa54233Schristos /* If no CIDR spec was given, infer width from net class. */
1613fa54233Schristos if (bits == -1) {
1623fa54233Schristos if (*odst >= 240) /* Class E */
1633fa54233Schristos bits = 32;
1643fa54233Schristos else if (*odst >= 224) /* Class D */
1653fa54233Schristos bits = 4;
1663fa54233Schristos else if (*odst >= 192) /* Class C */
1673fa54233Schristos bits = 24;
1683fa54233Schristos else if (*odst >= 128) /* Class B */
1693fa54233Schristos bits = 16;
1703fa54233Schristos else /* Class A */
1713fa54233Schristos bits = 8;
17282bba4e9Sandvar /* If inputed mask is narrower than specified octets, widen. */
1733fa54233Schristos if (bits >= 8 && bits < ((dst - odst) * 8))
174c5e820caSchristos bits = (int)(dst - odst) * 8;
1753fa54233Schristos }
1763fa54233Schristos /* Extend network to cover the actual mask. */
1773fa54233Schristos while (bits > ((dst - odst) * 8)) {
1783fa54233Schristos if (size-- == 0)
1793fa54233Schristos goto emsgsize;
1803fa54233Schristos *dst++ = '\0';
1813fa54233Schristos }
1823fa54233Schristos return (bits);
1833fa54233Schristos
1843fa54233Schristos enoent:
1853fa54233Schristos errno = ENOENT;
1863fa54233Schristos return (-1);
1873fa54233Schristos
1883fa54233Schristos emsgsize:
1893fa54233Schristos errno = EMSGSIZE;
1903fa54233Schristos return (-1);
1913fa54233Schristos }
1923fa54233Schristos
1933fa54233Schristos static int
getbits(const char * src,int * bitsp)1941f043722Smatt getbits(const char *src, int *bitsp)
1951f043722Smatt {
1963fa54233Schristos static const char digits[] = "0123456789";
1973fa54233Schristos int n;
1983fa54233Schristos int val;
1993fa54233Schristos char ch;
2003fa54233Schristos
2013fa54233Schristos val = 0;
2023fa54233Schristos n = 0;
2033fa54233Schristos while ((ch = *src++) != '\0') {
2043fa54233Schristos const char *pch;
2053fa54233Schristos
2063fa54233Schristos pch = strchr(digits, ch);
2073fa54233Schristos if (pch != NULL) {
2083fa54233Schristos if (n++ != 0 && val == 0) /* no leading zeros */
2093fa54233Schristos return (0);
2103fa54233Schristos val *= 10;
211c5e820caSchristos val += (int)(pch - digits);
2123fa54233Schristos if (val > 128) /* range */
2133fa54233Schristos return (0);
2143fa54233Schristos continue;
2153fa54233Schristos }
2163fa54233Schristos return (0);
2173fa54233Schristos }
2183fa54233Schristos if (n == 0)
2193fa54233Schristos return (0);
2203fa54233Schristos *bitsp = val;
2213fa54233Schristos return (1);
2223fa54233Schristos }
2233fa54233Schristos
2243fa54233Schristos static int
getv4(const char * src,u_char * dst,int * bitsp)2251f043722Smatt getv4(const char *src, u_char *dst, int *bitsp)
2261f043722Smatt {
2273fa54233Schristos static const char digits[] = "0123456789";
2283fa54233Schristos u_char *odst = dst;
2293fa54233Schristos int n;
2303fa54233Schristos u_int val;
2313fa54233Schristos char ch;
2323fa54233Schristos
2333fa54233Schristos val = 0;
2343fa54233Schristos n = 0;
2353fa54233Schristos while ((ch = *src++) != '\0') {
2363fa54233Schristos const char *pch;
2373fa54233Schristos
2383fa54233Schristos pch = strchr(digits, ch);
2393fa54233Schristos if (pch != NULL) {
2403fa54233Schristos if (n++ != 0 && val == 0) /* no leading zeros */
2413fa54233Schristos return (0);
2423fa54233Schristos val *= 10;
243c5e820caSchristos val += (int)(pch - digits);
2443fa54233Schristos if (val > 255) /* range */
2453fa54233Schristos return (0);
2463fa54233Schristos continue;
2473fa54233Schristos }
2483fa54233Schristos if (ch == '.' || ch == '/') {
2493fa54233Schristos if (dst - odst > 3) /* too many octets? */
2503fa54233Schristos return (0);
2513fa54233Schristos *dst++ = val;
2523fa54233Schristos if (ch == '/')
2533fa54233Schristos return (getbits(src, bitsp));
2543fa54233Schristos val = 0;
2553fa54233Schristos n = 0;
2563fa54233Schristos continue;
2573fa54233Schristos }
2583fa54233Schristos return (0);
2593fa54233Schristos }
2603fa54233Schristos if (n == 0)
2613fa54233Schristos return (0);
2623fa54233Schristos if (dst - odst > 3) /* too many octets? */
2633fa54233Schristos return (0);
2643fa54233Schristos *dst++ = val;
2653fa54233Schristos return (1);
2663fa54233Schristos }
2673fa54233Schristos
2683fa54233Schristos static int
inet_net_pton_ipv6(const char * src,u_char * dst,size_t size)2691f043722Smatt inet_net_pton_ipv6(const char *src, u_char *dst, size_t size)
2701f043722Smatt {
2713fa54233Schristos static const char xdigits_l[] = "0123456789abcdef",
2723fa54233Schristos xdigits_u[] = "0123456789ABCDEF";
2733fa54233Schristos u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
2743fa54233Schristos const char *xdigits, *curtok;
2753fa54233Schristos int ch, saw_xdigit;
2763fa54233Schristos u_int val;
2773fa54233Schristos int digits;
2783fa54233Schristos int bits;
2793fa54233Schristos size_t bytes;
2803fa54233Schristos int words;
2813fa54233Schristos int ipv4;
2823fa54233Schristos
2833fa54233Schristos memset((tp = tmp), '\0', NS_IN6ADDRSZ);
2843fa54233Schristos endp = tp + NS_IN6ADDRSZ;
2853fa54233Schristos colonp = NULL;
2863fa54233Schristos /* Leading :: requires some special handling. */
2873fa54233Schristos if (*src == ':')
2883fa54233Schristos if (*++src != ':')
2893fa54233Schristos goto enoent;
2903fa54233Schristos curtok = src;
2913fa54233Schristos saw_xdigit = 0;
2923fa54233Schristos val = 0;
2933fa54233Schristos digits = 0;
2943fa54233Schristos bits = -1;
2953fa54233Schristos ipv4 = 0;
2963fa54233Schristos while ((ch = *src++) != '\0') {
2973fa54233Schristos const char *pch;
2983fa54233Schristos
2993fa54233Schristos if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
3003fa54233Schristos pch = strchr((xdigits = xdigits_u), ch);
3013fa54233Schristos if (pch != NULL) {
3023fa54233Schristos val <<= 4;
303c5e820caSchristos val |= (int)(pch - xdigits);
3043fa54233Schristos if (++digits > 4)
3053fa54233Schristos goto enoent;
3063fa54233Schristos saw_xdigit = 1;
3073fa54233Schristos continue;
3083fa54233Schristos }
3093fa54233Schristos if (ch == ':') {
3103fa54233Schristos curtok = src;
3113fa54233Schristos if (!saw_xdigit) {
3123fa54233Schristos if (colonp)
3133fa54233Schristos goto enoent;
3143fa54233Schristos colonp = tp;
3153fa54233Schristos continue;
3163fa54233Schristos } else if (*src == '\0')
3173fa54233Schristos goto enoent;
3183fa54233Schristos if (tp + NS_INT16SZ > endp)
3193fa54233Schristos return (0);
3203fa54233Schristos *tp++ = (u_char) (val >> 8) & 0xff;
3213fa54233Schristos *tp++ = (u_char) val & 0xff;
3223fa54233Schristos saw_xdigit = 0;
3233fa54233Schristos digits = 0;
3243fa54233Schristos val = 0;
3253fa54233Schristos continue;
3263fa54233Schristos }
3273fa54233Schristos if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
3283fa54233Schristos getv4(curtok, tp, &bits) > 0) {
3293fa54233Schristos tp += NS_INADDRSZ;
3303fa54233Schristos saw_xdigit = 0;
3313fa54233Schristos ipv4 = 1;
3323fa54233Schristos break; /* '\0' was seen by inet_pton4(). */
3333fa54233Schristos }
3343fa54233Schristos if (ch == '/' && getbits(src, &bits) > 0)
3353fa54233Schristos break;
3363fa54233Schristos goto enoent;
3373fa54233Schristos }
3383fa54233Schristos if (saw_xdigit) {
3393fa54233Schristos if (tp + NS_INT16SZ > endp)
3403fa54233Schristos goto enoent;
3413fa54233Schristos *tp++ = (u_char) (val >> 8) & 0xff;
3423fa54233Schristos *tp++ = (u_char) val & 0xff;
3433fa54233Schristos }
3443fa54233Schristos if (bits == -1)
3453fa54233Schristos bits = 128;
3463fa54233Schristos
3473fa54233Schristos words = (bits + 15) / 16;
3483fa54233Schristos if (words < 2)
3493fa54233Schristos words = 2;
3503fa54233Schristos if (ipv4)
3513fa54233Schristos words = 8;
3523fa54233Schristos endp = tmp + 2 * words;
3533fa54233Schristos
3543fa54233Schristos if (colonp != NULL) {
3553fa54233Schristos /*
3563fa54233Schristos * Since some memmove()'s erroneously fail to handle
3573fa54233Schristos * overlapping regions, we'll do the shift by hand.
3583fa54233Schristos */
359c5e820caSchristos const ptrdiff_t n = tp - colonp;
3603fa54233Schristos int i;
3613fa54233Schristos
3623fa54233Schristos if (tp == endp)
3633fa54233Schristos goto enoent;
3643fa54233Schristos for (i = 1; i <= n; i++) {
3653fa54233Schristos endp[- i] = colonp[n - i];
3663fa54233Schristos colonp[n - i] = 0;
3673fa54233Schristos }
3683fa54233Schristos tp = endp;
3693fa54233Schristos }
3703fa54233Schristos if (tp != endp)
3713fa54233Schristos goto enoent;
3723fa54233Schristos
3733fa54233Schristos bytes = (bits + 7) / 8;
3743fa54233Schristos if (bytes > size)
3753fa54233Schristos goto emsgsize;
3763fa54233Schristos memcpy(dst, tmp, bytes);
3773fa54233Schristos return (bits);
3783fa54233Schristos
3793fa54233Schristos enoent:
3803fa54233Schristos errno = ENOENT;
3813fa54233Schristos return (-1);
3823fa54233Schristos
3833fa54233Schristos emsgsize:
3843fa54233Schristos errno = EMSGSIZE;
3853fa54233Schristos return (-1);
3863fa54233Schristos }
3873fa54233Schristos
3883fa54233Schristos /*
3893fa54233Schristos * int
3903fa54233Schristos * inet_net_pton(af, src, dst, size)
3913fa54233Schristos * convert network number from presentation to network format.
3923fa54233Schristos * accepts hex octets, hex strings, decimal octets, and /CIDR.
3933fa54233Schristos * "size" is in bytes and describes "dst".
3943fa54233Schristos * return:
39582bba4e9Sandvar * number of bits, either inputed classfully or specified with /CIDR,
3963fa54233Schristos * or -1 if some failure occurred (check errno). ENOENT means it was
3973fa54233Schristos * not a valid network specification.
3983fa54233Schristos * author:
3993fa54233Schristos * Paul Vixie (ISC), June 1996
4003fa54233Schristos */
4013fa54233Schristos int
inet_net_pton(int af,const char * src,void * dst,size_t size)4021f043722Smatt inet_net_pton(int af, const char *src, void *dst, size_t size)
4031f043722Smatt {
4043fa54233Schristos switch (af) {
4053fa54233Schristos case AF_INET:
4063fa54233Schristos return (inet_net_pton_ipv4(src, dst, size));
4073fa54233Schristos case AF_INET6:
4083fa54233Schristos return (inet_net_pton_ipv6(src, dst, size));
4093fa54233Schristos default:
4103fa54233Schristos errno = EAFNOSUPPORT;
4113fa54233Schristos return (-1);
4123fa54233Schristos }
4133fa54233Schristos }
414