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