xref: /netbsd-src/external/bsd/tcpdump/dist/strtoaddr.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /*
2  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (c) 1996,1999 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 ISC DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/cdefs.h>
19 #ifndef lint
20 __RCSID("$NetBSD: strtoaddr.c,v 1.3 2017/02/05 04:05:05 spz Exp $");
21 #endif
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <netdissect-stdinc.h>
28 #include <stddef.h>
29 #include <string.h>
30 
31 #include "strtoaddr.h"
32 
33 #ifndef NS_INADDRSZ
34 #define NS_INADDRSZ	4	/* IPv4 T_A */
35 #endif
36 
37 #ifndef NS_IN6ADDRSZ
38 #define NS_IN6ADDRSZ	16	/* IPv6 T_AAAA */
39 #endif
40 
41 #ifndef NS_INT16SZ
42 #define NS_INT16SZ	2	/* #/bytes of data in a uint16_t */
43 #endif
44 
45 /*%
46  * WARNING: Don't even consider trying to compile this on a system where
47  * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
48  */
49 
50 #ifndef NS_IN6ADDRSZ
51 #define NS_IN6ADDRSZ   16   /* IPv6 T_AAAA */
52 #endif
53 
54 /* int
55  * strtoaddr(src, dst)
56  *	convert presentation level IPv4 address to network order binary form.
57  * return:
58  *	1 if `src' is a valid input, else 0.
59  * notice:
60  *	does not touch `dst' unless it's returning 1.
61  * author:
62  *	Paul Vixie, 1996.
63  */
64 int
65 strtoaddr(const char *src, void *dst)
66 {
67 	uint32_t val;
68 	u_int digit;
69 	ptrdiff_t n;
70 	unsigned char c;
71 	u_int parts[4];
72 	u_int *pp = parts;
73 
74 	c = *src;
75 	for (;;) {
76 		/*
77 		 * Collect number up to ``.''.
78 		 * Values are specified as for C:
79 		 * 0x=hex, 0=octal, isdigit=decimal.
80 		 */
81 		if (!isdigit(c))
82 			return (0);
83 		val = 0;
84 		if (c == '0') {
85 			c = *++src;
86 			if (c == 'x' || c == 'X')
87 				return (0);
88 			else if (isdigit(c) && c != '9')
89 				return (0);
90 		}
91 		for (;;) {
92 			if (isdigit(c)) {
93 				digit = c - '0';
94 				if (digit >= 10)
95 					break;
96 				val = (val * 10) + digit;
97 				c = *++src;
98 			} else
99 				break;
100 		}
101 		if (c == '.') {
102 			/*
103 			 * Internet format:
104 			 *	a.b.c.d
105 			 *	a.b.c	(with c treated as 16 bits)
106 			 *	a.b	(with b treated as 24 bits)
107 			 *	a	(with a treated as 32 bits)
108 			 */
109 			if (pp >= parts + 3)
110 				return (0);
111 			*pp++ = val;
112 			c = *++src;
113 		} else
114 			break;
115 	}
116 	/*
117 	 * Check for trailing characters.
118 	 */
119 	if (c != '\0' && !isspace(c))
120 		return (0);
121 	/*
122 	 * Find the number of parts specified.
123 	 * It must be 4; we only support dotted quads, we don't
124 	 * support shorthand.
125 	 */
126 	n = pp - parts + 1;
127 	if (n != 4)
128 		return (0);
129 	/*
130 	 * parts[0-2] were set to the first 3 parts of the address;
131 	 * val was set to the 4th part.
132 	 *
133 	 * Check if any part is bigger than 255.
134 	 */
135 	if ((parts[0] | parts[1] | parts[2] | val) > 0xff)
136 		return (0);
137 	/*
138 	 * Add the other three parts to val.
139 	 */
140 	val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
141 	if (dst) {
142 		val = htonl(val);
143 		memcpy(dst, &val, NS_INADDRSZ);
144 	}
145 	return (1);
146 }
147 
148 /* int
149  * strtoaddr6(src, dst)
150  *	convert presentation level IPv6 address to network order binary form.
151  * return:
152  *	1 if `src' is a valid [RFC1884 2.2] address, else 0.
153  * notice:
154  *	(1) does not touch `dst' unless it's returning 1.
155  *	(2) :: in a full address is silently ignored.
156  * credit:
157  *	inspired by Mark Andrews.
158  * author:
159  *	Paul Vixie, 1996.
160  */
161 int
162 strtoaddr6(const char *src, void *dst)
163 {
164 	static const char xdigits_l[] = "0123456789abcdef",
165 			  xdigits_u[] = "0123456789ABCDEF";
166 	u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
167 	const char *xdigits, *curtok;
168 	int ch, seen_xdigits;
169 	u_int val;
170 
171 	memset((tp = tmp), '\0', NS_IN6ADDRSZ);
172 	endp = tp + NS_IN6ADDRSZ;
173 	colonp = NULL;
174 	/* Leading :: requires some special handling. */
175 	if (*src == ':')
176 		if (*++src != ':')
177 			return (0);
178 	curtok = src;
179 	seen_xdigits = 0;
180 	val = 0;
181 	while ((ch = *src++) != '\0') {
182 		const char *pch;
183 
184 		if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
185 			pch = strchr((xdigits = xdigits_u), ch);
186 		if (pch != NULL) {
187 			val <<= 4;
188 			val |= (int)(pch - xdigits);
189 			if (++seen_xdigits > 4)
190 				return (0);
191 			continue;
192 		}
193 		if (ch == ':') {
194 			curtok = src;
195 			if (!seen_xdigits) {
196 				if (colonp)
197 					return (0);
198 				colonp = tp;
199 				continue;
200 			} else if (*src == '\0')
201 				return (0);
202 			if (tp + NS_INT16SZ > endp)
203 				return (0);
204 			*tp++ = (u_char) (val >> 8) & 0xff;
205 			*tp++ = (u_char) val & 0xff;
206 			seen_xdigits = 0;
207 			val = 0;
208 			continue;
209 		}
210 		if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
211 		    strtoaddr(curtok, tp) > 0) {
212 			tp += NS_INADDRSZ;
213 			seen_xdigits = 0;
214 			break;	/*%< '\\0' was seen by strtoaddr(). */
215 		}
216 		return (0);
217 	}
218 	if (seen_xdigits) {
219 		if (tp + NS_INT16SZ > endp)
220 			return (0);
221 		*tp++ = (u_char) (val >> 8) & 0xff;
222 		*tp++ = (u_char) val & 0xff;
223 	}
224 	if (colonp != NULL) {
225 		/*
226 		 * Since some memmove()'s erroneously fail to handle
227 		 * overlapping regions, we'll do the shift by hand.
228 		 */
229 		const ptrdiff_t n = tp - colonp;
230 		int i;
231 
232 		if (tp == endp)
233 			return (0);
234 		for (i = 1; i <= n; i++) {
235 			endp[- i] = colonp[n - i];
236 			colonp[n - i] = 0;
237 		}
238 		tp = endp;
239 	}
240 	if (tp != endp)
241 		return (0);
242 	memcpy(dst, tmp, NS_IN6ADDRSZ);
243 	return (1);
244 }
245