|
160
|
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
|
2 *
|
|
|
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
4 * of this software and associated documentation files (the "Software"), to
|
|
|
5 * deal in the Software without restriction, including without limitation the
|
|
|
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
7 * sell copies of the Software, and to permit persons to whom the Software is
|
|
|
8 * furnished to do so, subject to the following conditions:
|
|
|
9 *
|
|
|
10 * The above copyright notice and this permission notice shall be included in
|
|
|
11 * all copies or substantial portions of the Software.
|
|
|
12 *
|
|
|
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
19 * IN THE SOFTWARE.
|
|
|
20 */
|
|
|
21
|
|
|
22 #include <assert.h>
|
|
|
23
|
|
|
24 #include "uv.h"
|
|
|
25 #include "internal.h"
|
|
|
26 #include "req-inl.h"
|
|
|
27 #include "idna.h"
|
|
|
28
|
|
|
29 /* EAI_* constants. */
|
|
|
30 #include <winsock2.h>
|
|
|
31
|
|
|
32 /* Needed for ConvertInterfaceIndexToLuid and ConvertInterfaceLuidToNameA */
|
|
|
33 #include <iphlpapi.h>
|
|
|
34
|
|
|
35 int uv__getaddrinfo_translate_error(int sys_err) {
|
|
|
36 switch (sys_err) {
|
|
|
37 case 0: return 0;
|
|
|
38 case WSATRY_AGAIN: return UV_EAI_AGAIN;
|
|
|
39 case WSAEINVAL: return UV_EAI_BADFLAGS;
|
|
|
40 case WSANO_RECOVERY: return UV_EAI_FAIL;
|
|
|
41 case WSAEAFNOSUPPORT: return UV_EAI_FAMILY;
|
|
|
42 case WSA_NOT_ENOUGH_MEMORY: return UV_EAI_MEMORY;
|
|
|
43 case WSAHOST_NOT_FOUND: return UV_EAI_NONAME;
|
|
|
44 case WSATYPE_NOT_FOUND: return UV_EAI_SERVICE;
|
|
|
45 case WSAESOCKTNOSUPPORT: return UV_EAI_SOCKTYPE;
|
|
|
46 default: return uv_translate_sys_error(sys_err);
|
|
|
47 }
|
|
|
48 }
|
|
|
49
|
|
|
50
|
|
|
51 /*
|
|
|
52 * MinGW is missing this
|
|
|
53 */
|
|
|
54 #if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)
|
|
|
55 typedef struct addrinfoW {
|
|
|
56 int ai_flags;
|
|
|
57 int ai_family;
|
|
|
58 int ai_socktype;
|
|
|
59 int ai_protocol;
|
|
|
60 size_t ai_addrlen;
|
|
|
61 WCHAR* ai_canonname;
|
|
|
62 struct sockaddr* ai_addr;
|
|
|
63 struct addrinfoW* ai_next;
|
|
|
64 } ADDRINFOW, *PADDRINFOW;
|
|
|
65
|
|
|
66 DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const WCHAR* node,
|
|
|
67 const WCHAR* service,
|
|
|
68 const ADDRINFOW* hints,
|
|
|
69 PADDRINFOW* result);
|
|
|
70
|
|
|
71 DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
|
|
|
72 #endif
|
|
|
73
|
|
|
74 static size_t align_offset(size_t off, size_t alignment) {
|
|
|
75 return ((off + alignment - 1) / alignment) * alignment;
|
|
|
76 }
|
|
|
77
|
|
|
78 #ifndef NDIS_IF_MAX_STRING_SIZE
|
|
|
79 #define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE
|
|
|
80 #endif
|
|
|
81
|
|
|
82 static void uv__getaddrinfo_work(struct uv__work* w) {
|
|
|
83 uv_getaddrinfo_t* req;
|
|
|
84 struct addrinfoW* hints;
|
|
|
85 int err;
|
|
|
86
|
|
|
87 req = container_of(w, uv_getaddrinfo_t, work_req);
|
|
|
88 hints = req->addrinfow;
|
|
|
89 req->addrinfow = NULL;
|
|
|
90 err = GetAddrInfoW(req->node, req->service, hints, &req->addrinfow);
|
|
|
91 req->retcode = uv__getaddrinfo_translate_error(err);
|
|
|
92 }
|
|
|
93
|
|
|
94
|
|
|
95 /*
|
|
|
96 * Called from uv_run when complete. Call user specified callback
|
|
|
97 * then free returned addrinfo
|
|
|
98 * Returned addrinfo strings are converted from UTF-16 to UTF-8.
|
|
|
99 *
|
|
|
100 * To minimize allocation we calculate total size required,
|
|
|
101 * and copy all structs and referenced strings into the one block.
|
|
|
102 * Each size calculation is adjusted to avoid unaligned pointers.
|
|
|
103 */
|
|
|
104 static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
|
|
105 uv_getaddrinfo_t* req = container_of(w, uv_getaddrinfo_t, work_req);
|
|
|
106
|
|
|
107 /* release input parameter memory */
|
|
|
108 uv__free(req->alloc);
|
|
|
109 req->alloc = NULL;
|
|
|
110
|
|
|
111 if (status == UV_ECANCELED) {
|
|
|
112 assert(req->retcode == 0);
|
|
|
113 req->retcode = UV_EAI_CANCELED;
|
|
|
114 goto complete;
|
|
|
115 }
|
|
|
116
|
|
|
117 if (req->retcode == 0) {
|
|
|
118 char* alloc_ptr = NULL;
|
|
|
119 size_t cur_off = 0;
|
|
|
120 size_t addrinfo_len;
|
|
|
121 /* Convert addrinfoW to addrinfo. First calculate required length. */
|
|
|
122 struct addrinfoW* addrinfow_ptr = req->addrinfow;
|
|
|
123 while (addrinfow_ptr != NULL) {
|
|
|
124 cur_off = align_offset(cur_off, sizeof(void*));
|
|
|
125 cur_off += sizeof(struct addrinfo);
|
|
|
126 /* TODO: This alignment could be smaller, if we could
|
|
|
127 portably get the alignment for sockaddr. */
|
|
|
128 cur_off = align_offset(cur_off, sizeof(void*));
|
|
|
129 cur_off += addrinfow_ptr->ai_addrlen;
|
|
|
130 if (addrinfow_ptr->ai_canonname != NULL) {
|
|
|
131 ssize_t name_len =
|
|
|
132 uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1);
|
|
|
133 if (name_len < 0) {
|
|
|
134 req->retcode = name_len;
|
|
|
135 goto complete;
|
|
|
136 }
|
|
|
137 cur_off += name_len + 1;
|
|
|
138 }
|
|
|
139 addrinfow_ptr = addrinfow_ptr->ai_next;
|
|
|
140 }
|
|
|
141
|
|
|
142 /* allocate memory for addrinfo results */
|
|
|
143 addrinfo_len = cur_off;
|
|
|
144 alloc_ptr = uv__malloc(addrinfo_len);
|
|
|
145
|
|
|
146 /* do conversions */
|
|
|
147 if (alloc_ptr != NULL) {
|
|
|
148 struct addrinfo *addrinfo_ptr = (struct addrinfo *)alloc_ptr;
|
|
|
149 cur_off = 0;
|
|
|
150 addrinfow_ptr = req->addrinfow;
|
|
|
151
|
|
|
152 for (;;) {
|
|
|
153 cur_off += sizeof(struct addrinfo);
|
|
|
154 assert(cur_off <= addrinfo_len);
|
|
|
155 /* copy addrinfo struct data */
|
|
|
156 addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
|
|
|
157 addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
|
|
|
158 addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
|
|
|
159 addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags;
|
|
|
160 addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen;
|
|
|
161 addrinfo_ptr->ai_canonname = NULL;
|
|
|
162 addrinfo_ptr->ai_addr = NULL;
|
|
|
163 addrinfo_ptr->ai_next = NULL;
|
|
|
164
|
|
|
165 /* copy sockaddr */
|
|
|
166 if (addrinfo_ptr->ai_addrlen > 0) {
|
|
|
167 cur_off = align_offset(cur_off, sizeof(void *));
|
|
|
168 addrinfo_ptr->ai_addr = (struct sockaddr *)(alloc_ptr + cur_off);
|
|
|
169 cur_off += addrinfo_ptr->ai_addrlen;
|
|
|
170 assert(cur_off <= addrinfo_len);
|
|
|
171 memcpy(addrinfo_ptr->ai_addr,
|
|
|
172 addrinfow_ptr->ai_addr,
|
|
|
173 addrinfo_ptr->ai_addrlen);
|
|
|
174 }
|
|
|
175
|
|
|
176 /* convert canonical name to UTF-8 */
|
|
|
177 if (addrinfow_ptr->ai_canonname != NULL) {
|
|
|
178 ssize_t name_len = addrinfo_len - cur_off;
|
|
|
179 addrinfo_ptr->ai_canonname = alloc_ptr + cur_off;
|
|
|
180 int r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
|
|
|
181 -1,
|
|
|
182 addrinfo_ptr->ai_canonname,
|
|
|
183 (size_t*)&name_len);
|
|
|
184 assert(r == 0);
|
|
|
185 cur_off += name_len + 1;
|
|
|
186 assert(cur_off <= addrinfo_len);
|
|
|
187 }
|
|
|
188
|
|
|
189 /* set next ptr */
|
|
|
190 addrinfow_ptr = addrinfow_ptr->ai_next;
|
|
|
191 if (addrinfow_ptr == NULL)
|
|
|
192 break;
|
|
|
193 cur_off = align_offset(cur_off, sizeof(void *));
|
|
|
194 struct addrinfo *next_addrinfo_ptr = (struct addrinfo *)(alloc_ptr + cur_off);
|
|
|
195 addrinfo_ptr->ai_next = next_addrinfo_ptr;
|
|
|
196 addrinfo_ptr = next_addrinfo_ptr;
|
|
|
197 }
|
|
|
198 req->addrinfo = (struct addrinfo*)alloc_ptr;
|
|
|
199 } else {
|
|
|
200 req->retcode = UV_EAI_MEMORY;
|
|
|
201 }
|
|
|
202 }
|
|
|
203
|
|
|
204 /* return memory to system */
|
|
|
205 if (req->addrinfow != NULL) {
|
|
|
206 FreeAddrInfoW(req->addrinfow);
|
|
|
207 req->addrinfow = NULL;
|
|
|
208 }
|
|
|
209
|
|
|
210 complete:
|
|
|
211 uv__req_unregister(req->loop);
|
|
|
212
|
|
|
213 /* finally do callback with converted result */
|
|
|
214 if (req->getaddrinfo_cb)
|
|
|
215 req->getaddrinfo_cb(req, req->retcode, req->addrinfo);
|
|
|
216 }
|
|
|
217
|
|
|
218
|
|
|
219 void uv_freeaddrinfo(struct addrinfo* ai) {
|
|
|
220 char* alloc_ptr = (char*)ai;
|
|
|
221
|
|
|
222 /* release copied result memory */
|
|
|
223 uv__free(alloc_ptr);
|
|
|
224 }
|
|
|
225
|
|
|
226
|
|
|
227 /*
|
|
|
228 * Entry point for getaddrinfo
|
|
|
229 * we convert the UTF-8 strings to UNICODE
|
|
|
230 * and save the UNICODE string pointers in the req
|
|
|
231 * We also copy hints so that caller does not need to keep memory until the
|
|
|
232 * callback.
|
|
|
233 * return 0 if a callback will be made
|
|
|
234 * return error code if validation fails
|
|
|
235 *
|
|
|
236 * To minimize allocation we calculate total size required,
|
|
|
237 * and copy all structs and referenced strings into the one block.
|
|
|
238 * Each size calculation is adjusted to avoid unaligned pointers.
|
|
|
239 */
|
|
|
240 int uv_getaddrinfo(uv_loop_t* loop,
|
|
|
241 uv_getaddrinfo_t* req,
|
|
|
242 uv_getaddrinfo_cb getaddrinfo_cb,
|
|
|
243 const char* node,
|
|
|
244 const char* service,
|
|
|
245 const struct addrinfo* hints) {
|
|
|
246 char hostname_ascii[256];
|
|
|
247 size_t off = 0;
|
|
|
248 size_t nodesize = 0;
|
|
|
249 size_t servicesize = 0;
|
|
|
250 size_t serviceoff = 0;
|
|
|
251 size_t hintssize = 0;
|
|
|
252 size_t hintoff = 0;
|
|
|
253 ssize_t rc;
|
|
|
254
|
|
|
255 if (req == NULL || (node == NULL && service == NULL)) {
|
|
|
256 return UV_EINVAL;
|
|
|
257 }
|
|
|
258
|
|
|
259 UV_REQ_INIT(req, UV_GETADDRINFO);
|
|
|
260 req->getaddrinfo_cb = getaddrinfo_cb;
|
|
|
261 req->addrinfo = NULL;
|
|
|
262 req->loop = loop;
|
|
|
263 req->retcode = 0;
|
|
|
264
|
|
|
265 /* calculate required memory size for all input values */
|
|
|
266 if (node != NULL) {
|
|
|
267 rc = uv__idna_toascii(node,
|
|
|
268 node + strlen(node),
|
|
|
269 hostname_ascii,
|
|
|
270 hostname_ascii + sizeof(hostname_ascii));
|
|
|
271 if (rc < 0)
|
|
|
272 return rc;
|
|
|
273 nodesize = strlen(hostname_ascii) + 1;
|
|
|
274 node = hostname_ascii;
|
|
|
275 off += nodesize * sizeof(WCHAR);
|
|
|
276 }
|
|
|
277
|
|
|
278 if (service != NULL) {
|
|
|
279 rc = uv_wtf8_length_as_utf16(service);
|
|
|
280 if (rc < 0)
|
|
|
281 return rc;
|
|
|
282 servicesize = rc;
|
|
|
283 off = align_offset(off, sizeof(WCHAR));
|
|
|
284 serviceoff = off;
|
|
|
285 off += servicesize * sizeof(WCHAR);
|
|
|
286 }
|
|
|
287
|
|
|
288 if (hints != NULL) {
|
|
|
289 off = align_offset(off, sizeof(void *));
|
|
|
290 hintoff = off;
|
|
|
291 hintssize = sizeof(struct addrinfoW);
|
|
|
292 off += hintssize;
|
|
|
293 }
|
|
|
294
|
|
|
295 /* allocate memory for inputs, and partition it as needed */
|
|
|
296 req->alloc = uv__malloc(off);
|
|
|
297 if (!req->alloc)
|
|
|
298 return UV_ENOMEM;
|
|
|
299
|
|
|
300 /* Convert node string to UTF16 into allocated memory and save pointer in the
|
|
|
301 * request. The node here has been converted to ascii. */
|
|
|
302 if (node != NULL) {
|
|
|
303 req->node = (WCHAR*) req->alloc;
|
|
|
304 uv_wtf8_to_utf16(node, req->node, nodesize);
|
|
|
305 } else {
|
|
|
306 req->node = NULL;
|
|
|
307 }
|
|
|
308
|
|
|
309 /* Convert service string to UTF16 into allocated memory and save pointer in
|
|
|
310 * the req. */
|
|
|
311 if (service != NULL) {
|
|
|
312 req->service = (WCHAR*) ((char*) req->alloc + serviceoff);
|
|
|
313 uv_wtf8_to_utf16(service, req->service, servicesize);
|
|
|
314 } else {
|
|
|
315 req->service = NULL;
|
|
|
316 }
|
|
|
317
|
|
|
318 /* copy hints to allocated memory and save pointer in req */
|
|
|
319 if (hints != NULL) {
|
|
|
320 req->addrinfow = (struct addrinfoW*) ((char*) req->alloc + hintoff);
|
|
|
321 req->addrinfow->ai_family = hints->ai_family;
|
|
|
322 req->addrinfow->ai_socktype = hints->ai_socktype;
|
|
|
323 req->addrinfow->ai_protocol = hints->ai_protocol;
|
|
|
324 req->addrinfow->ai_flags = hints->ai_flags;
|
|
|
325 req->addrinfow->ai_addrlen = 0;
|
|
|
326 req->addrinfow->ai_canonname = NULL;
|
|
|
327 req->addrinfow->ai_addr = NULL;
|
|
|
328 req->addrinfow->ai_next = NULL;
|
|
|
329 } else {
|
|
|
330 req->addrinfow = NULL;
|
|
|
331 }
|
|
|
332
|
|
|
333 uv__req_register(loop);
|
|
|
334
|
|
|
335 if (getaddrinfo_cb) {
|
|
|
336 uv__work_submit(loop,
|
|
|
337 &req->work_req,
|
|
|
338 UV__WORK_SLOW_IO,
|
|
|
339 uv__getaddrinfo_work,
|
|
|
340 uv__getaddrinfo_done);
|
|
|
341 return 0;
|
|
|
342 } else {
|
|
|
343 uv__getaddrinfo_work(&req->work_req);
|
|
|
344 uv__getaddrinfo_done(&req->work_req, 0);
|
|
|
345 return req->retcode;
|
|
|
346 }
|
|
|
347 }
|
|
|
348
|
|
|
349 int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
|
|
|
350 NET_LUID luid;
|
|
|
351 wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */
|
|
|
352 int r;
|
|
|
353
|
|
|
354 if (buffer == NULL || size == NULL || *size == 0)
|
|
|
355 return UV_EINVAL;
|
|
|
356
|
|
|
357 r = ConvertInterfaceIndexToLuid(ifindex, &luid);
|
|
|
358
|
|
|
359 if (r != 0)
|
|
|
360 return uv_translate_sys_error(r);
|
|
|
361
|
|
|
362 r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));
|
|
|
363
|
|
|
364 if (r != 0)
|
|
|
365 return uv_translate_sys_error(r);
|
|
|
366
|
|
|
367 return uv__copy_utf16_to_utf8(wname, -1, buffer, size);
|
|
|
368 }
|
|
|
369
|
|
|
370 int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
|
|
|
371 int r;
|
|
|
372
|
|
|
373 if (buffer == NULL || size == NULL || *size == 0)
|
|
|
374 return UV_EINVAL;
|
|
|
375
|
|
|
376 r = snprintf(buffer, *size, "%d", ifindex);
|
|
|
377
|
|
|
378 if (r < 0)
|
|
|
379 return uv_translate_sys_error(r);
|
|
|
380
|
|
|
381 if (r >= (int) *size) {
|
|
|
382 *size = r + 1;
|
|
|
383 return UV_ENOBUFS;
|
|
|
384 }
|
|
|
385
|
|
|
386 *size = r;
|
|
|
387 return 0;
|
|
|
388 }
|