xref: /netbsd-src/lib/libc/inet/inet_net_pton.c (revision 6d9e6faaad70238bb4a3a78da3177bef5e899a59)
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