Path: blob/master/Utilities/cmlibuv/src/win/getaddrinfo.c
3153 views
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.1*2* Permission is hereby granted, free of charge, to any person obtaining a copy3* of this software and associated documentation files (the "Software"), to4* deal in the Software without restriction, including without limitation the5* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or6* sell copies of the Software, and to permit persons to whom the Software is7* furnished to do so, subject to the following conditions:8*9* The above copyright notice and this permission notice shall be included in10* all copies or substantial portions of the Software.11*12* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR13* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,14* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE15* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER16* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING17* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS18* IN THE SOFTWARE.19*/2021#include <assert.h>2223#include "uv.h"24#include "internal.h"25#include "req-inl.h"26#include "idna.h"2728/* EAI_* constants. */29#include <winsock2.h>3031/* Needed for ConvertInterfaceIndexToLuid and ConvertInterfaceLuidToNameA */32#include <iphlpapi.h>3334int uv__getaddrinfo_translate_error(int sys_err) {35switch (sys_err) {36case 0: return 0;37case WSATRY_AGAIN: return UV_EAI_AGAIN;38case WSAEINVAL: return UV_EAI_BADFLAGS;39case WSANO_RECOVERY: return UV_EAI_FAIL;40case WSAEAFNOSUPPORT: return UV_EAI_FAMILY;41case WSA_NOT_ENOUGH_MEMORY: return UV_EAI_MEMORY;42case WSAHOST_NOT_FOUND: return UV_EAI_NONAME;43case WSATYPE_NOT_FOUND: return UV_EAI_SERVICE;44case WSAESOCKTNOSUPPORT: return UV_EAI_SOCKTYPE;45default: return uv_translate_sys_error(sys_err);46}47}484950/*51* MinGW is missing this52*/53#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)54typedef struct addrinfoW {55int ai_flags;56int ai_family;57int ai_socktype;58int ai_protocol;59size_t ai_addrlen;60WCHAR* ai_canonname;61struct sockaddr* ai_addr;62struct addrinfoW* ai_next;63} ADDRINFOW, *PADDRINFOW;6465DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const WCHAR* node,66const WCHAR* service,67const ADDRINFOW* hints,68PADDRINFOW* result);6970DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);71#endif727374/* Adjust size value to be multiple of 4. Use to keep pointer aligned.75* Do we need different versions of this for different architectures? */76#define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2)7778#ifndef NDIS_IF_MAX_STRING_SIZE79#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE80#endif8182static void uv__getaddrinfo_work(struct uv__work* w) {83uv_getaddrinfo_t* req;84struct addrinfoW* hints;85int err;8687req = container_of(w, uv_getaddrinfo_t, work_req);88hints = req->addrinfow;89req->addrinfow = NULL;90err = GetAddrInfoW(req->node, req->service, hints, &req->addrinfow);91req->retcode = uv__getaddrinfo_translate_error(err);92}939495/*96* Called from uv_run when complete. Call user specified callback97* then free returned addrinfo98* 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*/104static void uv__getaddrinfo_done(struct uv__work* w, int status) {105uv_getaddrinfo_t* req;106int addrinfo_len = 0;107int name_len = 0;108size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo));109struct addrinfoW* addrinfow_ptr;110struct addrinfo* addrinfo_ptr;111char* alloc_ptr = NULL;112char* cur_ptr = NULL;113114req = container_of(w, uv_getaddrinfo_t, work_req);115116/* release input parameter memory */117uv__free(req->alloc);118req->alloc = NULL;119120if (status == UV_ECANCELED) {121assert(req->retcode == 0);122req->retcode = UV_EAI_CANCELED;123goto complete;124}125126if (req->retcode == 0) {127/* Convert addrinfoW to addrinfo. First calculate required length. */128addrinfow_ptr = req->addrinfow;129while (addrinfow_ptr != NULL) {130addrinfo_len += addrinfo_struct_len +131ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);132if (addrinfow_ptr->ai_canonname != NULL) {133name_len = WideCharToMultiByte(CP_UTF8,1340,135addrinfow_ptr->ai_canonname,136-1,137NULL,1380,139NULL,140NULL);141if (name_len == 0) {142req->retcode = uv_translate_sys_error(GetLastError());143goto complete;144}145addrinfo_len += ALIGNED_SIZE(name_len);146}147addrinfow_ptr = addrinfow_ptr->ai_next;148}149150/* allocate memory for addrinfo results */151alloc_ptr = (char*)uv__malloc(addrinfo_len);152153/* do conversions */154if (alloc_ptr != NULL) {155cur_ptr = alloc_ptr;156addrinfow_ptr = req->addrinfow;157158while (addrinfow_ptr != NULL) {159/* copy addrinfo struct data */160assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len);161addrinfo_ptr = (struct addrinfo*)cur_ptr;162addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;163addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;164addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;165addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags;166addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen;167addrinfo_ptr->ai_canonname = NULL;168addrinfo_ptr->ai_addr = NULL;169addrinfo_ptr->ai_next = NULL;170171cur_ptr += addrinfo_struct_len;172173/* copy sockaddr */174if (addrinfo_ptr->ai_addrlen > 0) {175assert(cur_ptr + addrinfo_ptr->ai_addrlen <=176alloc_ptr + addrinfo_len);177memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);178addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;179cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);180}181182/* convert canonical name to UTF-8 */183if (addrinfow_ptr->ai_canonname != NULL) {184name_len = WideCharToMultiByte(CP_UTF8,1850,186addrinfow_ptr->ai_canonname,187-1,188NULL,1890,190NULL,191NULL);192assert(name_len > 0);193assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len);194name_len = WideCharToMultiByte(CP_UTF8,1950,196addrinfow_ptr->ai_canonname,197-1,198cur_ptr,199name_len,200NULL,201NULL);202assert(name_len > 0);203addrinfo_ptr->ai_canonname = cur_ptr;204cur_ptr += ALIGNED_SIZE(name_len);205}206assert(cur_ptr <= alloc_ptr + addrinfo_len);207208/* set next ptr */209addrinfow_ptr = addrinfow_ptr->ai_next;210if (addrinfow_ptr != NULL) {211addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;212}213}214req->addrinfo = (struct addrinfo*)alloc_ptr;215} else {216req->retcode = UV_EAI_MEMORY;217}218}219220/* return memory to system */221if (req->addrinfow != NULL) {222FreeAddrInfoW(req->addrinfow);223req->addrinfow = NULL;224}225226complete:227uv__req_unregister(req->loop, req);228229/* finally do callback with converted result */230if (req->getaddrinfo_cb)231req->getaddrinfo_cb(req, req->retcode, req->addrinfo);232}233234235void uv_freeaddrinfo(struct addrinfo* ai) {236char* alloc_ptr = (char*)ai;237238/* release copied result memory */239uv__free(alloc_ptr);240}241242243/*244* Entry point for getaddrinfo245* we convert the UTF-8 strings to UNICODE246* and save the UNICODE string pointers in the req247* We also copy hints so that caller does not need to keep memory until the248* callback.249* return 0 if a callback will be made250* return error code if validation fails251*252* To minimize allocation we calculate total size required,253* and copy all structs and referenced strings into the one block.254* Each size calculation is adjusted to avoid unaligned pointers.255*/256int uv_getaddrinfo(uv_loop_t* loop,257uv_getaddrinfo_t* req,258uv_getaddrinfo_cb getaddrinfo_cb,259const char* node,260const char* service,261const struct addrinfo* hints) {262char hostname_ascii[256];263int nodesize = 0;264int servicesize = 0;265int hintssize = 0;266char* alloc_ptr = NULL;267int err;268long rc;269270if (req == NULL || (node == NULL && service == NULL)) {271return UV_EINVAL;272}273274UV_REQ_INIT(req, UV_GETADDRINFO);275req->getaddrinfo_cb = getaddrinfo_cb;276req->addrinfo = NULL;277req->loop = loop;278req->retcode = 0;279280/* calculate required memory size for all input values */281if (node != NULL) {282rc = uv__idna_toascii(node,283node + strlen(node),284hostname_ascii,285hostname_ascii + sizeof(hostname_ascii));286if (rc < 0)287return rc;288nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, hostname_ascii,289-1, NULL, 0) * sizeof(WCHAR));290if (nodesize == 0) {291err = GetLastError();292goto error;293}294node = hostname_ascii;295}296297if (service != NULL) {298servicesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8,2990,300service,301-1,302NULL,3030) *304sizeof(WCHAR));305if (servicesize == 0) {306err = GetLastError();307goto error;308}309}310if (hints != NULL) {311hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));312}313314/* allocate memory for inputs, and partition it as needed */315alloc_ptr = (char*)uv__malloc(nodesize + servicesize + hintssize);316if (!alloc_ptr) {317err = WSAENOBUFS;318goto error;319}320321/* save alloc_ptr now so we can free if error */322req->alloc = (void*)alloc_ptr;323324/* Convert node string to UTF16 into allocated memory and save pointer in the325* request. */326if (node != NULL) {327req->node = (WCHAR*)alloc_ptr;328if (MultiByteToWideChar(CP_UTF8,3290,330node,331-1,332(WCHAR*) alloc_ptr,333nodesize / sizeof(WCHAR)) == 0) {334err = GetLastError();335goto error;336}337alloc_ptr += nodesize;338} else {339req->node = NULL;340}341342/* Convert service string to UTF16 into allocated memory and save pointer in343* the req. */344if (service != NULL) {345req->service = (WCHAR*)alloc_ptr;346if (MultiByteToWideChar(CP_UTF8,3470,348service,349-1,350(WCHAR*) alloc_ptr,351servicesize / sizeof(WCHAR)) == 0) {352err = GetLastError();353goto error;354}355alloc_ptr += servicesize;356} else {357req->service = NULL;358}359360/* copy hints to allocated memory and save pointer in req */361if (hints != NULL) {362req->addrinfow = (struct addrinfoW*)alloc_ptr;363req->addrinfow->ai_family = hints->ai_family;364req->addrinfow->ai_socktype = hints->ai_socktype;365req->addrinfow->ai_protocol = hints->ai_protocol;366req->addrinfow->ai_flags = hints->ai_flags;367req->addrinfow->ai_addrlen = 0;368req->addrinfow->ai_canonname = NULL;369req->addrinfow->ai_addr = NULL;370req->addrinfow->ai_next = NULL;371} else {372req->addrinfow = NULL;373}374375uv__req_register(loop, req);376377if (getaddrinfo_cb) {378uv__work_submit(loop,379&req->work_req,380UV__WORK_SLOW_IO,381uv__getaddrinfo_work,382uv__getaddrinfo_done);383return 0;384} else {385uv__getaddrinfo_work(&req->work_req);386uv__getaddrinfo_done(&req->work_req, 0);387return req->retcode;388}389390error:391if (req != NULL) {392uv__free(req->alloc);393req->alloc = NULL;394}395return uv_translate_sys_error(err);396}397398int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {399NET_LUID luid;400wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */401DWORD bufsize;402int r;403404if (buffer == NULL || size == NULL || *size == 0)405return UV_EINVAL;406407r = ConvertInterfaceIndexToLuid(ifindex, &luid);408409if (r != 0)410return uv_translate_sys_error(r);411412r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));413414if (r != 0)415return uv_translate_sys_error(r);416417/* Check how much space we need */418bufsize = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL);419420if (bufsize == 0) {421return uv_translate_sys_error(GetLastError());422} else if (bufsize > *size) {423*size = bufsize;424return UV_ENOBUFS;425}426427/* Convert to UTF-8 */428bufsize = WideCharToMultiByte(CP_UTF8,4290,430wname,431-1,432buffer,433*size,434NULL,435NULL);436437if (bufsize == 0)438return uv_translate_sys_error(GetLastError());439440*size = bufsize - 1;441return 0;442}443444int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {445int r;446447if (buffer == NULL || size == NULL || *size == 0)448return UV_EINVAL;449450r = snprintf(buffer, *size, "%d", ifindex);451452if (r < 0)453return uv_translate_sys_error(r);454455if (r >= (int) *size) {456*size = r + 1;457return UV_ENOBUFS;458}459460*size = r;461return 0;462}463464465