inet_pton.c: use locale-independent tolower
[gnulib.git] / lib / inet_pton.c
1 /* inet_pton.c -- convert IPv4 and IPv6 addresses from text to binary form
2
3    Copyright (C) 2006, 2008 Free Software Foundation, Inc.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 /*
19  * Copyright (c) 1996,1999 by Internet Software Consortium.
20  *
21  * Permission to use, copy, modify, and distribute this software for any
22  * purpose with or without fee is hereby granted, provided that the above
23  * copyright notice and this permission notice appear in all copies.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
26  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
27  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
28  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
29  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
30  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
31  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
32  * SOFTWARE.
33  */
34
35 #include <config.h>
36
37 /* Specification.  */
38 #include <arpa/inet.h>
39
40 #include <c-ctype.h>
41 #include <string.h>
42 #include <errno.h>
43
44 #ifndef EAFNOSUPPORT
45 # define EAFNOSUPPORT EINVAL
46 #endif
47
48 #define NS_INADDRSZ 4
49 #define NS_IN6ADDRSZ 16
50 #define NS_INT16SZ 2
51
52 /*
53  * WARNING: Don't even consider trying to compile this on a system where
54  * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
55  */
56
57 static int inet_pton4 (const char *src, unsigned char *dst);
58 #if HAVE_IPV6
59 static int inet_pton6 (const char *src, unsigned char *dst);
60 #endif
61
62 /* int
63  * inet_pton(af, src, dst)
64  *      convert from presentation format (which usually means ASCII printable)
65  *      to network format (which is usually some kind of binary format).
66  * return:
67  *      1 if the address was valid for the specified address family
68  *      0 if the address wasn't valid (`dst' is untouched in this case)
69  *      -1 if some other error occurred (`dst' is untouched in this case, too)
70  * author:
71  *      Paul Vixie, 1996.
72  */
73 int
74 inet_pton (int af, const char *restrict src, void *restrict dst)
75 {
76   switch (af)
77     {
78     case AF_INET:
79       return (inet_pton4 (src, dst));
80
81 #if HAVE_IPV6
82     case AF_INET6:
83       return (inet_pton6 (src, dst));
84 #endif
85
86     default:
87       errno = EAFNOSUPPORT;
88       return (-1);
89     }
90   /* NOTREACHED */
91 }
92
93 /* int
94  * inet_pton4(src, dst)
95  *      like inet_aton() but without all the hexadecimal, octal (with the
96  *      exception of 0) and shorthand.
97  * return:
98  *      1 if `src' is a valid dotted quad, else 0.
99  * notice:
100  *      does not touch `dst' unless it's returning 1.
101  * author:
102  *      Paul Vixie, 1996.
103  */
104 static int
105 inet_pton4 (const char *restrict src, unsigned char *restrict dst)
106 {
107   int saw_digit, octets, ch;
108   unsigned char tmp[NS_INADDRSZ], *tp;
109
110   saw_digit = 0;
111   octets = 0;
112   *(tp = tmp) = 0;
113   while ((ch = *src++) != '\0')
114     {
115
116       if (ch >= '0' && ch <= '9')
117         {
118           unsigned new = *tp * 10 + (ch - '0');
119
120           if (saw_digit && *tp == 0)
121             return (0);
122           if (new > 255)
123             return (0);
124           *tp = new;
125           if (!saw_digit)
126             {
127               if (++octets > 4)
128                 return (0);
129               saw_digit = 1;
130             }
131         }
132       else if (ch == '.' && saw_digit)
133         {
134           if (octets == 4)
135             return (0);
136           *++tp = 0;
137           saw_digit = 0;
138         }
139       else
140         return (0);
141     }
142   if (octets < 4)
143     return (0);
144   memcpy (dst, tmp, NS_INADDRSZ);
145   return (1);
146 }
147
148 #if HAVE_IPV6
149
150 /* int
151  * inet_pton6(src, dst)
152  *      convert presentation level address to network order binary form.
153  * return:
154  *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
155  * notice:
156  *      (1) does not touch `dst' unless it's returning 1.
157  *      (2) :: in a full address is silently ignored.
158  * credit:
159  *      inspired by Mark Andrews.
160  * author:
161  *      Paul Vixie, 1996.
162  */
163 static int
164 inet_pton6 (const char *restrict src, unsigned char *restrict dst)
165 {
166   static const char xdigits[] = "0123456789abcdef";
167   unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
168   const char *curtok;
169   int ch, saw_xdigit;
170   unsigned val;
171
172   tp = memset (tmp, '\0', NS_IN6ADDRSZ);
173   endp = tp + NS_IN6ADDRSZ;
174   colonp = NULL;
175   /* Leading :: requires some special handling. */
176   if (*src == ':')
177     if (*++src != ':')
178       return (0);
179   curtok = src;
180   saw_xdigit = 0;
181   val = 0;
182   while ((ch = c_tolower (*src++)) != '\0')
183     {
184       const char *pch;
185
186       pch = strchr (xdigits, ch);
187       if (pch != NULL)
188         {
189           val <<= 4;
190           val |= (pch - xdigits);
191           if (val > 0xffff)
192             return (0);
193           saw_xdigit = 1;
194           continue;
195         }
196       if (ch == ':')
197         {
198           curtok = src;
199           if (!saw_xdigit)
200             {
201               if (colonp)
202                 return (0);
203               colonp = tp;
204               continue;
205             }
206           else if (*src == '\0')
207             {
208               return (0);
209             }
210           if (tp + NS_INT16SZ > endp)
211             return (0);
212           *tp++ = (u_char) (val >> 8) & 0xff;
213           *tp++ = (u_char) val & 0xff;
214           saw_xdigit = 0;
215           val = 0;
216           continue;
217         }
218       if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
219           inet_pton4 (curtok, tp) > 0)
220         {
221           tp += NS_INADDRSZ;
222           saw_xdigit = 0;
223           break;                /* '\0' was seen by inet_pton4(). */
224         }
225       return (0);
226     }
227   if (saw_xdigit)
228     {
229       if (tp + NS_INT16SZ > endp)
230         return (0);
231       *tp++ = (u_char) (val >> 8) & 0xff;
232       *tp++ = (u_char) val & 0xff;
233     }
234   if (colonp != NULL)
235     {
236       /*
237        * Since some memmove()'s erroneously fail to handle
238        * overlapping regions, we'll do the shift by hand.
239        */
240       const int n = tp - colonp;
241       int i;
242
243       if (tp == endp)
244         return (0);
245       for (i = 1; i <= n; i++)
246         {
247           endp[-i] = colonp[n - i];
248           colonp[n - i] = 0;
249         }
250       tp = endp;
251     }
252   if (tp != endp)
253     return (0);
254   memcpy (dst, tmp, NS_IN6ADDRSZ);
255   return (1);
256 }
257 #endif