1156952Sume/*
2156952Sume * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3156952Sume * Copyright (c) 1996,1999 by Internet Software Consortium.
4156952Sume *
5156952Sume * Permission to use, copy, modify, and distribute this software for any
6156952Sume * purpose with or without fee is hereby granted, provided that the above
7156952Sume * copyright notice and this permission notice appear in all copies.
8156952Sume *
9156952Sume * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10156952Sume * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11156952Sume * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12156952Sume * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13156952Sume * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14156952Sume * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15156952Sume * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16156952Sume */
17156952Sume
18156952Sume#if defined(LIBC_SCCS) && !defined(lint)
19186090Sumestatic const char rcsid[] = "$Id: inet_net_pton.c,v 1.7.18.2 2008/08/26 04:42:43 marka Exp $";
20156952Sume#endif
21156956Sume#include <sys/cdefs.h>
22156956Sume__FBSDID("$FreeBSD$");
23156952Sume
24156952Sume#include "port_before.h"
25156952Sume
26156952Sume#include <sys/types.h>
27156952Sume#include <sys/socket.h>
28156952Sume#include <netinet/in.h>
29156952Sume#include <arpa/nameser.h>
30156952Sume#include <arpa/inet.h>
31156952Sume
32156956Sume#include <assert.h>
33156952Sume#include <ctype.h>
34156952Sume#include <errno.h>
35156952Sume#include <stdio.h>
36156952Sume#include <string.h>
37156952Sume#include <stdlib.h>
38156952Sume
39156952Sume#include "port_after.h"
40156952Sume
41156952Sume#ifdef SPRINTF_CHAR
42156952Sume# define SPRINTF(x) strlen(sprintf/**/x)
43156952Sume#else
44156952Sume# define SPRINTF(x) ((size_t)sprintf x)
45156952Sume#endif
46156952Sume
47170244Sume/*%
48156952Sume * static int
49156952Sume * inet_net_pton_ipv4(src, dst, size)
50156952Sume *	convert IPv4 network number from presentation to network format.
51156952Sume *	accepts hex octets, hex strings, decimal octets, and /CIDR.
52156952Sume *	"size" is in bytes and describes "dst".
53156952Sume * return:
54156952Sume *	number of bits, either imputed classfully or specified with /CIDR,
55156952Sume *	or -1 if some failure occurred (check errno).  ENOENT means it was
56156952Sume *	not an IPv4 network specification.
57156952Sume * note:
58156952Sume *	network byte order assumed.  this means 192.5.5.240/28 has
59156952Sume *	0b11110000 in its fourth octet.
60156952Sume * author:
61156952Sume *	Paul Vixie (ISC), June 1996
62156952Sume */
63156952Sumestatic int
64156952Sumeinet_net_pton_ipv4(const char *src, u_char *dst, size_t size) {
65156952Sume	static const char xdigits[] = "0123456789abcdef";
66156952Sume	static const char digits[] = "0123456789";
67156952Sume	int n, ch, tmp = 0, dirty, bits;
68156952Sume	const u_char *odst = dst;
69156952Sume
70156952Sume	ch = *src++;
71156952Sume	if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
72156952Sume	    && isascii((unsigned char)(src[1]))
73156952Sume	    && isxdigit((unsigned char)(src[1]))) {
74156952Sume		/* Hexadecimal: Eat nybble string. */
75156952Sume		if (size <= 0U)
76156952Sume			goto emsgsize;
77156952Sume		dirty = 0;
78170244Sume		src++;	/*%< skip x or X. */
79156952Sume		while ((ch = *src++) != '\0' && isascii(ch) && isxdigit(ch)) {
80156952Sume			if (isupper(ch))
81156952Sume				ch = tolower(ch);
82156952Sume			n = strchr(xdigits, ch) - xdigits;
83156956Sume			assert(n >= 0 && n <= 15);
84156952Sume			if (dirty == 0)
85156952Sume				tmp = n;
86156952Sume			else
87156952Sume				tmp = (tmp << 4) | n;
88156952Sume			if (++dirty == 2) {
89156952Sume				if (size-- <= 0U)
90156952Sume					goto emsgsize;
91156952Sume				*dst++ = (u_char) tmp;
92156952Sume				dirty = 0;
93156952Sume			}
94156952Sume		}
95170244Sume		if (dirty) {  /*%< Odd trailing nybble? */
96156952Sume			if (size-- <= 0U)
97156952Sume				goto emsgsize;
98156952Sume			*dst++ = (u_char) (tmp << 4);
99156952Sume		}
100156952Sume	} else if (isascii(ch) && isdigit(ch)) {
101156952Sume		/* Decimal: eat dotted digit string. */
102156952Sume		for (;;) {
103156952Sume			tmp = 0;
104156952Sume			do {
105156952Sume				n = strchr(digits, ch) - digits;
106156956Sume				assert(n >= 0 && n <= 9);
107156952Sume				tmp *= 10;
108156952Sume				tmp += n;
109156952Sume				if (tmp > 255)
110156952Sume					goto enoent;
111156952Sume			} while ((ch = *src++) != '\0' &&
112156952Sume				 isascii(ch) && isdigit(ch));
113156952Sume			if (size-- <= 0U)
114156952Sume				goto emsgsize;
115156952Sume			*dst++ = (u_char) tmp;
116156952Sume			if (ch == '\0' || ch == '/')
117156952Sume				break;
118156952Sume			if (ch != '.')
119156952Sume				goto enoent;
120156952Sume			ch = *src++;
121156952Sume			if (!isascii(ch) || !isdigit(ch))
122156952Sume				goto enoent;
123156952Sume		}
124156952Sume	} else
125156952Sume		goto enoent;
126156952Sume
127156952Sume	bits = -1;
128156952Sume	if (ch == '/' && isascii((unsigned char)(src[0])) &&
129156952Sume	    isdigit((unsigned char)(src[0])) && dst > odst) {
130156952Sume		/* CIDR width specifier.  Nothing can follow it. */
131170244Sume		ch = *src++;	/*%< Skip over the /. */
132156952Sume		bits = 0;
133156952Sume		do {
134156952Sume			n = strchr(digits, ch) - digits;
135156956Sume			assert(n >= 0 && n <= 9);
136156952Sume			bits *= 10;
137156952Sume			bits += n;
138186090Sume			if (bits > 32)
139186090Sume				goto enoent;
140156952Sume		} while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch));
141156952Sume		if (ch != '\0')
142156952Sume			goto enoent;
143156952Sume	}
144156952Sume
145156952Sume	/* Firey death and destruction unless we prefetched EOS. */
146156952Sume	if (ch != '\0')
147156952Sume		goto enoent;
148156952Sume
149156952Sume	/* If nothing was written to the destination, we found no address. */
150156952Sume	if (dst == odst)
151156952Sume		goto enoent;
152156952Sume	/* If no CIDR spec was given, infer width from net class. */
153156952Sume	if (bits == -1) {
154170244Sume		if (*odst >= 240)	/*%< Class E */
155156952Sume			bits = 32;
156170244Sume		else if (*odst >= 224)	/*%< Class D */
157156952Sume			bits = 8;
158170244Sume		else if (*odst >= 192)	/*%< Class C */
159156952Sume			bits = 24;
160170244Sume		else if (*odst >= 128)	/*%< Class B */
161156952Sume			bits = 16;
162170244Sume		else			/*%< Class A */
163156952Sume			bits = 8;
164156952Sume		/* If imputed mask is narrower than specified octets, widen. */
165156952Sume		if (bits < ((dst - odst) * 8))
166156952Sume			bits = (dst - odst) * 8;
167156952Sume		/*
168156952Sume		 * If there are no additional bits specified for a class D
169156952Sume		 * address adjust bits to 4.
170156952Sume		 */
171156952Sume		if (bits == 8 && *odst == 224)
172156952Sume			bits = 4;
173156952Sume	}
174156952Sume	/* Extend network to cover the actual mask. */
175156952Sume	while (bits > ((dst - odst) * 8)) {
176156952Sume		if (size-- <= 0U)
177156952Sume			goto emsgsize;
178156952Sume		*dst++ = '\0';
179156952Sume	}
180156952Sume	return (bits);
181156952Sume
182156952Sume enoent:
183156952Sume	errno = ENOENT;
184156952Sume	return (-1);
185156952Sume
186156952Sume emsgsize:
187156952Sume	errno = EMSGSIZE;
188156952Sume	return (-1);
189156952Sume}
190156952Sume
191156952Sumestatic int
192156952Sumegetbits(const char *src, int *bitsp) {
193156952Sume	static const char digits[] = "0123456789";
194156952Sume	int n;
195156952Sume	int val;
196156952Sume	char ch;
197156952Sume
198156952Sume	val = 0;
199156952Sume	n = 0;
200156952Sume	while ((ch = *src++) != '\0') {
201156952Sume		const char *pch;
202156952Sume
203156952Sume		pch = strchr(digits, ch);
204156952Sume		if (pch != NULL) {
205170244Sume			if (n++ != 0 && val == 0)	/*%< no leading zeros */
206156952Sume				return (0);
207156952Sume			val *= 10;
208156952Sume			val += (pch - digits);
209170244Sume			if (val > 128)			/*%< range */
210156952Sume				return (0);
211156952Sume			continue;
212156952Sume		}
213156952Sume		return (0);
214156952Sume	}
215156952Sume	if (n == 0)
216156952Sume		return (0);
217156952Sume	*bitsp = val;
218156952Sume	return (1);
219156952Sume}
220156952Sume
221156952Sumestatic int
222156952Sumegetv4(const char *src, u_char *dst, int *bitsp) {
223156952Sume	static const char digits[] = "0123456789";
224156952Sume	u_char *odst = dst;
225156952Sume	int n;
226156952Sume	u_int val;
227156952Sume	char ch;
228156952Sume
229156952Sume	val = 0;
230156952Sume	n = 0;
231156952Sume	while ((ch = *src++) != '\0') {
232156952Sume		const char *pch;
233156952Sume
234156952Sume		pch = strchr(digits, ch);
235156952Sume		if (pch != NULL) {
236170244Sume			if (n++ != 0 && val == 0)	/*%< no leading zeros */
237156952Sume				return (0);
238156952Sume			val *= 10;
239156952Sume			val += (pch - digits);
240170244Sume			if (val > 255)			/*%< range */
241156952Sume				return (0);
242156952Sume			continue;
243156952Sume		}
244156952Sume		if (ch == '.' || ch == '/') {
245170244Sume			if (dst - odst > 3)		/*%< too many octets? */
246156952Sume				return (0);
247156952Sume			*dst++ = val;
248156952Sume			if (ch == '/')
249156952Sume				return (getbits(src, bitsp));
250156952Sume			val = 0;
251156952Sume			n = 0;
252156952Sume			continue;
253156952Sume		}
254156952Sume		return (0);
255156952Sume	}
256156952Sume	if (n == 0)
257156952Sume		return (0);
258170244Sume	if (dst - odst > 3)		/*%< too many octets? */
259156952Sume		return (0);
260156952Sume	*dst++ = val;
261156952Sume	return (1);
262156952Sume}
263156952Sume
264156952Sumestatic int
265156952Sumeinet_net_pton_ipv6(const char *src, u_char *dst, size_t size) {
266156952Sume	static const char xdigits_l[] = "0123456789abcdef",
267156952Sume			  xdigits_u[] = "0123456789ABCDEF";
268156952Sume	u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
269156952Sume	const char *xdigits, *curtok;
270156952Sume	int ch, saw_xdigit;
271156952Sume	u_int val;
272156952Sume	int digits;
273156952Sume	int bits;
274156952Sume	size_t bytes;
275156952Sume	int words;
276156952Sume	int ipv4;
277156952Sume
278156952Sume	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
279156952Sume	endp = tp + NS_IN6ADDRSZ;
280156952Sume	colonp = NULL;
281156952Sume	/* Leading :: requires some special handling. */
282156952Sume	if (*src == ':')
283156952Sume		if (*++src != ':')
284156952Sume			goto enoent;
285156952Sume	curtok = src;
286156952Sume	saw_xdigit = 0;
287156952Sume	val = 0;
288156952Sume	digits = 0;
289156952Sume	bits = -1;
290156952Sume	ipv4 = 0;
291156952Sume	while ((ch = *src++) != '\0') {
292156952Sume		const char *pch;
293156952Sume
294156952Sume		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
295156952Sume			pch = strchr((xdigits = xdigits_u), ch);
296156952Sume		if (pch != NULL) {
297156952Sume			val <<= 4;
298156952Sume			val |= (pch - xdigits);
299156952Sume			if (++digits > 4)
300156952Sume				goto enoent;
301156952Sume			saw_xdigit = 1;
302156952Sume			continue;
303156952Sume		}
304156952Sume		if (ch == ':') {
305156952Sume			curtok = src;
306156952Sume			if (!saw_xdigit) {
307156952Sume				if (colonp)
308156952Sume					goto enoent;
309156952Sume				colonp = tp;
310156952Sume				continue;
311156952Sume			} else if (*src == '\0')
312156952Sume				goto enoent;
313156952Sume			if (tp + NS_INT16SZ > endp)
314156952Sume				return (0);
315156952Sume			*tp++ = (u_char) (val >> 8) & 0xff;
316156952Sume			*tp++ = (u_char) val & 0xff;
317156952Sume			saw_xdigit = 0;
318156952Sume			digits = 0;
319156952Sume			val = 0;
320156952Sume			continue;
321156952Sume		}
322156952Sume		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
323156952Sume		     getv4(curtok, tp, &bits) > 0) {
324156952Sume			tp += NS_INADDRSZ;
325156952Sume			saw_xdigit = 0;
326156952Sume			ipv4 = 1;
327170244Sume			break;	/*%< '\\0' was seen by inet_pton4(). */
328156952Sume		}
329156952Sume		if (ch == '/' && getbits(src, &bits) > 0)
330156952Sume			break;
331156952Sume		goto enoent;
332156952Sume	}
333156952Sume	if (saw_xdigit) {
334156952Sume		if (tp + NS_INT16SZ > endp)
335156952Sume			goto enoent;
336156952Sume		*tp++ = (u_char) (val >> 8) & 0xff;
337156952Sume		*tp++ = (u_char) val & 0xff;
338156952Sume	}
339156952Sume	if (bits == -1)
340156952Sume		bits = 128;
341156952Sume
342156952Sume	words = (bits + 15) / 16;
343156952Sume	if (words < 2)
344156952Sume		words = 2;
345156952Sume	if (ipv4)
346156952Sume		words = 8;
347156952Sume	endp =  tmp + 2 * words;
348156952Sume
349156952Sume	if (colonp != NULL) {
350156952Sume		/*
351156952Sume		 * Since some memmove()'s erroneously fail to handle
352156952Sume		 * overlapping regions, we'll do the shift by hand.
353156952Sume		 */
354156952Sume		const int n = tp - colonp;
355156952Sume		int i;
356156952Sume
357156952Sume		if (tp == endp)
358156952Sume			goto enoent;
359156952Sume		for (i = 1; i <= n; i++) {
360156952Sume			endp[- i] = colonp[n - i];
361156952Sume			colonp[n - i] = 0;
362156952Sume		}
363156952Sume		tp = endp;
364156952Sume	}
365156952Sume	if (tp != endp)
366156952Sume		goto enoent;
367156952Sume
368156952Sume	bytes = (bits + 7) / 8;
369156952Sume	if (bytes > size)
370156952Sume		goto emsgsize;
371156952Sume	memcpy(dst, tmp, bytes);
372156952Sume	return (bits);
373156952Sume
374156952Sume enoent:
375156952Sume	errno = ENOENT;
376156952Sume	return (-1);
377156952Sume
378156952Sume emsgsize:
379156952Sume	errno = EMSGSIZE;
380156952Sume	return (-1);
381156952Sume}
382156952Sume
383170244Sume/*%
384156952Sume * int
385156952Sume * inet_net_pton(af, src, dst, size)
386156952Sume *	convert network number from presentation to network format.
387156952Sume *	accepts hex octets, hex strings, decimal octets, and /CIDR.
388156952Sume *	"size" is in bytes and describes "dst".
389156952Sume * return:
390156952Sume *	number of bits, either imputed classfully or specified with /CIDR,
391156952Sume *	or -1 if some failure occurred (check errno).  ENOENT means it was
392156952Sume *	not a valid network specification.
393156952Sume * author:
394156952Sume *	Paul Vixie (ISC), June 1996
395156952Sume */
396156952Sumeint
397156952Sumeinet_net_pton(int af, const char *src, void *dst, size_t size) {
398156952Sume	switch (af) {
399156952Sume	case AF_INET:
400156952Sume		return (inet_net_pton_ipv4(src, dst, size));
401156952Sume	case AF_INET6:
402156952Sume		return (inet_net_pton_ipv6(src, dst, size));
403156952Sume	default:
404156952Sume		errno = EAFNOSUPPORT;
405156952Sume		return (-1);
406156952Sume	}
407156952Sume}
408156956Sume
409156956Sume/*
410156956Sume * Weak aliases for applications that use certain private entry points,
411156956Sume * and fail to include <arpa/inet.h>.
412156956Sume */
413156956Sume#undef inet_net_pton
414156956Sume__weak_reference(__inet_net_pton, inet_net_pton);
415170244Sume
416170244Sume/*! \file */
417