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