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.7 2024/05/12 23:58:18 msaitoh 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
__weak_alias(inet_net_pton,_inet_net_pton)47 __weak_alias(inet_net_pton,_inet_net_pton)
48 #endif
49 
50 /*
51  * static int
52  * inet_net_pton_ipv4(src, dst, size)
53  *        convert IPv4 network number from presentation to network format.
54  *        accepts hex octets, hex strings, decimal octets, and /CIDR.
55  *        "size" is in bytes and describes "dst".
56  * return:
57  *        number of bits, either inputed classfully or specified with /CIDR,
58  *        or -1 if some failure occurred (check errno).  ENOENT means it was
59  *        not an IPv4 network specification.
60  * note:
61  *        network byte order assumed.  this means 192.5.5.240/28 has
62  *        0b11110000 in its fourth octet.
63  * author:
64  *        Paul Vixie (ISC), June 1996
65  */
66 static int
67 inet_net_pton_ipv4(const char *src, u_char *dst, size_t size)
68 {
69           static const char xdigits[] = "0123456789abcdef";
70           static const char digits[] = "0123456789";
71           int ch, dirty, bits;
72           ptrdiff_t n, tmp;
73           const u_char *odst = dst;
74 
75           tmp = 0;
76           ch = *src++;
77           if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
78               && isascii((u_char)(src[1]))
79               && isxdigit((u_char)(src[1]))) {
80                     /* Hexadecimal: Eat nybble string. */
81                     if (size == 0)
82                               goto emsgsize;
83                     dirty = 0;
84                     src++;    /* skip x or X. */
85                     while ((ch = *src++) != '\0' && isascii((u_char)ch)
86                         && isxdigit((u_char)ch)) {
87                               if (isupper((u_char)ch))
88                                         ch = tolower((u_char)ch);
89                               n = strchr(xdigits, ch) - xdigits;
90                               INSIST(n >= 0 && n <= 15);
91                               if (dirty == 0)
92                                         tmp = n;
93                               else
94                                         tmp = (tmp << 4) | n;
95                               if (++dirty == 2) {
96                                         if (size-- == 0)
97                                                   goto emsgsize;
98                                         *dst++ = (u_char) tmp;
99                                         dirty = 0;
100                               }
101                     }
102                     if (dirty) {  /* Odd trailing nybble? */
103                               if (size-- == 0)
104                                         goto emsgsize;
105                               *dst++ = (u_char) (tmp << 4);
106                     }
107           } else if (isascii((u_char)ch) && isdigit((u_char)ch)) {
108                     /* Decimal: eat dotted digit string. */
109                     for (;;) {
110                               tmp = 0;
111                               do {
112                                         n = strchr(digits, ch) - digits;
113                                         INSIST(n >= 0 && n <= 9);
114                                         tmp *= 10;
115                                         tmp += n;
116                                         if (tmp > 255)
117                                                   goto enoent;
118                               } while ((ch = *src++) != '\0' &&
119                                          isascii((u_char)ch) && isdigit((u_char)ch));
120                               if (size-- == 0)
121                                         goto emsgsize;
122                               *dst++ = (u_char) tmp;
123                               if (ch == '\0' || ch == '/')
124                                         break;
125                               if (ch != '.')
126                                         goto enoent;
127                               ch = *src++;
128                               if (!isascii((u_char)ch) || !isdigit((u_char)ch))
129                                         goto enoent;
130                     }
131           } else
132                     goto enoent;
133 
134           bits = -1;
135           if (ch == '/' && isascii((u_char)(src[0])) &&
136               isdigit((u_char)(src[0])) && dst > odst) {
137                     /* CIDR width specifier.  Nothing can follow it. */
138                     ch = *src++;        /* Skip over the /. */
139                     bits = 0;
140                     do {
141                               n = strchr(digits, ch) - digits;
142                               INSIST(n >= 0 && n <= 9);
143                               bits *= 10;
144                               bits += (int)n;
145                               if (bits > 32)
146                                         goto emsgsize;
147                     } while ((ch = *src++) != '\0' && isascii((u_char)ch)
148                         && isdigit((u_char)ch));
149                     if (ch != '\0')
150                               goto enoent;
151           }
152 
153           /* Fiery death and destruction unless we prefetched EOS. */
154           if (ch != '\0')
155                     goto enoent;
156 
157           /* If nothing was written to the destination, we found no address. */
158           if (dst == odst)
159                     goto enoent;
160           /* If no CIDR spec was given, infer width from net class. */
161           if (bits == -1) {
162                     if (*odst >= 240)   /* Class E */
163                               bits = 32;
164                     else if (*odst >= 224)        /* Class D */
165                               bits = 4;
166                     else if (*odst >= 192)        /* Class C */
167                               bits = 24;
168                     else if (*odst >= 128)        /* Class B */
169                               bits = 16;
170                     else                          /* Class A */
171                               bits = 8;
172                     /* If inputed mask is narrower than specified octets, widen. */
173                     if (bits >= 8 && bits < ((dst - odst) * 8))
174                               bits = (int)(dst - odst) * 8;
175           }
176           /* Extend network to cover the actual mask. */
177           while (bits > ((dst - odst) * 8)) {
178                     if (size-- == 0)
179                               goto emsgsize;
180                     *dst++ = '\0';
181           }
182           return (bits);
183 
184  enoent:
185           errno = ENOENT;
186           return (-1);
187 
188  emsgsize:
189           errno = EMSGSIZE;
190           return (-1);
191 }
192 
193 static int
getbits(const char * src,int * bitsp)194 getbits(const char *src, int *bitsp)
195 {
196           static const char digits[] = "0123456789";
197           int n;
198           int val;
199           char ch;
200 
201           val = 0;
202           n = 0;
203           while ((ch = *src++) != '\0') {
204                     const char *pch;
205 
206                     pch = strchr(digits, ch);
207                     if (pch != NULL) {
208                               if (n++ != 0 && val == 0)     /* no leading zeros */
209                                         return (0);
210                               val *= 10;
211                               val += (int)(pch - digits);
212                               if (val > 128)                          /* range */
213                                         return (0);
214                               continue;
215                     }
216                     return (0);
217           }
218           if (n == 0)
219                     return (0);
220           *bitsp = val;
221           return (1);
222 }
223 
224 static int
getv4(const char * src,u_char * dst,int * bitsp)225 getv4(const char *src, u_char *dst, int *bitsp)
226 {
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 += (int)(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
inet_net_pton_ipv6(const char * src,u_char * dst,size_t size)269 inet_net_pton_ipv6(const char *src, u_char *dst, size_t size)
270 {
271           static const char xdigits_l[] = "0123456789abcdef",
272                                 xdigits_u[] = "0123456789ABCDEF";
273           u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
274           const char *xdigits, *curtok;
275           int ch, saw_xdigit;
276           u_int val;
277           int digits;
278           int bits;
279           size_t bytes;
280           int words;
281           int ipv4;
282 
283           memset((tp = tmp), '\0', NS_IN6ADDRSZ);
284           endp = tp + NS_IN6ADDRSZ;
285           colonp = NULL;
286           /* Leading :: requires some special handling. */
287           if (*src == ':')
288                     if (*++src != ':')
289                               goto enoent;
290           curtok = src;
291           saw_xdigit = 0;
292           val = 0;
293           digits = 0;
294           bits = -1;
295           ipv4 = 0;
296           while ((ch = *src++) != '\0') {
297                     const char *pch;
298 
299                     if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
300                               pch = strchr((xdigits = xdigits_u), ch);
301                     if (pch != NULL) {
302                               val <<= 4;
303                               val |= (int)(pch - xdigits);
304                               if (++digits > 4)
305                                         goto enoent;
306                               saw_xdigit = 1;
307                               continue;
308                     }
309                     if (ch == ':') {
310                               curtok = src;
311                               if (!saw_xdigit) {
312                                         if (colonp)
313                                                   goto enoent;
314                                         colonp = tp;
315                                         continue;
316                               } else if (*src == '\0')
317                                         goto enoent;
318                               if (tp + NS_INT16SZ > endp)
319                                         return (0);
320                               *tp++ = (u_char) (val >> 8) & 0xff;
321                               *tp++ = (u_char) val & 0xff;
322                               saw_xdigit = 0;
323                               digits = 0;
324                               val = 0;
325                               continue;
326                     }
327                     if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
328                          getv4(curtok, tp, &bits) > 0) {
329                               tp += NS_INADDRSZ;
330                               saw_xdigit = 0;
331                               ipv4 = 1;
332                               break;    /* '\0' was seen by inet_pton4(). */
333                     }
334                     if (ch == '/' && getbits(src, &bits) > 0)
335                               break;
336                     goto enoent;
337           }
338           if (saw_xdigit) {
339                     if (tp + NS_INT16SZ > endp)
340                               goto enoent;
341                     *tp++ = (u_char) (val >> 8) & 0xff;
342                     *tp++ = (u_char) val & 0xff;
343           }
344           if (bits == -1)
345                     bits = 128;
346 
347           words = (bits + 15) / 16;
348           if (words < 2)
349                     words = 2;
350           if (ipv4)
351                     words = 8;
352           endp =  tmp + 2 * words;
353 
354           if (colonp != NULL) {
355                     /*
356                      * Since some memmove()'s erroneously fail to handle
357                      * overlapping regions, we'll do the shift by hand.
358                      */
359                     const ptrdiff_t n = tp - colonp;
360                     int i;
361 
362                     if (tp == endp)
363                               goto enoent;
364                     for (i = 1; i <= n; i++) {
365                               endp[- i] = colonp[n - i];
366                               colonp[n - i] = 0;
367                     }
368                     tp = endp;
369           }
370           if (tp != endp)
371                     goto enoent;
372 
373           bytes = (bits + 7) / 8;
374           if (bytes > size)
375                     goto emsgsize;
376           memcpy(dst, tmp, bytes);
377           return (bits);
378 
379  enoent:
380           errno = ENOENT;
381           return (-1);
382 
383  emsgsize:
384           errno = EMSGSIZE;
385           return (-1);
386 }
387 
388 /*
389  * int
390  * inet_net_pton(af, src, dst, size)
391  *        convert network number from presentation to network format.
392  *        accepts hex octets, hex strings, decimal octets, and /CIDR.
393  *        "size" is in bytes and describes "dst".
394  * return:
395  *        number of bits, either inputed classfully or specified with /CIDR,
396  *        or -1 if some failure occurred (check errno).  ENOENT means it was
397  *        not a valid network specification.
398  * author:
399  *        Paul Vixie (ISC), June 1996
400  */
401 int
inet_net_pton(int af,const char * src,void * dst,size_t size)402 inet_net_pton(int af, const char *src, void *dst, size_t size)
403 {
404           switch (af) {
405           case AF_INET:
406                     return (inet_net_pton_ipv4(src, dst, size));
407           case AF_INET6:
408                     return (inet_net_pton_ipv6(src, dst, size));
409           default:
410                     errno = EAFNOSUPPORT;
411                     return (-1);
412           }
413 }
414