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