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