1/*	$KAME: inet_pton.c,v 1.5 2001/08/20 02:32:40 itojun Exp $	*/
2
3/* Copyright (c) 1996 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS
10 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
11 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
12 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
15 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
16 * SOFTWARE.
17 */
18
19#include <ldns/config.h>
20
21#include <string.h>
22#include <stdio.h>
23#include <errno.h>
24
25/*
26 * WARNING: Don't even consider trying to compile this on a system where
27 * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
28 */
29
30static int	inet_pton4 (const char *src, uint8_t *dst);
31static int	inet_pton6 (const char *src, uint8_t *dst);
32
33/*
34 *
35 * The definitions we might miss.
36 *
37 */
38#ifndef NS_INT16SZ
39#define	NS_INT16SZ	2
40#endif
41
42#ifndef NS_IN6ADDRSZ
43#define NS_IN6ADDRSZ 16
44#endif
45
46#ifndef NS_INADDRSZ
47#define NS_INADDRSZ 4
48#endif
49
50/* int
51 * inet_pton(af, src, dst)
52 *	convert from presentation format (which usually means ASCII printable)
53 *	to network format (which is usually some kind of binary format).
54 * return:
55 *	1 if the address was valid for the specified address family
56 *	0 if the address wasn't valid (`dst' is untouched in this case)
57 *	-1 if some other error occurred (`dst' is untouched in this case, too)
58 * author:
59 *	Paul Vixie, 1996.
60 */
61int
62inet_pton(af, src, dst)
63	int af;
64	const char *src;
65	void *dst;
66{
67	switch (af) {
68	case AF_INET:
69		return (inet_pton4(src, dst));
70	case AF_INET6:
71		return (inet_pton6(src, dst));
72	default:
73#ifdef EAFNOSUPPORT
74		errno = EAFNOSUPPORT;
75#else
76		errno = ENOSYS;
77#endif
78		return (-1);
79	}
80	/* NOTREACHED */
81}
82
83/* int
84 * inet_pton4(src, dst)
85 *	like inet_aton() but without all the hexadecimal and shorthand.
86 * return:
87 *	1 if `src' is a valid dotted quad, else 0.
88 * notice:
89 *	does not touch `dst' unless it's returning 1.
90 * author:
91 *	Paul Vixie, 1996.
92 */
93static int
94inet_pton4(src, dst)
95	const char *src;
96	uint8_t *dst;
97{
98	static const char digits[] = "0123456789";
99	int saw_digit, octets, ch;
100	uint8_t tmp[NS_INADDRSZ], *tp;
101
102	saw_digit = 0;
103	octets = 0;
104	*(tp = tmp) = 0;
105	while ((ch = *src++) != '\0') {
106		const char *pch;
107
108		if ((pch = strchr(digits, ch)) != NULL) {
109			uint32_t new = *tp * 10 + (pch - digits);
110
111			if (new > 255)
112				return (0);
113			*tp = new;
114			if (! saw_digit) {
115				if (++octets > 4)
116					return (0);
117				saw_digit = 1;
118			}
119		} else if (ch == '.' && saw_digit) {
120			if (octets == 4)
121				return (0);
122			*++tp = 0;
123			saw_digit = 0;
124		} else
125			return (0);
126	}
127	if (octets < 4)
128		return (0);
129
130	memcpy(dst, tmp, NS_INADDRSZ);
131	return (1);
132}
133
134/* int
135 * inet_pton6(src, dst)
136 *	convert presentation level address to network order binary form.
137 * return:
138 *	1 if `src' is a valid [RFC1884 2.2] address, else 0.
139 * notice:
140 *	(1) does not touch `dst' unless it's returning 1.
141 *	(2) :: in a full address is silently ignored.
142 * credit:
143 *	inspired by Mark Andrews.
144 * author:
145 *	Paul Vixie, 1996.
146 */
147static int
148inet_pton6(src, dst)
149	const char *src;
150	uint8_t *dst;
151{
152	static const char xdigits_l[] = "0123456789abcdef",
153			  xdigits_u[] = "0123456789ABCDEF";
154	uint8_t tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
155	const char *xdigits, *curtok;
156	int ch, saw_xdigit;
157	uint32_t val;
158
159	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
160	endp = tp + NS_IN6ADDRSZ;
161	colonp = NULL;
162	/* Leading :: requires some special handling. */
163	if (*src == ':')
164		if (*++src != ':')
165			return (0);
166	curtok = src;
167	saw_xdigit = 0;
168	val = 0;
169	while ((ch = *src++) != '\0') {
170		const char *pch;
171
172		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
173			pch = strchr((xdigits = xdigits_u), ch);
174		if (pch != NULL) {
175			val <<= 4;
176			val |= (pch - xdigits);
177			if (val > 0xffff)
178				return (0);
179			saw_xdigit = 1;
180			continue;
181		}
182		if (ch == ':') {
183			curtok = src;
184			if (!saw_xdigit) {
185				if (colonp)
186					return (0);
187				colonp = tp;
188				continue;
189			}
190			if (tp + NS_INT16SZ > endp)
191				return (0);
192			*tp++ = (uint8_t) (val >> 8) & 0xff;
193			*tp++ = (uint8_t) val & 0xff;
194			saw_xdigit = 0;
195			val = 0;
196			continue;
197		}
198		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
199		    inet_pton4(curtok, tp) > 0) {
200			tp += NS_INADDRSZ;
201			saw_xdigit = 0;
202			break;	/* '\0' was seen by inet_pton4(). */
203		}
204		return (0);
205	}
206	if (saw_xdigit) {
207		if (tp + NS_INT16SZ > endp)
208			return (0);
209		*tp++ = (uint8_t) (val >> 8) & 0xff;
210		*tp++ = (uint8_t) val & 0xff;
211	}
212	if (colonp != NULL) {
213		/*
214		 * Since some memmove()'s erroneously fail to handle
215		 * overlapping regions, we'll do the shift by hand.
216		 */
217		const int n = tp - colonp;
218		int i;
219
220		for (i = 1; i <= n; i++) {
221			endp[- i] = colonp[n - i];
222			colonp[n - i] = 0;
223		}
224		tp = endp;
225	}
226	if (tp != endp)
227		return (0);
228	memcpy(dst, tmp, NS_IN6ADDRSZ);
229	return (1);
230}
231