Home | History | Annotate | Line # | Download | only in inet
      1 /*
      2  * Copyright (c) 1996,1999 by Internet Software Consortium.
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
      9  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
     10  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
     11  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
     13  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
     14  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     15  * SOFTWARE.
     16  */
     17 
     18 #include <sys/cdefs.h>
     19 #if defined(LIBC_SCCS) && !defined(lint)
     20 #if 0
     21 static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.1 2002/08/02 02:17:21 marka Exp ";
     22 #else
     23 __RCSID("$NetBSD: inet_net_pton.c,v 1.7 2024/05/12 23:58:18 msaitoh Exp $");
     24 #endif
     25 #endif
     26 
     27 #include "port_before.h"
     28 
     29 #include "namespace.h"
     30 #include <sys/types.h>
     31 #include <sys/socket.h>
     32 #include <netinet/in.h>
     33 #include <arpa/nameser.h>
     34 #include <arpa/inet.h>
     35 
     36 #include <isc/assertions.h>
     37 #include <stddef.h>
     38 #include <ctype.h>
     39 #include <errno.h>
     40 #include <stdio.h>
     41 #include <string.h>
     42 #include <stdlib.h>
     43 
     44 #include "port_after.h"
     45 
     46 #ifdef __weak_alias
     47 __weak_alias(inet_net_pton,_inet_net_pton)
     48 #endif
     49 
     50 /*
     51  * static int
     52  * inet_net_pton_ipv4(src, dst, size)
     53  *	convert IPv4 network number from presentation to network format.
     54  *	accepts hex octets, hex strings, decimal octets, and /CIDR.
     55  *	"size" is in bytes and describes "dst".
     56  * return:
     57  *	number of bits, either inputed classfully or specified with /CIDR,
     58  *	or -1 if some failure occurred (check errno).  ENOENT means it was
     59  *	not an IPv4 network specification.
     60  * note:
     61  *	network byte order assumed.  this means 192.5.5.240/28 has
     62  *	0b11110000 in its fourth octet.
     63  * author:
     64  *	Paul Vixie (ISC), June 1996
     65  */
     66 static int
     67 inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
     68 {
     69 	static const char xdigits[] = "0123456789abcdef";
     70 	static const char digits[] = "0123456789";
     71 	int ch, dirty, bits;
     72 	ptrdiff_t n, tmp;
     73 	const u_char *odst = dst;
     74 
     75 	tmp = 0;
     76 	ch = *src++;
     77 	if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
     78 	    && isascii((u_char)(src[1]))
     79 	    && isxdigit((u_char)(src[1]))) {
     80 		/* Hexadecimal: Eat nybble string. */
     81 		if (size == 0)
     82 			goto emsgsize;
     83 		dirty = 0;
     84 		src++;	/* skip x or X. */
     85 		while ((ch = *src++) != '\0' && isascii((u_char)ch)
     86 		    && isxdigit((u_char)ch)) {
     87 			if (isupper((u_char)ch))
     88 				ch = tolower((u_char)ch);
     89 			n = strchr(xdigits, ch) - xdigits;
     90 			INSIST(n >= 0 && n <= 15);
     91 			if (dirty == 0)
     92 				tmp = n;
     93 			else
     94 				tmp = (tmp << 4) | n;
     95 			if (++dirty == 2) {
     96 				if (size-- == 0)
     97 					goto emsgsize;
     98 				*dst++ = (u_char) tmp;
     99 				dirty = 0;
    100 			}
    101 		}
    102 		if (dirty) {  /* Odd trailing nybble? */
    103 			if (size-- == 0)
    104 				goto emsgsize;
    105 			*dst++ = (u_char) (tmp << 4);
    106 		}
    107 	} else if (isascii((u_char)ch) && isdigit((u_char)ch)) {
    108 		/* Decimal: eat dotted digit string. */
    109 		for (;;) {
    110 			tmp = 0;
    111 			do {
    112 				n = strchr(digits, ch) - digits;
    113 				INSIST(n >= 0 && n <= 9);
    114 				tmp *= 10;
    115 				tmp += n;
    116 				if (tmp > 255)
    117 					goto enoent;
    118 			} while ((ch = *src++) != '\0' &&
    119 				 isascii((u_char)ch) && isdigit((u_char)ch));
    120 			if (size-- == 0)
    121 				goto emsgsize;
    122 			*dst++ = (u_char) tmp;
    123 			if (ch == '\0' || ch == '/')
    124 				break;
    125 			if (ch != '.')
    126 				goto enoent;
    127 			ch = *src++;
    128 			if (!isascii((u_char)ch) || !isdigit((u_char)ch))
    129 				goto enoent;
    130 		}
    131 	} else
    132 		goto enoent;
    133 
    134 	bits = -1;
    135 	if (ch == '/' && isascii((u_char)(src[0])) &&
    136 	    isdigit((u_char)(src[0])) && dst > odst) {
    137 		/* CIDR width specifier.  Nothing can follow it. */
    138 		ch = *src++;	/* Skip over the /. */
    139 		bits = 0;
    140 		do {
    141 			n = strchr(digits, ch) - digits;
    142 			INSIST(n >= 0 && n <= 9);
    143 			bits *= 10;
    144 			bits += (int)n;
    145 			if (bits > 32)
    146 				goto emsgsize;
    147 		} while ((ch = *src++) != '\0' && isascii((u_char)ch)
    148 		    && isdigit((u_char)ch));
    149 		if (ch != '\0')
    150 			goto enoent;
    151 	}
    152 
    153 	/* Fiery death and destruction unless we prefetched EOS. */
    154 	if (ch != '\0')
    155 		goto enoent;
    156 
    157 	/* If nothing was written to the destination, we found no address. */
    158 	if (dst == odst)
    159 		goto enoent;
    160 	/* If no CIDR spec was given, infer width from net class. */
    161 	if (bits == -1) {
    162 		if (*odst >= 240)	/* Class E */
    163 			bits = 32;
    164 		else if (*odst >= 224)	/* Class D */
    165 			bits = 4;
    166 		else if (*odst >= 192)	/* Class C */
    167 			bits = 24;
    168 		else if (*odst >= 128)	/* Class B */
    169 			bits = 16;
    170 		else			/* Class A */
    171 			bits = 8;
    172 		/* If inputed mask is narrower than specified octets, widen. */
    173 		if (bits >= 8 && bits < ((dst - odst) * 8))
    174 			bits = (int)(dst - odst) * 8;
    175 	}
    176 	/* Extend network to cover the actual mask. */
    177 	while (bits > ((dst - odst) * 8)) {
    178 		if (size-- == 0)
    179 			goto emsgsize;
    180 		*dst++ = '\0';
    181 	}
    182 	return (bits);
    183 
    184  enoent:
    185 	errno = ENOENT;
    186 	return (-1);
    187 
    188  emsgsize:
    189 	errno = EMSGSIZE;
    190 	return (-1);
    191 }
    192 
    193 static int
    194 getbits(const char *src, int *bitsp)
    195 {
    196 	static const char digits[] = "0123456789";
    197 	int n;
    198 	int val;
    199 	char ch;
    200 
    201 	val = 0;
    202 	n = 0;
    203 	while ((ch = *src++) != '\0') {
    204 		const char *pch;
    205 
    206 		pch = strchr(digits, ch);
    207 		if (pch != NULL) {
    208 			if (n++ != 0 && val == 0)	/* no leading zeros */
    209 				return (0);
    210 			val *= 10;
    211 			val += (int)(pch - digits);
    212 			if (val > 128)			/* range */
    213 				return (0);
    214 			continue;
    215 		}
    216 		return (0);
    217 	}
    218 	if (n == 0)
    219 		return (0);
    220 	*bitsp = val;
    221 	return (1);
    222 }
    223 
    224 static int
    225 getv4(const char *src, u_char *dst, int *bitsp)
    226 {
    227 	static const char digits[] = "0123456789";
    228 	u_char *odst = dst;
    229 	int n;
    230 	u_int val;
    231 	char ch;
    232 
    233 	val = 0;
    234 	n = 0;
    235 	while ((ch = *src++) != '\0') {
    236 		const char *pch;
    237 
    238 		pch = strchr(digits, ch);
    239 		if (pch != NULL) {
    240 			if (n++ != 0 && val == 0)	/* no leading zeros */
    241 				return (0);
    242 			val *= 10;
    243 			val += (int)(pch - digits);
    244 			if (val > 255)			/* range */
    245 				return (0);
    246 			continue;
    247 		}
    248 		if (ch == '.' || ch == '/') {
    249 			if (dst - odst > 3)		/* too many octets? */
    250 				return (0);
    251 			*dst++ = val;
    252 			if (ch == '/')
    253 				return (getbits(src, bitsp));
    254 			val = 0;
    255 			n = 0;
    256 			continue;
    257 		}
    258 		return (0);
    259 	}
    260 	if (n == 0)
    261 		return (0);
    262 	if (dst - odst > 3)		/* too many octets? */
    263 		return (0);
    264 	*dst++ = val;
    265 	return (1);
    266 }
    267 
    268 static int
    269 inet_net_pton_ipv6(const char *src, u_char *dst, size_t size)
    270 {
    271 	static const char xdigits_l[] = "0123456789abcdef",
    272 			  xdigits_u[] = "0123456789ABCDEF";
    273 	u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
    274 	const char *xdigits, *curtok;
    275 	int ch, saw_xdigit;
    276 	u_int val;
    277 	int digits;
    278 	int bits;
    279 	size_t bytes;
    280 	int words;
    281 	int ipv4;
    282 
    283 	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
    284 	endp = tp + NS_IN6ADDRSZ;
    285 	colonp = NULL;
    286 	/* Leading :: requires some special handling. */
    287 	if (*src == ':')
    288 		if (*++src != ':')
    289 			goto enoent;
    290 	curtok = src;
    291 	saw_xdigit = 0;
    292 	val = 0;
    293 	digits = 0;
    294 	bits = -1;
    295 	ipv4 = 0;
    296 	while ((ch = *src++) != '\0') {
    297 		const char *pch;
    298 
    299 		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
    300 			pch = strchr((xdigits = xdigits_u), ch);
    301 		if (pch != NULL) {
    302 			val <<= 4;
    303 			val |= (int)(pch - xdigits);
    304 			if (++digits > 4)
    305 				goto enoent;
    306 			saw_xdigit = 1;
    307 			continue;
    308 		}
    309 		if (ch == ':') {
    310 			curtok = src;
    311 			if (!saw_xdigit) {
    312 				if (colonp)
    313 					goto enoent;
    314 				colonp = tp;
    315 				continue;
    316 			} else if (*src == '\0')
    317 				goto enoent;
    318 			if (tp + NS_INT16SZ > endp)
    319 				return (0);
    320 			*tp++ = (u_char) (val >> 8) & 0xff;
    321 			*tp++ = (u_char) val & 0xff;
    322 			saw_xdigit = 0;
    323 			digits = 0;
    324 			val = 0;
    325 			continue;
    326 		}
    327 		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
    328 		     getv4(curtok, tp, &bits) > 0) {
    329 			tp += NS_INADDRSZ;
    330 			saw_xdigit = 0;
    331 			ipv4 = 1;
    332 			break;	/* '\0' was seen by inet_pton4(). */
    333 		}
    334 		if (ch == '/' && getbits(src, &bits) > 0)
    335 			break;
    336 		goto enoent;
    337 	}
    338 	if (saw_xdigit) {
    339 		if (tp + NS_INT16SZ > endp)
    340 			goto enoent;
    341 		*tp++ = (u_char) (val >> 8) & 0xff;
    342 		*tp++ = (u_char) val & 0xff;
    343 	}
    344 	if (bits == -1)
    345 		bits = 128;
    346 
    347 	words = (bits + 15) / 16;
    348 	if (words < 2)
    349 		words = 2;
    350 	if (ipv4)
    351 		words = 8;
    352 	endp =  tmp + 2 * words;
    353 
    354 	if (colonp != NULL) {
    355 		/*
    356 		 * Since some memmove()'s erroneously fail to handle
    357 		 * overlapping regions, we'll do the shift by hand.
    358 		 */
    359 		const ptrdiff_t n = tp - colonp;
    360 		int i;
    361 
    362 		if (tp == endp)
    363 			goto enoent;
    364 		for (i = 1; i <= n; i++) {
    365 			endp[- i] = colonp[n - i];
    366 			colonp[n - i] = 0;
    367 		}
    368 		tp = endp;
    369 	}
    370 	if (tp != endp)
    371 		goto enoent;
    372 
    373 	bytes = (bits + 7) / 8;
    374 	if (bytes > size)
    375 		goto emsgsize;
    376 	memcpy(dst, tmp, bytes);
    377 	return (bits);
    378 
    379  enoent:
    380 	errno = ENOENT;
    381 	return (-1);
    382 
    383  emsgsize:
    384 	errno = EMSGSIZE;
    385 	return (-1);
    386 }
    387 
    388 /*
    389  * int
    390  * inet_net_pton(af, src, dst, size)
    391  *	convert network number from presentation to network format.
    392  *	accepts hex octets, hex strings, decimal octets, and /CIDR.
    393  *	"size" is in bytes and describes "dst".
    394  * return:
    395  *	number of bits, either inputed classfully or specified with /CIDR,
    396  *	or -1 if some failure occurred (check errno).  ENOENT means it was
    397  *	not a valid network specification.
    398  * author:
    399  *	Paul Vixie (ISC), June 1996
    400  */
    401 int
    402 inet_net_pton(int af, const char *src, void *dst, size_t size)
    403 {
    404 	switch (af) {
    405 	case AF_INET:
    406 		return (inet_net_pton_ipv4(src, dst, size));
    407 	case AF_INET6:
    408 		return (inet_net_pton_ipv6(src, dst, size));
    409 	default:
    410 		errno = EAFNOSUPPORT;
    411 		return (-1);
    412 	}
    413 }
    414