inet_pton: Reject IPv6 addresses with many leading zeros [BZ #16637]
[platform/upstream/glibc.git] / resolv / inet_net_pton.c
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/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22
23 #include <assert.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #ifdef SPRINTF_CHAR
31 # define SPRINTF(x) strlen(sprintf/**/x)
32 #else
33 # define SPRINTF(x) ((size_t)sprintf x)
34 #endif
35
36 static int      inet_net_pton_ipv4 (const char *src, u_char *dst,
37                                     size_t size) __THROW;
38
39 /*
40  * static int
41  * inet_net_pton(af, src, dst, size)
42  *      convert network number from presentation to network format.
43  *      accepts hex octets, hex strings, decimal octets, and /CIDR.
44  *      "size" is in bytes and describes "dst".
45  * return:
46  *      number of bits, either imputed classfully or specified with /CIDR,
47  *      or -1 if some failure occurred (check errno).  ENOENT means it was
48  *      not a valid network specification.
49  * author:
50  *      Paul Vixie (ISC), June 1996
51  */
52 int
53 inet_net_pton (int af, const char *src, void *dst, size_t size)
54 {
55         switch (af) {
56         case AF_INET:
57                 return (inet_net_pton_ipv4(src, dst, size));
58         default:
59                 __set_errno (EAFNOSUPPORT);
60                 return (-1);
61         }
62 }
63
64 /*
65  * static int
66  * inet_net_pton_ipv4(src, dst, size)
67  *      convert IPv4 network number from presentation to network format.
68  *      accepts hex octets, hex strings, decimal octets, and /CIDR.
69  *      "size" is in bytes and describes "dst".
70  * return:
71  *      number of bits, either imputed classfully or specified with /CIDR,
72  *      or -1 if some failure occurred (check errno).  ENOENT means it was
73  *      not an IPv4 network specification.
74  * note:
75  *      network byte order assumed.  this means 192.5.5.240/28 has
76  *      0b11110000 in its fourth octet.
77  * author:
78  *      Paul Vixie (ISC), June 1996
79  */
80 static int
81 inet_net_pton_ipv4 (const char *src, u_char *dst, size_t size)
82 {
83         static const char xdigits[] = "0123456789abcdef";
84         int n, ch, tmp, dirty, bits;
85         const u_char *odst = dst;
86
87         ch = *src++;
88         if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
89             && isascii(src[1]) && isxdigit(src[1])) {
90                 /* Hexadecimal: Eat nybble string. */
91                 if (size <= 0)
92                         goto emsgsize;
93                 dirty = 0;
94                 tmp = 0;        /* To calm down gcc.  */
95                 src++;  /* skip x or X. */
96                 while (isxdigit((ch = *src++))) {
97                         ch = _tolower(ch);
98                         n = (const char *) __rawmemchr(xdigits, ch) - xdigits;
99                         assert(n >= 0 && n <= 15);
100                         if (dirty == 0)
101                                 tmp = n;
102                         else
103                                 tmp = (tmp << 4) | n;
104                         if (++dirty == 2) {
105                                 if (size-- <= 0)
106                                         goto emsgsize;
107                                 *dst++ = (u_char) tmp;
108                                 dirty = 0;
109                         }
110                 }
111                 if (dirty) {  /* Odd trailing nybble? */
112                         if (size-- <= 0)
113                                 goto emsgsize;
114                         *dst++ = (u_char) (tmp << 4);
115                 }
116         } else if (isascii(ch) && isdigit(ch)) {
117                 /* Decimal: eat dotted digit string. */
118                 for (;;) {
119                         tmp = 0;
120                         do {
121                                 n = ((const char *) __rawmemchr(xdigits, ch)
122                                      - xdigits);
123                                 assert(n >= 0 && n <= 9);
124                                 tmp *= 10;
125                                 tmp += n;
126                                 if (tmp > 255)
127                                         goto enoent;
128                         } while (isascii((ch = *src++)) && isdigit(ch));
129                         if (size-- <= 0)
130                                 goto emsgsize;
131                         *dst++ = (u_char) tmp;
132                         if (ch == '\0' || ch == '/')
133                                 break;
134                         if (ch != '.')
135                                 goto enoent;
136                         ch = *src++;
137                         if (!isascii(ch) || !isdigit(ch))
138                                 goto enoent;
139                 }
140         } else
141                 goto enoent;
142
143         bits = -1;
144         if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst) {
145                 /* CIDR width specifier.  Nothing can follow it. */
146                 ch = *src++;    /* Skip over the /. */
147                 bits = 0;
148                 do {
149                         n = (const char *) __rawmemchr(xdigits, ch) - xdigits;
150                         assert(n >= 0 && n <= 9);
151                         bits *= 10;
152                         bits += n;
153                 } while (isascii((ch = *src++)) && isdigit(ch));
154                 if (ch != '\0')
155                         goto enoent;
156                 if (bits > 32)
157                         goto emsgsize;
158         }
159
160         /* Firey death and destruction unless we prefetched EOS. */
161         if (ch != '\0')
162                 goto enoent;
163
164         /* If nothing was written to the destination, we found no address. */
165         if (dst == odst)
166                 goto enoent;
167         /* If no CIDR spec was given, infer width from net class. */
168         if (bits == -1) {
169                 if (*odst >= 240)       /* Class E */
170                         bits = 32;
171                 else if (*odst >= 224)  /* Class D */
172                         bits = 4;
173                 else if (*odst >= 192)  /* Class C */
174                         bits = 24;
175                 else if (*odst >= 128)  /* Class B */
176                         bits = 16;
177                 else                    /* Class A */
178                         bits = 8;
179                 /* If imputed mask is narrower than specified octets, widen. */
180                 if (bits >= 8 && bits < ((dst - odst) * 8))
181                         bits = (dst - odst) * 8;
182         }
183         /* Extend network to cover the actual mask. */
184         while (bits > ((dst - odst) * 8)) {
185                 if (size-- <= 0)
186                         goto emsgsize;
187                 *dst++ = '\0';
188         }
189         return (bits);
190
191  enoent:
192         __set_errno (ENOENT);
193         return (-1);
194
195  emsgsize:
196         __set_errno (EMSGSIZE);
197         return (-1);
198 }