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 } 389