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