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