xref: /netbsd-src/lib/libc/inet/inet_net_pton.c (revision 6d9e6faaad70238bb4a3a78da3177bef5e899a59)
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
__weak_alias(inet_net_pton,_inet_net_pton)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
getbits(const char * src,int * bitsp)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
getv4(const char * src,u_char * dst,int * bitsp)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
inet_net_pton_ipv6(const char * src,u_char * dst,size_t size)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
inet_net_pton(int af,const char * src,void * dst,size_t size)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