1/* $NetBSD: inet_pton.c,v 1.9 2007/07/23 11:45:52 lukem Exp $ */
2/* from	NetBSD: inet_pton.c,v 1.3 2006/09/26 05:59:18 lukem Exp */
3
4/*
5 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
6 * Copyright (c) 1996,1999 by Internet Software Consortium.
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
18 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21#include "tnftp.h"
22
23#if defined(HAVE_ARPA_NAMESER_H)
24# include <arpa/nameser.h>
25#endif
26#if !defined(NS_INADDRSZ)
27# define NS_INADDRSZ	4
28#endif
29#if !defined(NS_IN6ADDRSZ)
30# define NS_IN6ADDRSZ	16
31#endif
32#if !defined(NS_INT16SZ)
33# define NS_INT16SZ	2
34#endif
35
36/*
37 * WARNING: Don't even consider trying to compile this on a system where
38 * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
39 */
40
41static int	inet_pton4(const char *src, unsigned char *dst, int pton);
42#ifdef INET6
43static int	inet_pton6(const char *src, unsigned char *dst);
44#endif /* INET6 */
45
46/* int
47 * inet_pton(af, src, dst)
48 *	convert from presentation format (which usually means ASCII printable)
49 *	to network format (which is usually some kind of binary format).
50 * return:
51 *	1 if the address was valid for the specified address family
52 *	0 if the address wasn't valid (`dst' is untouched in this case)
53 *	-1 if some other error occurred (`dst' is untouched in this case, too)
54 * author:
55 *	Paul Vixie, 1996.
56 */
57int
58inet_pton(int af, const char *src, void *dst)
59{
60
61	switch (af) {
62	case AF_INET:
63		return (inet_pton4(src, dst, 1));
64#ifdef INET6
65	case AF_INET6:
66		return (inet_pton6(src, dst));
67#endif /* INET6 */
68	default:
69		errno = EAFNOSUPPORT;
70		return (-1);
71	}
72	/* NOTREACHED */
73}
74
75/* int
76 * inet_pton4(src, dst, pton)
77 *	when last arg is 0: inet_aton(). with hexadecimal, octal and shorthand.
78 *	when last arg is 1: inet_pton(). decimal dotted-quad only.
79 * return:
80 *	1 if `src' is a valid input, else 0.
81 * notice:
82 *	does not touch `dst' unless it's returning 1.
83 * author:
84 *	Paul Vixie, 1996.
85 */
86static int
87inet_pton4(const char *src, unsigned char *dst, int pton)
88{
89	uint32_t val;
90	unsigned int digit, base;
91	int n;
92	unsigned char c;
93	unsigned int parts[4];
94	register unsigned int *pp = parts;
95
96	c = *src;
97	for (;;) {
98		/*
99		 * Collect number up to ``.''.
100		 * Values are specified as for C:
101		 * 0x=hex, 0=octal, isdigit=decimal.
102		 */
103		if (!isdigit(c))
104			return (0);
105		val = 0; base = 10;
106		if (c == '0') {
107			c = *++src;
108			if (c == 'x' || c == 'X')
109				base = 16, c = *++src;
110			else if (isdigit(c) && c != '9')
111				base = 8;
112		}
113		/* inet_pton() takes decimal only */
114		if (pton && base != 10)
115			return (0);
116		for (;;) {
117			if (isdigit(c)) {
118				digit = c - '0';
119				if (digit >= base)
120					break;
121				val = (val * base) + digit;
122				c = *++src;
123			} else if (base == 16 && isxdigit(c)) {
124				digit = c + 10 - (islower(c) ? 'a' : 'A');
125				if (digit >= 16)
126					break;
127				val = (val << 4) | digit;
128				c = *++src;
129			} else
130				break;
131		}
132		if (c == '.') {
133			/*
134			 * Internet format:
135			 *	a.b.c.d
136			 *	a.b.c	(with c treated as 16 bits)
137			 *	a.b	(with b treated as 24 bits)
138			 *	a	(with a treated as 32 bits)
139			 */
140			if (pp >= parts + 3)
141				return (0);
142			*pp++ = val;
143			c = *++src;
144		} else
145			break;
146	}
147	/*
148	 * Check for trailing characters.
149	 */
150	if (c != '\0' && !isspace(c))
151		return (0);
152	/*
153	 * Concoct the address according to
154	 * the number of parts specified.
155	 */
156	n = pp - parts + 1;
157	/* inet_pton() takes dotted-quad only.  it does not take shorthand. */
158	if (pton && n != 4)
159		return (0);
160	switch (n) {
161
162	case 0:
163		return (0);		/* initial nondigit */
164
165	case 1:				/* a -- 32 bits */
166		break;
167
168	case 2:				/* a.b -- 8.24 bits */
169		if (parts[0] > 0xff || val > 0xffffff)
170			return (0);
171		val |= parts[0] << 24;
172		break;
173
174	case 3:				/* a.b.c -- 8.8.16 bits */
175		if ((parts[0] | parts[1]) > 0xff || val > 0xffff)
176			return (0);
177		val |= (parts[0] << 24) | (parts[1] << 16);
178		break;
179
180	case 4:				/* a.b.c.d -- 8.8.8.8 bits */
181		if ((parts[0] | parts[1] | parts[2] | val) > 0xff)
182			return (0);
183		val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
184		break;
185	}
186	if (dst) {
187		val = htonl(val);
188		memcpy(dst, &val, NS_INADDRSZ);
189	}
190	return (1);
191}
192
193#ifdef INET6
194/* int
195 * inet_pton6(src, dst)
196 *	convert presentation level address to network order binary form.
197 * return:
198 *	1 if `src' is a valid [RFC1884 2.2] address, else 0.
199 * notice:
200 *	(1) does not touch `dst' unless it's returning 1.
201 *	(2) :: in a full address is silently ignored.
202 * credit:
203 *	inspired by Mark Andrews.
204 * author:
205 *	Paul Vixie, 1996.
206 */
207static int
208inet_pton6(const char *src, unsigned char *dst)
209{
210	static const char xdigits_l[] = "0123456789abcdef",
211			  xdigits_u[] = "0123456789ABCDEF";
212	unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
213	const char *xdigits, *curtok;
214	int ch, saw_xdigit;
215	unsigned int val;
216
217	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
218	endp = tp + NS_IN6ADDRSZ;
219	colonp = NULL;
220	/* Leading :: requires some special handling. */
221	if (*src == ':')
222		if (*++src != ':')
223			return (0);
224	curtok = src;
225	saw_xdigit = 0;
226	val = 0;
227	while ((ch = *src++) != '\0') {
228		const char *pch;
229
230		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
231			pch = strchr((xdigits = xdigits_u), ch);
232		if (pch != NULL) {
233			val <<= 4;
234			val |= (pch - xdigits);
235			if (val > 0xffff)
236				return (0);
237			saw_xdigit = 1;
238			continue;
239		}
240		if (ch == ':') {
241			curtok = src;
242			if (!saw_xdigit) {
243				if (colonp)
244					return (0);
245				colonp = tp;
246				continue;
247			} else if (*src == '\0')
248				return (0);
249			if (tp + NS_INT16SZ > endp)
250				return (0);
251			*tp++ = (unsigned char) (val >> 8) & 0xff;
252			*tp++ = (unsigned char) val & 0xff;
253			saw_xdigit = 0;
254			val = 0;
255			continue;
256		}
257		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
258		    inet_pton4(curtok, tp, 1) > 0) {
259			tp += NS_INADDRSZ;
260			saw_xdigit = 0;
261			break;	/* '\0' was seen by inet_pton4(). */
262		}
263		return (0);
264	}
265	if (saw_xdigit) {
266		if (tp + NS_INT16SZ > endp)
267			return (0);
268		*tp++ = (unsigned char) (val >> 8) & 0xff;
269		*tp++ = (unsigned char) val & 0xff;
270	}
271	if (colonp != NULL) {
272		/*
273		 * Since some memmove()'s erroneously fail to handle
274		 * overlapping regions, we'll do the shift by hand.
275		 */
276		const int n = tp - colonp;
277		int i;
278
279		if (tp == endp)
280			return (0);
281		for (i = 1; i <= n; i++) {
282			endp[- i] = colonp[n - i];
283			colonp[n - i] = 0;
284		}
285		tp = endp;
286	}
287	if (tp != endp)
288		return (0);
289	memcpy(dst, tmp, NS_IN6ADDRSZ);
290	return (1);
291}
292#endif /* INET6 */
293