Imported Upstream version 3.2.0
[debian/amanda] / gnulib / inet_pton.c
1 /* inet_pton.c -- convert IPv4 and IPv6 addresses from text to binary form
2
3    Copyright (C) 2006, 2008, 2009, 2010 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 #define NS_INADDRSZ 4
45 #define NS_IN6ADDRSZ 16
46 #define NS_INT16SZ 2
47
48 /*
49  * WARNING: Don't even consider trying to compile this on a system where
50  * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
51  */
52
53 static int inet_pton4 (const char *src, unsigned char *dst);
54 #if HAVE_IPV6
55 static int inet_pton6 (const char *src, unsigned char *dst);
56 #endif
57
58 /* int
59  * inet_pton(af, src, dst)
60  *      convert from presentation format (which usually means ASCII printable)
61  *      to network format (which is usually some kind of binary format).
62  * return:
63  *      1 if the address was valid for the specified address family
64  *      0 if the address wasn't valid (`dst' is untouched in this case)
65  *      -1 if some other error occurred (`dst' is untouched in this case, too)
66  * author:
67  *      Paul Vixie, 1996.
68  */
69 int
70 inet_pton (int af, const char *restrict src, void *restrict dst)
71 {
72   switch (af)
73     {
74     case AF_INET:
75       return (inet_pton4 (src, dst));
76
77 #if HAVE_IPV6
78     case AF_INET6:
79       return (inet_pton6 (src, dst));
80 #endif
81
82     default:
83       errno = EAFNOSUPPORT;
84       return (-1);
85     }
86   /* NOTREACHED */
87 }
88
89 /* int
90  * inet_pton4(src, dst)
91  *      like inet_aton() but without all the hexadecimal, octal (with the
92  *      exception of 0) and shorthand.
93  * return:
94  *      1 if `src' is a valid dotted quad, else 0.
95  * notice:
96  *      does not touch `dst' unless it's returning 1.
97  * author:
98  *      Paul Vixie, 1996.
99  */
100 static int
101 inet_pton4 (const char *restrict src, unsigned char *restrict dst)
102 {
103   int saw_digit, octets, ch;
104   unsigned char tmp[NS_INADDRSZ], *tp;
105
106   saw_digit = 0;
107   octets = 0;
108   *(tp = tmp) = 0;
109   while ((ch = *src++) != '\0')
110     {
111
112       if (ch >= '0' && ch <= '9')
113         {
114           unsigned new = *tp * 10 + (ch - '0');
115
116           if (saw_digit && *tp == 0)
117             return (0);
118           if (new > 255)
119             return (0);
120           *tp = new;
121           if (!saw_digit)
122             {
123               if (++octets > 4)
124                 return (0);
125               saw_digit = 1;
126             }
127         }
128       else if (ch == '.' && saw_digit)
129         {
130           if (octets == 4)
131             return (0);
132           *++tp = 0;
133           saw_digit = 0;
134         }
135       else
136         return (0);
137     }
138   if (octets < 4)
139     return (0);
140   memcpy (dst, tmp, NS_INADDRSZ);
141   return (1);
142 }
143
144 #if HAVE_IPV6
145
146 /* int
147  * inet_pton6(src, dst)
148  *      convert presentation level address to network order binary form.
149  * return:
150  *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
151  * notice:
152  *      (1) does not touch `dst' unless it's returning 1.
153  *      (2) :: in a full address is silently ignored.
154  * credit:
155  *      inspired by Mark Andrews.
156  * author:
157  *      Paul Vixie, 1996.
158  */
159 static int
160 inet_pton6 (const char *restrict src, unsigned char *restrict dst)
161 {
162   static const char xdigits[] = "0123456789abcdef";
163   unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
164   const char *curtok;
165   int ch, saw_xdigit;
166   unsigned val;
167
168   tp = memset (tmp, '\0', NS_IN6ADDRSZ);
169   endp = tp + NS_IN6ADDRSZ;
170   colonp = NULL;
171   /* Leading :: requires some special handling. */
172   if (*src == ':')
173     if (*++src != ':')
174       return (0);
175   curtok = src;
176   saw_xdigit = 0;
177   val = 0;
178   while ((ch = c_tolower (*src++)) != '\0')
179     {
180       const char *pch;
181
182       pch = strchr (xdigits, ch);
183       if (pch != NULL)
184         {
185           val <<= 4;
186           val |= (pch - xdigits);
187           if (val > 0xffff)
188             return (0);
189           saw_xdigit = 1;
190           continue;
191         }
192       if (ch == ':')
193         {
194           curtok = src;
195           if (!saw_xdigit)
196             {
197               if (colonp)
198                 return (0);
199               colonp = tp;
200               continue;
201             }
202           else if (*src == '\0')
203             {
204               return (0);
205             }
206           if (tp + NS_INT16SZ > endp)
207             return (0);
208           *tp++ = (u_char) (val >> 8) & 0xff;
209           *tp++ = (u_char) val & 0xff;
210           saw_xdigit = 0;
211           val = 0;
212           continue;
213         }
214       if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
215           inet_pton4 (curtok, tp) > 0)
216         {
217           tp += NS_INADDRSZ;
218           saw_xdigit = 0;
219           break;                /* '\0' was seen by inet_pton4(). */
220         }
221       return (0);
222     }
223   if (saw_xdigit)
224     {
225       if (tp + NS_INT16SZ > endp)
226         return (0);
227       *tp++ = (u_char) (val >> 8) & 0xff;
228       *tp++ = (u_char) val & 0xff;
229     }
230   if (colonp != NULL)
231     {
232       /*
233        * Since some memmove()'s erroneously fail to handle
234        * overlapping regions, we'll do the shift by hand.
235        */
236       const int n = tp - colonp;
237       int i;
238
239       if (tp == endp)
240         return (0);
241       for (i = 1; i <= n; i++)
242         {
243           endp[-i] = colonp[n - i];
244           colonp[n - i] = 0;
245         }
246       tp = endp;
247     }
248   if (tp != endp)
249     return (0);
250   memcpy (dst, tmp, NS_IN6ADDRSZ);
251   return (1);
252 }
253 #endif