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
uv__getaddrinfo_translate_error(int sys_err)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
75 /* Adjust size value to be multiple of 4. Use to keep pointer aligned.
76 * Do we need different versions of this for different architectures? */
77 #define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2)
78
79 #ifndef NDIS_IF_MAX_STRING_SIZE
80 #define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE
81 #endif
82
uv__getaddrinfo_work(struct uv__work * w)83 static void uv__getaddrinfo_work(struct uv__work* w) {
84 uv_getaddrinfo_t* req;
85 struct addrinfoW* hints;
86 int err;
87
88 req = container_of(w, uv_getaddrinfo_t, work_req);
89 hints = req->addrinfow;
90 req->addrinfow = NULL;
91 err = GetAddrInfoW(req->node, req->service, hints, &req->addrinfow);
92 req->retcode = uv__getaddrinfo_translate_error(err);
93 }
94
95
96 /*
97 * Called from uv_run when complete. Call user specified callback
98 * then free returned addrinfo
99 * Returned addrinfo strings are converted from UTF-16 to UTF-8.
100 *
101 * To minimize allocation we calculate total size required,
102 * and copy all structs and referenced strings into the one block.
103 * Each size calculation is adjusted to avoid unaligned pointers.
104 */
uv__getaddrinfo_done(struct uv__work * w,int status)105 static void uv__getaddrinfo_done(struct uv__work* w, int status) {
106 uv_getaddrinfo_t* req;
107 int addrinfo_len = 0;
108 int name_len = 0;
109 size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo));
110 struct addrinfoW* addrinfow_ptr;
111 struct addrinfo* addrinfo_ptr;
112 char* alloc_ptr = NULL;
113 char* cur_ptr = NULL;
114
115 req = container_of(w, uv_getaddrinfo_t, work_req);
116
117 /* release input parameter memory */
118 uv__free(req->alloc);
119 req->alloc = NULL;
120
121 if (status == UV_ECANCELED) {
122 assert(req->retcode == 0);
123 req->retcode = UV_EAI_CANCELED;
124 goto complete;
125 }
126
127 if (req->retcode == 0) {
128 /* Convert addrinfoW to addrinfo. First calculate required length. */
129 addrinfow_ptr = req->addrinfow;
130 while (addrinfow_ptr != NULL) {
131 addrinfo_len += addrinfo_struct_len +
132 ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
133 if (addrinfow_ptr->ai_canonname != NULL) {
134 name_len = WideCharToMultiByte(CP_UTF8,
135 0,
136 addrinfow_ptr->ai_canonname,
137 -1,
138 NULL,
139 0,
140 NULL,
141 NULL);
142 if (name_len == 0) {
143 req->retcode = uv_translate_sys_error(GetLastError());
144 goto complete;
145 }
146 addrinfo_len += ALIGNED_SIZE(name_len);
147 }
148 addrinfow_ptr = addrinfow_ptr->ai_next;
149 }
150
151 /* allocate memory for addrinfo results */
152 alloc_ptr = (char*)uv__malloc(addrinfo_len);
153
154 /* do conversions */
155 if (alloc_ptr != NULL) {
156 cur_ptr = alloc_ptr;
157 addrinfow_ptr = req->addrinfow;
158
159 while (addrinfow_ptr != NULL) {
160 /* copy addrinfo struct data */
161 assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len);
162 addrinfo_ptr = (struct addrinfo*)cur_ptr;
163 addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
164 addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
165 addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
166 addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags;
167 addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen;
168 addrinfo_ptr->ai_canonname = NULL;
169 addrinfo_ptr->ai_addr = NULL;
170 addrinfo_ptr->ai_next = NULL;
171
172 cur_ptr += addrinfo_struct_len;
173
174 /* copy sockaddr */
175 if (addrinfo_ptr->ai_addrlen > 0) {
176 assert(cur_ptr + addrinfo_ptr->ai_addrlen <=
177 alloc_ptr + addrinfo_len);
178 memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);
179 addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;
180 cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);
181 }
182
183 /* convert canonical name to UTF-8 */
184 if (addrinfow_ptr->ai_canonname != NULL) {
185 name_len = WideCharToMultiByte(CP_UTF8,
186 0,
187 addrinfow_ptr->ai_canonname,
188 -1,
189 NULL,
190 0,
191 NULL,
192 NULL);
193 assert(name_len > 0);
194 assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len);
195 name_len = WideCharToMultiByte(CP_UTF8,
196 0,
197 addrinfow_ptr->ai_canonname,
198 -1,
199 cur_ptr,
200 name_len,
201 NULL,
202 NULL);
203 assert(name_len > 0);
204 addrinfo_ptr->ai_canonname = cur_ptr;
205 cur_ptr += ALIGNED_SIZE(name_len);
206 }
207 assert(cur_ptr <= alloc_ptr + addrinfo_len);
208
209 /* set next ptr */
210 addrinfow_ptr = addrinfow_ptr->ai_next;
211 if (addrinfow_ptr != NULL) {
212 addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;
213 }
214 }
215 req->addrinfo = (struct addrinfo*)alloc_ptr;
216 } else {
217 req->retcode = UV_EAI_MEMORY;
218 }
219 }
220
221 /* return memory to system */
222 if (req->addrinfow != NULL) {
223 FreeAddrInfoW(req->addrinfow);
224 req->addrinfow = NULL;
225 }
226
227 complete:
228 uv__req_unregister(req->loop, req);
229
230 /* finally do callback with converted result */
231 if (req->getaddrinfo_cb)
232 req->getaddrinfo_cb(req, req->retcode, req->addrinfo);
233 }
234
235
uv_freeaddrinfo(struct addrinfo * ai)236 void uv_freeaddrinfo(struct addrinfo* ai) {
237 char* alloc_ptr = (char*)ai;
238
239 /* release copied result memory */
240 uv__free(alloc_ptr);
241 }
242
243
244 /*
245 * Entry point for getaddrinfo
246 * we convert the UTF-8 strings to UNICODE
247 * and save the UNICODE string pointers in the req
248 * We also copy hints so that caller does not need to keep memory until the
249 * callback.
250 * return 0 if a callback will be made
251 * return error code if validation fails
252 *
253 * To minimize allocation we calculate total size required,
254 * and copy all structs and referenced strings into the one block.
255 * Each size calculation is adjusted to avoid unaligned pointers.
256 */
uv_getaddrinfo(uv_loop_t * loop,uv_getaddrinfo_t * req,uv_getaddrinfo_cb getaddrinfo_cb,const char * node,const char * service,const struct addrinfo * hints)257 int uv_getaddrinfo(uv_loop_t* loop,
258 uv_getaddrinfo_t* req,
259 uv_getaddrinfo_cb getaddrinfo_cb,
260 const char* node,
261 const char* service,
262 const struct addrinfo* hints) {
263 char hostname_ascii[256];
264 int nodesize = 0;
265 int servicesize = 0;
266 int hintssize = 0;
267 char* alloc_ptr = NULL;
268 int err;
269 long rc;
270
271 if (req == NULL || (node == NULL && service == NULL)) {
272 return UV_EINVAL;
273 }
274
275 UV_REQ_INIT(req, UV_GETADDRINFO);
276 req->getaddrinfo_cb = getaddrinfo_cb;
277 req->addrinfo = NULL;
278 req->loop = loop;
279 req->retcode = 0;
280
281 /* calculate required memory size for all input values */
282 if (node != NULL) {
283 rc = uv__idna_toascii(node,
284 node + strlen(node),
285 hostname_ascii,
286 hostname_ascii + sizeof(hostname_ascii));
287 if (rc < 0)
288 return rc;
289 nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, hostname_ascii,
290 -1, NULL, 0) * sizeof(WCHAR));
291 if (nodesize == 0) {
292 err = GetLastError();
293 goto error;
294 }
295 node = hostname_ascii;
296 }
297
298 if (service != NULL) {
299 servicesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8,
300 0,
301 service,
302 -1,
303 NULL,
304 0) *
305 sizeof(WCHAR));
306 if (servicesize == 0) {
307 err = GetLastError();
308 goto error;
309 }
310 }
311 if (hints != NULL) {
312 hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));
313 }
314
315 /* allocate memory for inputs, and partition it as needed */
316 alloc_ptr = (char*)uv__malloc(nodesize + servicesize + hintssize);
317 if (!alloc_ptr) {
318 err = WSAENOBUFS;
319 goto error;
320 }
321
322 /* save alloc_ptr now so we can free if error */
323 req->alloc = (void*)alloc_ptr;
324
325 /* Convert node string to UTF16 into allocated memory and save pointer in the
326 * request. */
327 if (node != NULL) {
328 req->node = (WCHAR*)alloc_ptr;
329 if (MultiByteToWideChar(CP_UTF8,
330 0,
331 node,
332 -1,
333 (WCHAR*) alloc_ptr,
334 nodesize / sizeof(WCHAR)) == 0) {
335 err = GetLastError();
336 goto error;
337 }
338 alloc_ptr += nodesize;
339 } else {
340 req->node = NULL;
341 }
342
343 /* Convert service string to UTF16 into allocated memory and save pointer in
344 * the req. */
345 if (service != NULL) {
346 req->service = (WCHAR*)alloc_ptr;
347 if (MultiByteToWideChar(CP_UTF8,
348 0,
349 service,
350 -1,
351 (WCHAR*) alloc_ptr,
352 servicesize / sizeof(WCHAR)) == 0) {
353 err = GetLastError();
354 goto error;
355 }
356 alloc_ptr += servicesize;
357 } else {
358 req->service = NULL;
359 }
360
361 /* copy hints to allocated memory and save pointer in req */
362 if (hints != NULL) {
363 req->addrinfow = (struct addrinfoW*)alloc_ptr;
364 req->addrinfow->ai_family = hints->ai_family;
365 req->addrinfow->ai_socktype = hints->ai_socktype;
366 req->addrinfow->ai_protocol = hints->ai_protocol;
367 req->addrinfow->ai_flags = hints->ai_flags;
368 req->addrinfow->ai_addrlen = 0;
369 req->addrinfow->ai_canonname = NULL;
370 req->addrinfow->ai_addr = NULL;
371 req->addrinfow->ai_next = NULL;
372 } else {
373 req->addrinfow = NULL;
374 }
375
376 uv__req_register(loop, req);
377
378 if (getaddrinfo_cb) {
379 uv__work_submit(loop,
380 &req->work_req,
381 UV__WORK_SLOW_IO,
382 uv__getaddrinfo_work,
383 uv__getaddrinfo_done);
384 return 0;
385 } else {
386 uv__getaddrinfo_work(&req->work_req);
387 uv__getaddrinfo_done(&req->work_req, 0);
388 return req->retcode;
389 }
390
391 error:
392 if (req != NULL) {
393 uv__free(req->alloc);
394 req->alloc = NULL;
395 }
396 return uv_translate_sys_error(err);
397 }
398
uv_if_indextoname(unsigned int ifindex,char * buffer,size_t * size)399 int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
400 NET_LUID luid;
401 wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */
402 DWORD bufsize;
403 int r;
404
405 if (buffer == NULL || size == NULL || *size == 0)
406 return UV_EINVAL;
407
408 r = ConvertInterfaceIndexToLuid(ifindex, &luid);
409
410 if (r != 0)
411 return uv_translate_sys_error(r);
412
413 r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));
414
415 if (r != 0)
416 return uv_translate_sys_error(r);
417
418 /* Check how much space we need */
419 bufsize = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL);
420
421 if (bufsize == 0) {
422 return uv_translate_sys_error(GetLastError());
423 } else if (bufsize > *size) {
424 *size = bufsize;
425 return UV_ENOBUFS;
426 }
427
428 /* Convert to UTF-8 */
429 bufsize = WideCharToMultiByte(CP_UTF8,
430 0,
431 wname,
432 -1,
433 buffer,
434 *size,
435 NULL,
436 NULL);
437
438 if (bufsize == 0)
439 return uv_translate_sys_error(GetLastError());
440
441 *size = bufsize - 1;
442 return 0;
443 }
444
uv_if_indextoiid(unsigned int ifindex,char * buffer,size_t * size)445 int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
446 int r;
447
448 if (buffer == NULL || size == NULL || *size == 0)
449 return UV_EINVAL;
450
451 r = snprintf(buffer, *size, "%d", ifindex);
452
453 if (r < 0)
454 return uv_translate_sys_error(r);
455
456 if (r >= (int) *size) {
457 *size = r + 1;
458 return UV_ENOBUFS;
459 }
460
461 *size = r;
462 return 0;
463 }
464