]> git.saurik.com Git - apple/libc.git/blob - net/inet_net_pton-fbsd.c
Libc-498.1.5.tar.gz
[apple/libc.git] / net / inet_net_pton-fbsd.c
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 #if defined(LIBC_SCCS) && !defined(lint)
19 static const char rcsid[] = "$Id: inet_net_pton.c,v 1.7.18.1 2005/04/27 05:00:53 sra Exp $";
20 #endif
21
22 /* the algorithms only can deal with ASCII, so we optimize for it */
23 #define USE_ASCII
24
25 #include <sys/cdefs.h>
26 __FBSDID("$FreeBSD: src/lib/libc/inet/inet_net_pton.c,v 1.3 2007/06/03 17:20:26 ume Exp $");
27
28 #include "port_before.h"
29
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 <assert.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42
43 #include "port_after.h"
44
45 #ifdef SPRINTF_CHAR
46 # define SPRINTF(x) strlen(sprintf/**/x)
47 #else
48 # define SPRINTF(x) ((size_t)sprintf x)
49 #endif
50
51 /*%
52 * static int
53 * inet_net_pton_ipv4(src, dst, size)
54 * convert IPv4 network number from presentation to network format.
55 * accepts hex octets, hex strings, decimal octets, and /CIDR.
56 * "size" is in bytes and describes "dst".
57 * return:
58 * number of bits, either imputed classfully or specified with /CIDR,
59 * or -1 if some failure occurred (check errno). ENOENT means it was
60 * not an IPv4 network specification.
61 * note:
62 * network byte order assumed. this means 192.5.5.240/28 has
63 * 0b11110000 in its fourth octet.
64 * author:
65 * Paul Vixie (ISC), June 1996
66 */
67 static int
68 inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) {
69 static const char xdigits[] = "0123456789abcdef";
70 static const char digits[] = "0123456789";
71 int n, ch, tmp = 0, dirty, bits;
72 const u_char *odst = dst;
73
74 ch = *src++;
75 if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
76 && isascii((unsigned char)(src[1]))
77 && isxdigit((unsigned char)(src[1]))) {
78 /* Hexadecimal: Eat nybble string. */
79 if (size <= 0U)
80 goto emsgsize;
81 dirty = 0;
82 src++; /*%< skip x or X. */
83 while ((ch = *src++) != '\0' && isascii(ch) && isxdigit(ch)) {
84 if (isupper(ch))
85 ch = tolower(ch);
86 n = strchr(xdigits, ch) - xdigits;
87 assert(n >= 0 && n <= 15);
88 if (dirty == 0)
89 tmp = n;
90 else
91 tmp = (tmp << 4) | n;
92 if (++dirty == 2) {
93 if (size-- <= 0U)
94 goto emsgsize;
95 *dst++ = (u_char) tmp;
96 dirty = 0;
97 }
98 }
99 if (dirty) { /*%< Odd trailing nybble? */
100 if (size-- <= 0U)
101 goto emsgsize;
102 *dst++ = (u_char) (tmp << 4);
103 }
104 } else if (isascii(ch) && isdigit(ch)) {
105 /* Decimal: eat dotted digit string. */
106 for (;;) {
107 tmp = 0;
108 do {
109 n = strchr(digits, ch) - digits;
110 assert(n >= 0 && n <= 9);
111 tmp *= 10;
112 tmp += n;
113 if (tmp > 255)
114 goto enoent;
115 } while ((ch = *src++) != '\0' &&
116 isascii(ch) && isdigit(ch));
117 if (size-- <= 0U)
118 goto emsgsize;
119 *dst++ = (u_char) tmp;
120 if (ch == '\0' || ch == '/')
121 break;
122 if (ch != '.')
123 goto enoent;
124 ch = *src++;
125 if (!isascii(ch) || !isdigit(ch))
126 goto enoent;
127 }
128 } else
129 goto enoent;
130
131 bits = -1;
132 if (ch == '/' && isascii((unsigned char)(src[0])) &&
133 isdigit((unsigned char)(src[0])) && dst > odst) {
134 /* CIDR width specifier. Nothing can follow it. */
135 ch = *src++; /*%< Skip over the /. */
136 bits = 0;
137 do {
138 n = strchr(digits, ch) - digits;
139 assert(n >= 0 && n <= 9);
140 bits *= 10;
141 bits += n;
142 if (bits > 32)
143 goto emsgsize;
144 } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch));
145 if (ch != '\0')
146 goto enoent;
147 }
148
149 /* Firey death and destruction unless we prefetched EOS. */
150 if (ch != '\0')
151 goto enoent;
152
153 /* If nothing was written to the destination, we found no address. */
154 if (dst == odst)
155 goto enoent;
156 /* If no CIDR spec was given, infer width from net class. */
157 if (bits == -1) {
158 if (*odst >= 240) /*%< Class E */
159 bits = 32;
160 else if (*odst >= 224) /*%< Class D */
161 bits = 8;
162 else if (*odst >= 192) /*%< Class C */
163 bits = 24;
164 else if (*odst >= 128) /*%< Class B */
165 bits = 16;
166 else /*%< Class A */
167 bits = 8;
168 /* If imputed mask is narrower than specified octets, widen. */
169 if (bits < ((dst - odst) * 8))
170 bits = (dst - odst) * 8;
171 /*
172 * If there are no additional bits specified for a class D
173 * address adjust bits to 4.
174 */
175 if (bits == 8 && *odst == 224)
176 bits = 4;
177 }
178 /* Extend network to cover the actual mask. */
179 while (bits > ((dst - odst) * 8)) {
180 if (size-- <= 0U)
181 goto emsgsize;
182 *dst++ = '\0';
183 }
184 return (bits);
185
186 enoent:
187 errno = ENOENT;
188 return (-1);
189
190 emsgsize:
191 errno = EMSGSIZE;
192 return (-1);
193 }
194
195 static int
196 getbits(const char *src, int *bitsp) {
197 static const char digits[] = "0123456789";
198 int n;
199 int val;
200 char ch;
201
202 val = 0;
203 n = 0;
204 while ((ch = *src++) != '\0') {
205 const char *pch;
206
207 pch = strchr(digits, ch);
208 if (pch != NULL) {
209 if (n++ != 0 && val == 0) /*%< no leading zeros */
210 return (0);
211 val *= 10;
212 val += (pch - digits);
213 if (val > 128) /*%< range */
214 return (0);
215 continue;
216 }
217 return (0);
218 }
219 if (n == 0)
220 return (0);
221 *bitsp = val;
222 return (1);
223 }
224
225 static int
226 getv4(const char *src, u_char *dst, int *bitsp) {
227 static const char digits[] = "0123456789";
228 u_char *odst = dst;
229 int n;
230 u_int val;
231 char ch;
232
233 val = 0;
234 n = 0;
235 while ((ch = *src++) != '\0') {
236 const char *pch;
237
238 pch = strchr(digits, ch);
239 if (pch != NULL) {
240 if (n++ != 0 && val == 0) /*%< no leading zeros */
241 return (0);
242 val *= 10;
243 val += (pch - digits);
244 if (val > 255) /*%< range */
245 return (0);
246 continue;
247 }
248 if (ch == '.' || ch == '/') {
249 if (dst - odst > 3) /*%< too many octets? */
250 return (0);
251 *dst++ = val;
252 if (ch == '/')
253 return (getbits(src, bitsp));
254 val = 0;
255 n = 0;
256 continue;
257 }
258 return (0);
259 }
260 if (n == 0)
261 return (0);
262 if (dst - odst > 3) /*%< too many octets? */
263 return (0);
264 *dst++ = val;
265 return (1);
266 }
267
268 static int
269 inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) {
270 static const char xdigits_l[] = "0123456789abcdef",
271 xdigits_u[] = "0123456789ABCDEF";
272 u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
273 const char *xdigits, *curtok;
274 int ch, saw_xdigit;
275 u_int val;
276 int digits;
277 int bits;
278 size_t bytes;
279 int words;
280 int ipv4;
281
282 memset((tp = tmp), '\0', NS_IN6ADDRSZ);
283 endp = tp + NS_IN6ADDRSZ;
284 colonp = NULL;
285 /* Leading :: requires some special handling. */
286 if (*src == ':')
287 if (*++src != ':')
288 goto enoent;
289 curtok = src;
290 saw_xdigit = 0;
291 val = 0;
292 digits = 0;
293 bits = -1;
294 ipv4 = 0;
295 while ((ch = *src++) != '\0') {
296 const char *pch;
297
298 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
299 pch = strchr((xdigits = xdigits_u), ch);
300 if (pch != NULL) {
301 val <<= 4;
302 val |= (pch - xdigits);
303 if (++digits > 4)
304 goto enoent;
305 saw_xdigit = 1;
306 continue;
307 }
308 if (ch == ':') {
309 curtok = src;
310 if (!saw_xdigit) {
311 if (colonp)
312 goto enoent;
313 colonp = tp;
314 continue;
315 } else if (*src == '\0')
316 goto enoent;
317 if (tp + NS_INT16SZ > endp)
318 return (0);
319 *tp++ = (u_char) (val >> 8) & 0xff;
320 *tp++ = (u_char) val & 0xff;
321 saw_xdigit = 0;
322 digits = 0;
323 val = 0;
324 continue;
325 }
326 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
327 getv4(curtok, tp, &bits) > 0) {
328 tp += NS_INADDRSZ;
329 saw_xdigit = 0;
330 ipv4 = 1;
331 break; /*%< '\\0' was seen by inet_pton4(). */
332 }
333 if (ch == '/' && getbits(src, &bits) > 0)
334 break;
335 goto enoent;
336 }
337 if (saw_xdigit) {
338 if (tp + NS_INT16SZ > endp)
339 goto enoent;
340 *tp++ = (u_char) (val >> 8) & 0xff;
341 *tp++ = (u_char) val & 0xff;
342 }
343 if (bits == -1)
344 bits = 128;
345
346 words = (bits + 15) / 16;
347 if (words < 2)
348 words = 2;
349 if (ipv4)
350 words = 8;
351 endp = tmp + 2 * words;
352
353 if (colonp != NULL) {
354 /*
355 * Since some memmove()'s erroneously fail to handle
356 * overlapping regions, we'll do the shift by hand.
357 */
358 const int n = tp - colonp;
359 int i;
360
361 if (tp == endp)
362 goto enoent;
363 for (i = 1; i <= n; i++) {
364 endp[- i] = colonp[n - i];
365 colonp[n - i] = 0;
366 }
367 tp = endp;
368 }
369 if (tp != endp)
370 goto enoent;
371
372 bytes = (bits + 7) / 8;
373 if (bytes > size)
374 goto emsgsize;
375 memcpy(dst, tmp, bytes);
376 return (bits);
377
378 enoent:
379 errno = ENOENT;
380 return (-1);
381
382 emsgsize:
383 errno = EMSGSIZE;
384 return (-1);
385 }
386
387 /*%
388 * int
389 * inet_net_pton(af, src, dst, size)
390 * convert network number from presentation to network format.
391 * accepts hex octets, hex strings, decimal octets, and /CIDR.
392 * "size" is in bytes and describes "dst".
393 * return:
394 * number of bits, either imputed classfully or specified with /CIDR,
395 * or -1 if some failure occurred (check errno). ENOENT means it was
396 * not a valid network specification.
397 * author:
398 * Paul Vixie (ISC), June 1996
399 */
400 int
401 inet_net_pton(int af, const char *src, void *dst, size_t size) {
402 switch (af) {
403 case AF_INET:
404 return (inet_net_pton_ipv4(src, dst, size));
405 case AF_INET6:
406 return (inet_net_pton_ipv6(src, dst, size));
407 default:
408 errno = EAFNOSUPPORT;
409 return (-1);
410 }
411 }
412
413 /*
414 * Weak aliases for applications that use certain private entry points,
415 * and fail to include <arpa/inet.h>.
416 */
417 #undef inet_net_pton
418 __weak_reference(__inet_net_pton, inet_net_pton);
419
420 /*! \file */