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