1 | /* |
2 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") |
3 | * Copyright (c) 1996-1999 by Internet Software Consortium. |
4 | * |
5 | * Permission to use, copy, modify, and 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 |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
15 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | */ |
17 | |
18 | #include <stdio.h> |
19 | #include <string.h> |
20 | |
21 | #if defined(_MSC_VER) && _MSC_VER < 1600 |
22 | # include "uv/stdint-msvc2008.h" |
23 | #else |
24 | # include <stdint.h> |
25 | #endif |
26 | |
27 | #include "uv.h" |
28 | #include "uv-common.h" |
29 | |
30 | #define UV__INET_ADDRSTRLEN 16 |
31 | #define UV__INET6_ADDRSTRLEN 46 |
32 | |
33 | |
34 | static int inet_ntop4(const unsigned char *src, char *dst, size_t size); |
35 | static int inet_ntop6(const unsigned char *src, char *dst, size_t size); |
36 | static int inet_pton4(const char *src, unsigned char *dst); |
37 | static int inet_pton6(const char *src, unsigned char *dst); |
38 | |
39 | |
40 | int uv_inet_ntop(int af, const void* src, char* dst, size_t size) { |
41 | switch (af) { |
42 | case AF_INET: |
43 | return (inet_ntop4(src, dst, size)); |
44 | case AF_INET6: |
45 | return (inet_ntop6(src, dst, size)); |
46 | default: |
47 | return UV_EAFNOSUPPORT; |
48 | } |
49 | /* NOTREACHED */ |
50 | } |
51 | |
52 | |
53 | static int inet_ntop4(const unsigned char *src, char *dst, size_t size) { |
54 | static const char fmt[] = "%u.%u.%u.%u" ; |
55 | char tmp[UV__INET_ADDRSTRLEN]; |
56 | int l; |
57 | |
58 | l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); |
59 | if (l <= 0 || (size_t) l >= size) { |
60 | return UV_ENOSPC; |
61 | } |
62 | uv__strscpy(dst, tmp, size); |
63 | return 0; |
64 | } |
65 | |
66 | |
67 | static int inet_ntop6(const unsigned char *src, char *dst, size_t size) { |
68 | /* |
69 | * Note that int32_t and int16_t need only be "at least" large enough |
70 | * to contain a value of the specified size. On some systems, like |
71 | * Crays, there is no such thing as an integer variable with 16 bits. |
72 | * Keep this in mind if you think this function should have been coded |
73 | * to use pointer overlays. All the world's not a VAX. |
74 | */ |
75 | char tmp[UV__INET6_ADDRSTRLEN], *tp; |
76 | struct { int base, len; } best, cur; |
77 | unsigned int words[sizeof(struct in6_addr) / sizeof(uint16_t)]; |
78 | int i; |
79 | |
80 | /* |
81 | * Preprocess: |
82 | * Copy the input (bytewise) array into a wordwise array. |
83 | * Find the longest run of 0x00's in src[] for :: shorthanding. |
84 | */ |
85 | memset(words, '\0', sizeof words); |
86 | for (i = 0; i < (int) sizeof(struct in6_addr); i++) |
87 | words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); |
88 | best.base = -1; |
89 | best.len = 0; |
90 | cur.base = -1; |
91 | cur.len = 0; |
92 | for (i = 0; i < (int) ARRAY_SIZE(words); i++) { |
93 | if (words[i] == 0) { |
94 | if (cur.base == -1) |
95 | cur.base = i, cur.len = 1; |
96 | else |
97 | cur.len++; |
98 | } else { |
99 | if (cur.base != -1) { |
100 | if (best.base == -1 || cur.len > best.len) |
101 | best = cur; |
102 | cur.base = -1; |
103 | } |
104 | } |
105 | } |
106 | if (cur.base != -1) { |
107 | if (best.base == -1 || cur.len > best.len) |
108 | best = cur; |
109 | } |
110 | if (best.base != -1 && best.len < 2) |
111 | best.base = -1; |
112 | |
113 | /* |
114 | * Format the result. |
115 | */ |
116 | tp = tmp; |
117 | for (i = 0; i < (int) ARRAY_SIZE(words); i++) { |
118 | /* Are we inside the best run of 0x00's? */ |
119 | if (best.base != -1 && i >= best.base && |
120 | i < (best.base + best.len)) { |
121 | if (i == best.base) |
122 | *tp++ = ':'; |
123 | continue; |
124 | } |
125 | /* Are we following an initial run of 0x00s or any real hex? */ |
126 | if (i != 0) |
127 | *tp++ = ':'; |
128 | /* Is this address an encapsulated IPv4? */ |
129 | if (i == 6 && best.base == 0 && (best.len == 6 || |
130 | (best.len == 7 && words[7] != 0x0001) || |
131 | (best.len == 5 && words[5] == 0xffff))) { |
132 | int err = inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)); |
133 | if (err) |
134 | return err; |
135 | tp += strlen(tp); |
136 | break; |
137 | } |
138 | tp += sprintf(tp, "%x" , words[i]); |
139 | } |
140 | /* Was it a trailing run of 0x00's? */ |
141 | if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words)) |
142 | *tp++ = ':'; |
143 | *tp++ = '\0'; |
144 | if (UV_E2BIG == uv__strscpy(dst, tmp, size)) |
145 | return UV_ENOSPC; |
146 | return 0; |
147 | } |
148 | |
149 | |
150 | int uv_inet_pton(int af, const char* src, void* dst) { |
151 | if (src == NULL || dst == NULL) |
152 | return UV_EINVAL; |
153 | |
154 | switch (af) { |
155 | case AF_INET: |
156 | return (inet_pton4(src, dst)); |
157 | case AF_INET6: { |
158 | int len; |
159 | char tmp[UV__INET6_ADDRSTRLEN], *s, *p; |
160 | s = (char*) src; |
161 | p = strchr(src, '%'); |
162 | if (p != NULL) { |
163 | s = tmp; |
164 | len = p - src; |
165 | if (len > UV__INET6_ADDRSTRLEN-1) |
166 | return UV_EINVAL; |
167 | memcpy(s, src, len); |
168 | s[len] = '\0'; |
169 | } |
170 | return inet_pton6(s, dst); |
171 | } |
172 | default: |
173 | return UV_EAFNOSUPPORT; |
174 | } |
175 | /* NOTREACHED */ |
176 | } |
177 | |
178 | |
179 | static int inet_pton4(const char *src, unsigned char *dst) { |
180 | static const char digits[] = "0123456789" ; |
181 | int saw_digit, octets, ch; |
182 | unsigned char tmp[sizeof(struct in_addr)], *tp; |
183 | |
184 | saw_digit = 0; |
185 | octets = 0; |
186 | *(tp = tmp) = 0; |
187 | while ((ch = *src++) != '\0') { |
188 | const char *pch; |
189 | |
190 | if ((pch = strchr(digits, ch)) != NULL) { |
191 | unsigned int nw = *tp * 10 + (pch - digits); |
192 | |
193 | if (saw_digit && *tp == 0) |
194 | return UV_EINVAL; |
195 | if (nw > 255) |
196 | return UV_EINVAL; |
197 | *tp = nw; |
198 | if (!saw_digit) { |
199 | if (++octets > 4) |
200 | return UV_EINVAL; |
201 | saw_digit = 1; |
202 | } |
203 | } else if (ch == '.' && saw_digit) { |
204 | if (octets == 4) |
205 | return UV_EINVAL; |
206 | *++tp = 0; |
207 | saw_digit = 0; |
208 | } else |
209 | return UV_EINVAL; |
210 | } |
211 | if (octets < 4) |
212 | return UV_EINVAL; |
213 | memcpy(dst, tmp, sizeof(struct in_addr)); |
214 | return 0; |
215 | } |
216 | |
217 | |
218 | static int inet_pton6(const char *src, unsigned char *dst) { |
219 | static const char xdigits_l[] = "0123456789abcdef" , |
220 | xdigits_u[] = "0123456789ABCDEF" ; |
221 | unsigned char tmp[sizeof(struct in6_addr)], *tp, *endp, *colonp; |
222 | const char *xdigits, *curtok; |
223 | int ch, seen_xdigits; |
224 | unsigned int val; |
225 | |
226 | memset((tp = tmp), '\0', sizeof tmp); |
227 | endp = tp + sizeof tmp; |
228 | colonp = NULL; |
229 | /* Leading :: requires some special handling. */ |
230 | if (*src == ':') |
231 | if (*++src != ':') |
232 | return UV_EINVAL; |
233 | curtok = src; |
234 | seen_xdigits = 0; |
235 | val = 0; |
236 | while ((ch = *src++) != '\0') { |
237 | const char *pch; |
238 | |
239 | if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) |
240 | pch = strchr((xdigits = xdigits_u), ch); |
241 | if (pch != NULL) { |
242 | val <<= 4; |
243 | val |= (pch - xdigits); |
244 | if (++seen_xdigits > 4) |
245 | return UV_EINVAL; |
246 | continue; |
247 | } |
248 | if (ch == ':') { |
249 | curtok = src; |
250 | if (!seen_xdigits) { |
251 | if (colonp) |
252 | return UV_EINVAL; |
253 | colonp = tp; |
254 | continue; |
255 | } else if (*src == '\0') { |
256 | return UV_EINVAL; |
257 | } |
258 | if (tp + sizeof(uint16_t) > endp) |
259 | return UV_EINVAL; |
260 | *tp++ = (unsigned char) (val >> 8) & 0xff; |
261 | *tp++ = (unsigned char) val & 0xff; |
262 | seen_xdigits = 0; |
263 | val = 0; |
264 | continue; |
265 | } |
266 | if (ch == '.' && ((tp + sizeof(struct in_addr)) <= endp)) { |
267 | int err = inet_pton4(curtok, tp); |
268 | if (err == 0) { |
269 | tp += sizeof(struct in_addr); |
270 | seen_xdigits = 0; |
271 | break; /*%< '\\0' was seen by inet_pton4(). */ |
272 | } |
273 | } |
274 | return UV_EINVAL; |
275 | } |
276 | if (seen_xdigits) { |
277 | if (tp + sizeof(uint16_t) > endp) |
278 | return UV_EINVAL; |
279 | *tp++ = (unsigned char) (val >> 8) & 0xff; |
280 | *tp++ = (unsigned char) val & 0xff; |
281 | } |
282 | if (colonp != NULL) { |
283 | /* |
284 | * Since some memmove()'s erroneously fail to handle |
285 | * overlapping regions, we'll do the shift by hand. |
286 | */ |
287 | const int n = tp - colonp; |
288 | int i; |
289 | |
290 | if (tp == endp) |
291 | return UV_EINVAL; |
292 | for (i = 1; i <= n; i++) { |
293 | endp[- i] = colonp[n - i]; |
294 | colonp[n - i] = 0; |
295 | } |
296 | tp = endp; |
297 | } |
298 | if (tp != endp) |
299 | return UV_EINVAL; |
300 | memcpy(dst, tmp, sizeof tmp); |
301 | return 0; |
302 | } |
303 | |