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 35int 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 83static 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 */ 105static 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 227complete: 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 236void 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 */ 257int 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 391error: 392 if (req != NULL) { 393 uv__free(req->alloc); 394 req->alloc = NULL; 395 } 396 return uv_translate_sys_error(err); 397} 398 399int 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 445int 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