inet_net_pton.c revision 1.3 1 /*
2 * Copyright (c) 1996,1999 by Internet Software Consortium.
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
9 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
10 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
11 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
13 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15 * SOFTWARE.
16 */
17
18 #include <sys/cdefs.h>
19 #if defined(LIBC_SCCS) && !defined(lint)
20 #if 0
21 static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.1 2002/08/02 02:17:21 marka Exp ";
22 #else
23 __RCSID("$NetBSD: inet_net_pton.c,v 1.3 2012/03/13 21:13:38 christos Exp $");
24 #endif
25 #endif
26
27 #include "port_before.h"
28
29 #include "namespace.h"
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/nameser.h>
34 #include <arpa/inet.h>
35
36 #include <isc/assertions.h>
37 #include <stddef.h>
38 #include <ctype.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43
44 #include "port_after.h"
45
46 #ifdef __weak_alias
47 __weak_alias(inet_net_pton,_inet_net_pton)
48 #endif
49
50 #ifdef SPRINTF_CHAR
51 # define SPRINTF(x) strlen(sprintf/**/x)
52 #else
53 # define SPRINTF(x) ((size_t)sprintf x)
54 #endif
55
56
57 /*
58 * static int
59 * inet_net_pton_ipv4(src, dst, size)
60 * convert IPv4 network number from presentation to network format.
61 * accepts hex octets, hex strings, decimal octets, and /CIDR.
62 * "size" is in bytes and describes "dst".
63 * return:
64 * number of bits, either imputed classfully or specified with /CIDR,
65 * or -1 if some failure occurred (check errno). ENOENT means it was
66 * not an IPv4 network specification.
67 * note:
68 * network byte order assumed. this means 192.5.5.240/28 has
69 * 0b11110000 in its fourth octet.
70 * author:
71 * Paul Vixie (ISC), June 1996
72 */
73 static int
74 inet_net_pton_ipv4( const char *src, u_char *dst, size_t size) {
75 static const char xdigits[] = "0123456789abcdef";
76 static const char digits[] = "0123456789";
77 int ch, dirty, bits;
78 ptrdiff_t n, tmp;
79 const u_char *odst = dst;
80
81 tmp = 0;
82 ch = *src++;
83 if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
84 && isascii((u_char)(src[1]))
85 && isxdigit((u_char)(src[1]))) {
86 /* Hexadecimal: Eat nybble string. */
87 if (size == 0)
88 goto emsgsize;
89 dirty = 0;
90 src++; /* skip x or X. */
91 while ((ch = *src++) != '\0' && isascii((u_char)ch)
92 && isxdigit((u_char)ch)) {
93 if (isupper((u_char)ch))
94 ch = tolower((u_char)ch);
95 n = strchr(xdigits, ch) - xdigits;
96 INSIST(n >= 0 && n <= 15);
97 if (dirty == 0)
98 tmp = n;
99 else
100 tmp = (tmp << 4) | n;
101 if (++dirty == 2) {
102 if (size-- == 0)
103 goto emsgsize;
104 *dst++ = (u_char) tmp;
105 dirty = 0;
106 }
107 }
108 if (dirty) { /* Odd trailing nybble? */
109 if (size-- == 0)
110 goto emsgsize;
111 *dst++ = (u_char) (tmp << 4);
112 }
113 } else if (isascii((u_char)ch) && isdigit((u_char)ch)) {
114 /* Decimal: eat dotted digit string. */
115 for (;;) {
116 tmp = 0;
117 do {
118 n = strchr(digits, ch) - digits;
119 INSIST(n >= 0 && n <= 9);
120 tmp *= 10;
121 tmp += n;
122 if (tmp > 255)
123 goto enoent;
124 } while ((ch = *src++) != '\0' &&
125 isascii((u_char)ch) && isdigit((u_char)ch));
126 if (size-- == 0)
127 goto emsgsize;
128 *dst++ = (u_char) tmp;
129 if (ch == '\0' || ch == '/')
130 break;
131 if (ch != '.')
132 goto enoent;
133 ch = *src++;
134 if (!isascii((u_char)ch) || !isdigit((u_char)ch))
135 goto enoent;
136 }
137 } else
138 goto enoent;
139
140 bits = -1;
141 if (ch == '/' && isascii((u_char)(src[0])) &&
142 isdigit((u_char)(src[0])) && dst > odst) {
143 /* CIDR width specifier. Nothing can follow it. */
144 ch = *src++; /* Skip over the /. */
145 bits = 0;
146 do {
147 n = strchr(digits, ch) - digits;
148 INSIST(n >= 0 && n <= 9);
149 bits *= 10;
150 bits += (int)n;
151 if (bits > 32)
152 goto emsgsize;
153 } while ((ch = *src++) != '\0' && isascii((u_char)ch)
154 && isdigit((u_char)ch));
155 if (ch != '\0')
156 goto enoent;
157 }
158
159 /* Firey death and destruction unless we prefetched EOS. */
160 if (ch != '\0')
161 goto enoent;
162
163 /* If nothing was written to the destination, we found no address. */
164 if (dst == odst)
165 goto enoent;
166 /* If no CIDR spec was given, infer width from net class. */
167 if (bits == -1) {
168 if (*odst >= 240) /* Class E */
169 bits = 32;
170 else if (*odst >= 224) /* Class D */
171 bits = 4;
172 else if (*odst >= 192) /* Class C */
173 bits = 24;
174 else if (*odst >= 128) /* Class B */
175 bits = 16;
176 else /* Class A */
177 bits = 8;
178 /* If imputed mask is narrower than specified octets, widen. */
179 if (bits >= 8 && bits < ((dst - odst) * 8))
180 bits = (int)(dst - odst) * 8;
181 }
182 /* Extend network to cover the actual mask. */
183 while (bits > ((dst - odst) * 8)) {
184 if (size-- == 0)
185 goto emsgsize;
186 *dst++ = '\0';
187 }
188 return (bits);
189
190 enoent:
191 errno = ENOENT;
192 return (-1);
193
194 emsgsize:
195 errno = EMSGSIZE;
196 return (-1);
197 }
198
199 static int
200 getbits(const char *src, int *bitsp) {
201 static const char digits[] = "0123456789";
202 int n;
203 int val;
204 char ch;
205
206 val = 0;
207 n = 0;
208 while ((ch = *src++) != '\0') {
209 const char *pch;
210
211 pch = strchr(digits, ch);
212 if (pch != NULL) {
213 if (n++ != 0 && val == 0) /* no leading zeros */
214 return (0);
215 val *= 10;
216 val += (int)(pch - digits);
217 if (val > 128) /* range */
218 return (0);
219 continue;
220 }
221 return (0);
222 }
223 if (n == 0)
224 return (0);
225 *bitsp = val;
226 return (1);
227 }
228
229 static int
230 getv4(const char *src, u_char *dst, int *bitsp) {
231 static const char digits[] = "0123456789";
232 u_char *odst = dst;
233 int n;
234 u_int val;
235 char ch;
236
237 val = 0;
238 n = 0;
239 while ((ch = *src++) != '\0') {
240 const char *pch;
241
242 pch = strchr(digits, ch);
243 if (pch != NULL) {
244 if (n++ != 0 && val == 0) /* no leading zeros */
245 return (0);
246 val *= 10;
247 val += (int)(pch - digits);
248 if (val > 255) /* range */
249 return (0);
250 continue;
251 }
252 if (ch == '.' || ch == '/') {
253 if (dst - odst > 3) /* too many octets? */
254 return (0);
255 *dst++ = val;
256 if (ch == '/')
257 return (getbits(src, bitsp));
258 val = 0;
259 n = 0;
260 continue;
261 }
262 return (0);
263 }
264 if (n == 0)
265 return (0);
266 if (dst - odst > 3) /* too many octets? */
267 return (0);
268 *dst++ = val;
269 return (1);
270 }
271
272 static int
273 inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) {
274 static const char xdigits_l[] = "0123456789abcdef",
275 xdigits_u[] = "0123456789ABCDEF";
276 u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
277 const char *xdigits, *curtok;
278 int ch, saw_xdigit;
279 u_int val;
280 int digits;
281 int bits;
282 size_t bytes;
283 int words;
284 int ipv4;
285
286 memset((tp = tmp), '\0', NS_IN6ADDRSZ);
287 endp = tp + NS_IN6ADDRSZ;
288 colonp = NULL;
289 /* Leading :: requires some special handling. */
290 if (*src == ':')
291 if (*++src != ':')
292 goto enoent;
293 curtok = src;
294 saw_xdigit = 0;
295 val = 0;
296 digits = 0;
297 bits = -1;
298 ipv4 = 0;
299 while ((ch = *src++) != '\0') {
300 const char *pch;
301
302 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
303 pch = strchr((xdigits = xdigits_u), ch);
304 if (pch != NULL) {
305 val <<= 4;
306 val |= (int)(pch - xdigits);
307 if (++digits > 4)
308 goto enoent;
309 saw_xdigit = 1;
310 continue;
311 }
312 if (ch == ':') {
313 curtok = src;
314 if (!saw_xdigit) {
315 if (colonp)
316 goto enoent;
317 colonp = tp;
318 continue;
319 } else if (*src == '\0')
320 goto enoent;
321 if (tp + NS_INT16SZ > endp)
322 return (0);
323 *tp++ = (u_char) (val >> 8) & 0xff;
324 *tp++ = (u_char) val & 0xff;
325 saw_xdigit = 0;
326 digits = 0;
327 val = 0;
328 continue;
329 }
330 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
331 getv4(curtok, tp, &bits) > 0) {
332 tp += NS_INADDRSZ;
333 saw_xdigit = 0;
334 ipv4 = 1;
335 break; /* '\0' was seen by inet_pton4(). */
336 }
337 if (ch == '/' && getbits(src, &bits) > 0)
338 break;
339 goto enoent;
340 }
341 if (saw_xdigit) {
342 if (tp + NS_INT16SZ > endp)
343 goto enoent;
344 *tp++ = (u_char) (val >> 8) & 0xff;
345 *tp++ = (u_char) val & 0xff;
346 }
347 if (bits == -1)
348 bits = 128;
349
350 words = (bits + 15) / 16;
351 if (words < 2)
352 words = 2;
353 if (ipv4)
354 words = 8;
355 endp = tmp + 2 * words;
356
357 if (colonp != NULL) {
358 /*
359 * Since some memmove()'s erroneously fail to handle
360 * overlapping regions, we'll do the shift by hand.
361 */
362 const ptrdiff_t n = tp - colonp;
363 int i;
364
365 if (tp == endp)
366 goto enoent;
367 for (i = 1; i <= n; i++) {
368 endp[- i] = colonp[n - i];
369 colonp[n - i] = 0;
370 }
371 tp = endp;
372 }
373 if (tp != endp)
374 goto enoent;
375
376 bytes = (bits + 7) / 8;
377 if (bytes > size)
378 goto emsgsize;
379 memcpy(dst, tmp, bytes);
380 return (bits);
381
382 enoent:
383 errno = ENOENT;
384 return (-1);
385
386 emsgsize:
387 errno = EMSGSIZE;
388 return (-1);
389 }
390
391 /*
392 * int
393 * inet_net_pton(af, src, dst, size)
394 * convert network number from presentation to network format.
395 * accepts hex octets, hex strings, decimal octets, and /CIDR.
396 * "size" is in bytes and describes "dst".
397 * return:
398 * number of bits, either imputed classfully or specified with /CIDR,
399 * or -1 if some failure occurred (check errno). ENOENT means it was
400 * not a valid network specification.
401 * author:
402 * Paul Vixie (ISC), June 1996
403 */
404 int
405 inet_net_pton(int af, const char *src, void *dst, size_t size) {
406 switch (af) {
407 case AF_INET:
408 return (inet_net_pton_ipv4(src, dst, size));
409 case AF_INET6:
410 return (inet_net_pton_ipv6(src, dst, size));
411 default:
412 errno = EAFNOSUPPORT;
413 return (-1);
414 }
415 }
416