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