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
21static 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.1 2004/05/20 23:13:02 christos 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 <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 __weak_alias
46__weak_alias(inet_net_pton,_inet_net_pton)
47#endif
48
49#ifdef SPRINTF_CHAR
50# define SPRINTF(x) strlen(sprintf/**/x)
51#else
52# define SPRINTF(x) ((size_t)sprintf x)
53#endif
54
55
56/*
57 * static int
58 * inet_net_pton_ipv4(src, dst, size)
59 *	convert IPv4 network number from presentation to network format.
60 *	accepts hex octets, hex strings, decimal octets, and /CIDR.
61 *	"size" is in bytes and describes "dst".
62 * return:
63 *	number of bits, either imputed classfully or specified with /CIDR,
64 *	or -1 if some failure occurred (check errno).  ENOENT means it was
65 *	not an IPv4 network specification.
66 * note:
67 *	network byte order assumed.  this means 192.5.5.240/28 has
68 *	0b11110000 in its fourth octet.
69 * author:
70 *	Paul Vixie (ISC), June 1996
71 */
72static int
73inet_net_pton_ipv4( const char *src, u_char *dst, size_t size) {
74	static const char xdigits[] = "0123456789abcdef";
75	static const char digits[] = "0123456789";
76	int n, ch, tmp = 0, dirty, bits;
77	const u_char *odst = dst;
78
79	ch = *src++;
80	if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
81	    && isascii((u_char)(src[1]))
82	    && isxdigit((u_char)(src[1]))) {
83		/* Hexadecimal: Eat nybble string. */
84		if (size == 0)
85			goto emsgsize;
86		dirty = 0;
87		src++;	/* skip x or X. */
88		while ((ch = *src++) != '\0' && isascii((u_char)ch)
89		    && isxdigit((u_char)ch)) {
90			if (isupper((u_char)ch))
91				ch = tolower((u_char)ch);
92			n = strchr(xdigits, ch) - xdigits;
93			INSIST(n >= 0 && n <= 15);
94			if (dirty == 0)
95				tmp = n;
96			else
97				tmp = (tmp << 4) | n;
98			if (++dirty == 2) {
99				if (size-- == 0)
100					goto emsgsize;
101				*dst++ = (u_char) tmp;
102				dirty = 0;
103			}
104		}
105		if (dirty) {  /* Odd trailing nybble? */
106			if (size-- == 0)
107				goto emsgsize;
108			*dst++ = (u_char) (tmp << 4);
109		}
110	} else if (isascii((u_char)ch) && isdigit((u_char)ch)) {
111		/* Decimal: eat dotted digit string. */
112		for (;;) {
113			tmp = 0;
114			do {
115				n = strchr(digits, ch) - digits;
116				INSIST(n >= 0 && n <= 9);
117				tmp *= 10;
118				tmp += n;
119				if (tmp > 255)
120					goto enoent;
121			} while ((ch = *src++) != '\0' &&
122				 isascii((u_char)ch) && isdigit((u_char)ch));
123			if (size-- == 0)
124				goto emsgsize;
125			*dst++ = (u_char) tmp;
126			if (ch == '\0' || ch == '/')
127				break;
128			if (ch != '.')
129				goto enoent;
130			ch = *src++;
131			if (!isascii((u_char)ch) || !isdigit((u_char)ch))
132				goto enoent;
133		}
134	} else
135		goto enoent;
136
137	bits = -1;
138	if (ch == '/' && isascii((u_char)(src[0])) &&
139	    isdigit((u_char)(src[0])) && dst > odst) {
140		/* CIDR width specifier.  Nothing can follow it. */
141		ch = *src++;	/* Skip over the /. */
142		bits = 0;
143		do {
144			n = strchr(digits, ch) - digits;
145			INSIST(n >= 0 && n <= 9);
146			bits *= 10;
147			bits += n;
148			if (bits > 32)
149				goto emsgsize;
150		} while ((ch = *src++) != '\0' && isascii((u_char)ch)
151		    && isdigit((u_char)ch));
152		if (ch != '\0')
153			goto enoent;
154	}
155
156	/* Firey death and destruction unless we prefetched EOS. */
157	if (ch != '\0')
158		goto enoent;
159
160	/* If nothing was written to the destination, we found no address. */
161	if (dst == odst)
162		goto enoent;
163	/* If no CIDR spec was given, infer width from net class. */
164	if (bits == -1) {
165		if (*odst >= 240)	/* Class E */
166			bits = 32;
167		else if (*odst >= 224)	/* Class D */
168			bits = 4;
169		else if (*odst >= 192)	/* Class C */
170			bits = 24;
171		else if (*odst >= 128)	/* Class B */
172			bits = 16;
173		else			/* Class A */
174			bits = 8;
175		/* If imputed mask is narrower than specified octets, widen. */
176		if (bits >= 8 && bits < ((dst - odst) * 8))
177			bits = (dst - odst) * 8;
178	}
179	/* Extend network to cover the actual mask. */
180	while (bits > ((dst - odst) * 8)) {
181		if (size-- == 0)
182			goto emsgsize;
183		*dst++ = '\0';
184	}
185	return (bits);
186
187 enoent:
188	errno = ENOENT;
189	return (-1);
190
191 emsgsize:
192	errno = EMSGSIZE;
193	return (-1);
194}
195
196static int
197getbits(const char *src, int *bitsp) {
198	static const char digits[] = "0123456789";
199	int n;
200	int val;
201	char ch;
202
203	val = 0;
204	n = 0;
205	while ((ch = *src++) != '\0') {
206		const char *pch;
207
208		pch = strchr(digits, ch);
209		if (pch != NULL) {
210			if (n++ != 0 && val == 0)	/* no leading zeros */
211				return (0);
212			val *= 10;
213			val += (pch - digits);
214			if (val > 128)			/* range */
215				return (0);
216			continue;
217		}
218		return (0);
219	}
220	if (n == 0)
221		return (0);
222	*bitsp = val;
223	return (1);
224}
225
226static int
227getv4(const char *src, u_char *dst, int *bitsp) {
228	static const char digits[] = "0123456789";
229	u_char *odst = dst;
230	int n;
231	u_int val;
232	char ch;
233
234	val = 0;
235	n = 0;
236	while ((ch = *src++) != '\0') {
237		const char *pch;
238
239		pch = strchr(digits, ch);
240		if (pch != NULL) {
241			if (n++ != 0 && val == 0)	/* no leading zeros */
242				return (0);
243			val *= 10;
244			val += (pch - digits);
245			if (val > 255)			/* range */
246				return (0);
247			continue;
248		}
249		if (ch == '.' || ch == '/') {
250			if (dst - odst > 3)		/* too many octets? */
251				return (0);
252			*dst++ = val;
253			if (ch == '/')
254				return (getbits(src, bitsp));
255			val = 0;
256			n = 0;
257			continue;
258		}
259		return (0);
260	}
261	if (n == 0)
262		return (0);
263	if (dst - odst > 3)		/* too many octets? */
264		return (0);
265	*dst++ = val;
266	return (1);
267}
268
269static int
270inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) {
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 |= (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 int 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 imputed 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 */
401int
402inet_net_pton(int af, const char *src, void *dst, size_t size) {
403	switch (af) {
404	case AF_INET:
405		return (inet_net_pton_ipv4(src, dst, size));
406	case AF_INET6:
407		return (inet_net_pton_ipv6(src, dst, size));
408	default:
409		errno = EAFNOSUPPORT;
410		return (-1);
411	}
412}
413