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