xref: /netbsd-src/external/bsd/nsd/dist/compat/inet_pton.c (revision d83a80ee7fb31190352cf1f781441e06ca6a86db)
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 <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 
30 static int	inet_pton4 (const char *src, uint8_t *dst);
31 static 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  */
61 int
inet_pton(af,src,dst)62 inet_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 		errno = EAFNOSUPPORT;
74 		return (-1);
75 	}
76 	/* NOTREACHED */
77 }
78 
79 /* int
80  * inet_pton4(src, dst)
81  *	like inet_aton() but without all the hexadecimal and shorthand.
82  * return:
83  *	1 if `src' is a valid dotted quad, else 0.
84  * notice:
85  *	does not touch `dst' unless it's returning 1.
86  * author:
87  *	Paul Vixie, 1996.
88  */
89 static int
inet_pton4(src,dst)90 inet_pton4(src, dst)
91 	const char *src;
92 	uint8_t *dst;
93 {
94 	static const char digits[] = "0123456789";
95 	int saw_digit, octets, ch;
96 	uint8_t tmp[NS_INADDRSZ], *tp;
97 
98 	saw_digit = 0;
99 	octets = 0;
100 	*(tp = tmp) = 0;
101 	while ((ch = *src++) != '\0') {
102 		const char *pch;
103 
104 		if ((pch = strchr(digits, ch)) != NULL) {
105 			uint32_t new = *tp * 10 + (pch - digits);
106 
107 			if (new > 255)
108 				return (0);
109 			*tp = new;
110 			if (! saw_digit) {
111 				if (++octets > 4)
112 					return (0);
113 				saw_digit = 1;
114 			}
115 		} else if (ch == '.' && saw_digit) {
116 			if (octets == 4)
117 				return (0);
118 			*++tp = 0;
119 			saw_digit = 0;
120 		} else
121 			return (0);
122 	}
123 	if (octets < 4)
124 		return (0);
125 
126 	memcpy(dst, tmp, NS_INADDRSZ);
127 	return (1);
128 }
129 
130 /* int
131  * inet_pton6(src, dst)
132  *	convert presentation level address to network order binary form.
133  * return:
134  *	1 if `src' is a valid [RFC1884 2.2] address, else 0.
135  * notice:
136  *	(1) does not touch `dst' unless it's returning 1.
137  *	(2) :: in a full address is silently ignored.
138  * credit:
139  *	inspired by Mark Andrews.
140  * author:
141  *	Paul Vixie, 1996.
142  */
143 static int
inet_pton6(src,dst)144 inet_pton6(src, dst)
145 	const char *src;
146 	uint8_t *dst;
147 {
148 	static const char xdigits_l[] = "0123456789abcdef",
149 			  xdigits_u[] = "0123456789ABCDEF";
150 	uint8_t tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
151 	const char *xdigits, *curtok;
152 	int ch, saw_xdigit;
153 	uint32_t val;
154 
155 	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
156 	endp = tp + NS_IN6ADDRSZ;
157 	colonp = NULL;
158 	/* Leading :: requires some special handling. */
159 	if (*src == ':')
160 		if (*++src != ':')
161 			return (0);
162 	curtok = src;
163 	saw_xdigit = 0;
164 	val = 0;
165 	while ((ch = *src++) != '\0') {
166 		const char *pch;
167 
168 		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
169 			pch = strchr((xdigits = xdigits_u), ch);
170 		if (pch != NULL) {
171 			val <<= 4;
172 			val |= (pch - xdigits);
173 			if (val > 0xffff)
174 				return (0);
175 			saw_xdigit = 1;
176 			continue;
177 		}
178 		if (ch == ':') {
179 			curtok = src;
180 			if (!saw_xdigit) {
181 				if (colonp)
182 					return (0);
183 				colonp = tp;
184 				continue;
185 			}
186 			if (tp + NS_INT16SZ > endp)
187 				return (0);
188 			*tp++ = (uint8_t) (val >> 8) & 0xff;
189 			*tp++ = (uint8_t) val & 0xff;
190 			saw_xdigit = 0;
191 			val = 0;
192 			continue;
193 		}
194 		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
195 		    inet_pton4(curtok, tp) > 0) {
196 			tp += NS_INADDRSZ;
197 			saw_xdigit = 0;
198 			break;	/* '\0' was seen by inet_pton4(). */
199 		}
200 		return (0);
201 	}
202 	if (saw_xdigit) {
203 		if (tp + NS_INT16SZ > endp)
204 			return (0);
205 		*tp++ = (uint8_t) (val >> 8) & 0xff;
206 		*tp++ = (uint8_t) val & 0xff;
207 	}
208 	if (colonp != NULL) {
209 		/*
210 		 * Since some memmove()'s erroneously fail to handle
211 		 * overlapping regions, we'll do the shift by hand.
212 		 */
213 		const int n = tp - colonp;
214 		int i;
215 
216 		for (i = 1; i <= n; i++) {
217 			endp[- i] = colonp[n - i];
218 			colonp[n - i] = 0;
219 		}
220 		tp = endp;
221 	}
222 	if (tp != endp)
223 		return (0);
224 	memcpy(dst, tmp, NS_IN6ADDRSZ);
225 	return (1);
226 }
227