1 /*
2 inet_pton.c -- convert IPv4 and IPv6 addresses from text to binary form
3 
4 Modifications to inet_pton6 allowing IPv4 addresses to appear
5 throughout, and miscellaneous modifications to inet_pton4, by Tom
6 Harrison <tomhrr@cpan.org> (Copyright (C) 2010).
7 
8 Copyright (C) 2006 Free Software Foundation, Inc.
9 
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2, or (at your option)
13 any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software Foundation,
22 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 
24 Copyright (c) 1996,1999 by Internet Software Consortium.
25 
26 Permission to use, copy, modify, and distribute this software for any
27 purpose with or without fee is hereby granted, provided that the above
28 copyright notice and this permission notice appear in all copies.
29 
30 THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
31 ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
32 OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
33 CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
34 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
35 PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
36 ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
37 SOFTWARE.
38 */
39 
40 #include <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 int inet_pton4 (const char *src, unsigned char *dst);
49 int inet_pton6 (const char *src, unsigned char *dst);
50 
51 /* int
52  * inet_pton4(src, dst)
53  *	like inet_aton() but without all the hexadecimal, octal (with the
54  *	exception of 0) and shorthand.
55  * return:
56  *	1 if `src' is a valid dotted quad, else 0.
57  * notice:
58  *	does not touch `dst' unless it's returning 1.
59  * author:
60  *	Paul Vixie, 1996.
61  *      (Some modifications by Tom Harrison, 2010.)
62  */
63 int
inet_pton4(const char * src,unsigned char * dst)64 inet_pton4 (const char *src, unsigned char *dst)
65 {
66     int saw_digit, octets, ch;
67     unsigned char tmp[NS_INADDRSZ], *tp;
68 
69     memset(tmp, 0, NS_INADDRSZ);
70     saw_digit = 0;
71     octets = 0;
72     *(tp = tmp) = 0;
73 
74     while ((ch = *src++) != '\0') {
75         if (ch >= '0' && ch <= '9') {
76 	    unsigned n = *tp * 10 + (ch - '0');
77 
78             if (saw_digit && *tp == 0) {
79                 return 0;
80             }
81             if (n > 255) {
82                 return 0;
83             }
84             *tp = n;
85             if (!saw_digit) {
86                 ++octets;
87                 saw_digit = 1;
88 	    }
89         } else if (ch == '.' && saw_digit) {
90             if (octets == 4) {
91                 return 0;
92             }
93             ++tp;
94             saw_digit = 0;
95 	} else {
96             return 0;
97         }
98     }
99 
100     memcpy(dst, tmp, NS_INADDRSZ);
101     return 1;
102 }
103 
104 /* int
105  * inet_pton6(src, dst)
106  *	convert presentation level address to network order binary form.
107  * return:
108  *	1 if `src' is a valid IPv6 address (may contain IPv4 addresses
109  *      in any position), else 0.
110  * notice:
111  *	(1) does not touch `dst' unless it's returning 1.
112  *	(2) :: in a full address is silently ignored.
113  * credit:
114  *	inspired by Mark Andrews.
115  * author:
116  *	Paul Vixie, 1996.
117  *      (Modifications allowing IPv4 addresses to appear throughout
118  *       by Tom Harrison, 2010.)
119  */
120 int
inet_pton6(const char * src,unsigned char * dst)121 inet_pton6 (const char *src, unsigned char *dst)
122 {
123     static const char xdigits[] = "0123456789abcdef";
124     unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
125     const char *ipv4_endp;
126     char ipv4[16];
127     int diff;
128     const char *curtok;
129     int ch, saw_xdigit;
130     unsigned val;
131 
132     tp = (unsigned char *) memset (tmp, '\0', NS_IN6ADDRSZ);
133     endp = tp + NS_IN6ADDRSZ;
134     colonp = NULL;
135     /* Leading :: requires some special handling. */
136     if (*src == ':') {
137         if (*++src != ':') {
138             return 0;
139         }
140     }
141     curtok = src;
142     saw_xdigit = 0;
143     val = 0;
144     while ((ch = tolower (*src++)) != '\0') {
145         const char *pch;
146 
147         pch = strchr (xdigits, ch);
148         if (pch != NULL) {
149             val <<= 4;
150             val |= (pch - xdigits);
151             if (val > 0xffff) {
152                 return 0;
153             }
154             saw_xdigit = 1;
155             continue;
156         }
157         if (ch == ':') {
158             curtok = src;
159             if (!saw_xdigit) {
160                 if (colonp) {
161                     return 0;
162                 }
163                 colonp = tp;
164                 continue;
165 	    } else if (*src == '\0') {
166                 return 0;
167 	    }
168             if (tp + NS_INT16SZ > endp) {
169                 return 0;
170             }
171             *tp++ = (unsigned char) (val >> 8) & 0xff;
172             *tp++ = (unsigned char) val & 0xff;
173             saw_xdigit = 0;
174             val = 0;
175             continue;
176 	}
177         if (ch == '.' && ((tp + NS_INADDRSZ) <= endp)) {
178             /* Find the next : from curtok, copy from curtok to that
179              * point to ipv4, if it's IPv4 all is good. If the : is not found,
180              * it terminates, so check it directly. */
181             ipv4_endp = strchr(curtok, ':');
182             if (ipv4_endp) {
183                 diff = ipv4_endp - curtok;
184                 if (diff > 15) {
185                     return 0;
186                 }
187                 memcpy(ipv4, curtok, diff);
188                 ipv4[diff] = '\0';
189                 diff = inet_pton4(ipv4, tp);
190             } else {
191                 diff = inet_pton4(curtok, tp);
192             }
193             if (diff) {
194                 val = (tp[2] << 8) | tp[3];
195                 tp += 2;
196                 saw_xdigit=1;
197                 if (ipv4_endp) {
198                     src = ipv4_endp;
199                     continue;
200                 } else {
201                     saw_xdigit=0;
202                     tp += 2;
203                     break;
204                 }
205             }
206         }
207         return 0;
208     }
209     if (saw_xdigit) {
210         if (tp + NS_INT16SZ > endp) {
211             return 0;
212         }
213         *tp++ = (unsigned char) (val >> 8) & 0xff;
214         *tp++ = (unsigned char) val & 0xff;
215     }
216     if (colonp != NULL) {
217         /*
218          * Since some memmove()'s erroneously fail to handle
219          * overlapping regions, we'll do the shift by hand.
220          */
221         const int n = tp - colonp;
222         int i;
223 
224         if (tp == endp) {
225             return 0;
226         }
227         for (i = 1; i <= n; i++) {
228             endp[-i] = colonp[n - i];
229             colonp[n - i] = 0;
230         }
231         tp = endp;
232     }
233 
234     while (tp < endp) {
235         *(tp++) = 0;
236     }
237     memcpy(dst, tmp, NS_IN6ADDRSZ);
238     return 1;
239 }
240