]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/ntp/lib/isc/inet_pton.c
o Fix invalid TCP checksums with pf(4). [EN-16:02.pf]
[FreeBSD/releng/9.3.git] / contrib / ntp / lib / isc / inet_pton.c
1 /*
2  * Copyright (C) 2004, 2005, 2007  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1996-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /*! \file */
19
20 #if defined(LIBC_SCCS) && !defined(lint)
21 static char rcsid[] =
22         "$Id: inet_pton.c,v 1.19 2007/06/19 23:47:17 tbox Exp $";
23 #endif /* LIBC_SCCS and not lint */
24
25 #include <config.h>
26
27 #include <errno.h>
28 #include <string.h>
29
30 #include <isc/net.h>
31
32 /*% INT16 Size */
33 #define NS_INT16SZ       2
34 /*% IPv4 Address Size */
35 #define NS_INADDRSZ      4
36 /*% IPv6 Address Size */
37 #define NS_IN6ADDRSZ    16
38
39 /*
40  * WARNING: Don't even consider trying to compile this on a system where
41  * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
42  */
43
44 static int inet_pton4(const char *src, unsigned char *dst);
45 static int inet_pton6(const char *src, unsigned char *dst);
46 int isc_net_pton(int af, const char *src, void *dst);
47
48 /*% 
49  *      convert from presentation format (which usually means ASCII printable)
50  *      to network format (which is usually some kind of binary format).
51  * \return
52  *      1 if the address was valid for the specified address family
53  *      0 if the address wasn't valid (`dst' is untouched in this case)
54  *      -1 if some other error occurred (`dst' is untouched in this case, too)
55  * \author
56  *      Paul Vixie, 1996.
57  */
58 int
59 isc_net_pton(int af, const char *src, void *dst) {
60         switch (af) {
61         case AF_INET:
62                 return (inet_pton4(src, dst));
63         case AF_INET6:
64                 return (inet_pton6(src, dst));
65         default:
66                 errno = EAFNOSUPPORT;
67                 return (-1);
68         }
69         /* NOTREACHED */
70 }
71
72 /*!\fn static int inet_pton4(const char *src, unsigned char *dst)
73  * \brief
74  *      like inet_aton() but without all the hexadecimal and shorthand.
75  * \return
76  *      1 if `src' is a valid dotted quad, else 0.
77  * \note
78  *      does not touch `dst' unless it's returning 1.
79  * \author
80  *      Paul Vixie, 1996.
81  */
82 static int
83 inet_pton4(const char *src, unsigned char *dst) {
84         static const char digits[] = "0123456789";
85         int saw_digit, octets, ch;
86         unsigned char tmp[NS_INADDRSZ], *tp;
87
88         saw_digit = 0;
89         octets = 0;
90         *(tp = tmp) = 0;
91         while ((ch = *src++) != '\0') {
92                 const char *pch;
93
94                 if ((pch = strchr(digits, ch)) != NULL) {
95                         size_t newv = *tp * 10 + (pch - digits);
96
97                         if (saw_digit && *tp == 0)
98                                 return (0);
99                         if (newv > 255)
100                                 return (0);
101                         *tp = (unsigned char)newv;
102                         if (!saw_digit) {
103                                 if (++octets > 4)
104                                         return (0);
105                                 saw_digit = 1;
106                         }
107                 } else if (ch == '.' && saw_digit) {
108                         if (octets == 4)
109                                 return (0);
110                         *++tp = 0;
111                         saw_digit = 0;
112                 } else
113                         return (0);
114         }
115         if (octets < 4)
116                 return (0);
117         memcpy(dst, tmp, NS_INADDRSZ);
118         return (1);
119 }
120
121 /*%
122  *      convert presentation level address to network order binary form.
123  * \return
124  *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
125  * \note
126  *      (1) does not touch `dst' unless it's returning 1.
127  * \note
128  *      (2) :: in a full address is silently ignored.
129  * \author
130  *      inspired by Mark Andrews.
131  * \author
132  *      Paul Vixie, 1996.
133  */
134 static int
135 inet_pton6(const char *src, unsigned char *dst) {
136         static const char xdigits_l[] = "0123456789abcdef",
137                           xdigits_u[] = "0123456789ABCDEF";
138         unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
139         const char *xdigits, *curtok;
140         int ch, seen_xdigits;
141         unsigned int val;
142
143         memset((tp = tmp), '\0', NS_IN6ADDRSZ);
144         endp = tp + NS_IN6ADDRSZ;
145         colonp = NULL;
146         /* Leading :: requires some special handling. */
147         if (*src == ':')
148                 if (*++src != ':')
149                         return (0);
150         curtok = src;
151         seen_xdigits = 0;
152         val = 0;
153         while ((ch = *src++) != '\0') {
154                 const char *pch;
155
156                 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
157                         pch = strchr((xdigits = xdigits_u), ch);
158                 if (pch != NULL) {
159                         val <<= 4;
160                         val |= (pch - xdigits);
161                         if (++seen_xdigits > 4)
162                                 return (0);
163                         continue;
164                 }
165                 if (ch == ':') {
166                         curtok = src;
167                         if (!seen_xdigits) {
168                                 if (colonp)
169                                         return (0);
170                                 colonp = tp;
171                                 continue;
172                         }
173                         if (tp + NS_INT16SZ > endp)
174                                 return (0);
175                         *tp++ = (unsigned char) (val >> 8) & 0xff;
176                         *tp++ = (unsigned char) val & 0xff;
177                         seen_xdigits = 0;
178                         val = 0;
179                         continue;
180                 }
181                 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
182                     inet_pton4(curtok, tp) > 0) {
183                         tp += NS_INADDRSZ;
184                         seen_xdigits = 0;
185                         break;  /* '\0' was seen by inet_pton4(). */
186                 }
187                 return (0);
188         }
189         if (seen_xdigits) {
190                 if (tp + NS_INT16SZ > endp)
191                         return (0);
192                 *tp++ = (unsigned char) (val >> 8) & 0xff;
193                 *tp++ = (unsigned char) val & 0xff;
194         }
195         if (colonp != NULL) {
196                 /*
197                  * Since some memmove()'s erroneously fail to handle
198                  * overlapping regions, we'll do the shift by hand.
199                  */
200                 const size_t n = tp - colonp;
201                 int i;
202
203                 if (tp == endp)
204                         return (0);
205                 for (i = 1; (size_t)i <= n; i++) {
206                         endp[- i] = colonp[n - i];
207                         colonp[n - i] = 0;
208                 }
209                 tp = endp;
210         }
211         if (tp != endp)
212                 return (0);
213         memcpy(dst, tmp, NS_IN6ADDRSZ);
214         return (1);
215 }