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