blob: 4b8ee75a0622f6b138ea366c7f42306960b61b3d [file]
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <assert.h>
#include "uv.h"
#include "internal.h"
#include "req-inl.h"
#include "idna.h"
/* EAI_* constants. */
#include <winsock2.h>
/* Needed for ConvertInterfaceIndexToLuid and ConvertInterfaceLuidToNameA */
#include <iphlpapi.h>
int uv__getaddrinfo_translate_error(int sys_err) {
switch (sys_err) {
case 0: return 0;
case WSATRY_AGAIN: return UV_EAI_AGAIN;
case WSAEINVAL: return UV_EAI_BADFLAGS;
case WSANO_RECOVERY: return UV_EAI_FAIL;
case WSAEAFNOSUPPORT: return UV_EAI_FAMILY;
case WSA_NOT_ENOUGH_MEMORY: return UV_EAI_MEMORY;
case WSAHOST_NOT_FOUND: return UV_EAI_NONAME;
case WSATYPE_NOT_FOUND: return UV_EAI_SERVICE;
case WSAESOCKTNOSUPPORT: return UV_EAI_SOCKTYPE;
default: return uv_translate_sys_error(sys_err);
}
}
/*
* MinGW is missing this
*/
#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)
typedef struct addrinfoW {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
WCHAR* ai_canonname;
struct sockaddr* ai_addr;
struct addrinfoW* ai_next;
} ADDRINFOW, *PADDRINFOW;
DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const WCHAR* node,
const WCHAR* service,
const ADDRINFOW* hints,
PADDRINFOW* result);
DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
#endif
static size_t align_offset(size_t off, size_t alignment) {
return ((off + alignment - 1) / alignment) * alignment;
}
#ifndef NDIS_IF_MAX_STRING_SIZE
#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE
#endif
static void uv__getaddrinfo_work(struct uv__work* w) {
uv_getaddrinfo_t* req;
struct addrinfoW* hints;
int err;
req = container_of(w, uv_getaddrinfo_t, work_req);
hints = req->addrinfow;
req->addrinfow = NULL;
err = GetAddrInfoW(req->node, req->service, hints, &req->addrinfow);
req->retcode = uv__getaddrinfo_translate_error(err);
}
/*
* Called from uv_run when complete. Call user specified callback
* then free returned addrinfo
* Returned addrinfo strings are converted from UTF-16 to UTF-8.
*
* To minimize allocation we calculate total size required,
* and copy all structs and referenced strings into the one block.
* Each size calculation is adjusted to avoid unaligned pointers.
*/
static void uv__getaddrinfo_done(struct uv__work* w, int status) {
uv_getaddrinfo_t* req = container_of(w, uv_getaddrinfo_t, work_req);
/* release input parameter memory */
uv__free(req->alloc);
req->alloc = NULL;
if (status == UV_ECANCELED) {
assert(req->retcode == 0);
req->retcode = UV_EAI_CANCELED;
goto complete;
}
if (req->retcode == 0) {
char* alloc_ptr = NULL;
size_t cur_off = 0;
size_t addrinfo_len;
/* Convert addrinfoW to addrinfo. First calculate required length. */
struct addrinfoW* addrinfow_ptr = req->addrinfow;
while (addrinfow_ptr != NULL) {
cur_off = align_offset(cur_off, sizeof(void*));
cur_off += sizeof(struct addrinfo);
/* TODO: This alignment could be smaller, if we could
portably get the alignment for sockaddr. */
cur_off = align_offset(cur_off, sizeof(void*));
cur_off += addrinfow_ptr->ai_addrlen;
if (addrinfow_ptr->ai_canonname != NULL) {
ssize_t name_len =
uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1);
if (name_len < 0) {
req->retcode = name_len;
goto complete;
}
cur_off += name_len + 1;
}
addrinfow_ptr = addrinfow_ptr->ai_next;
}
/* allocate memory for addrinfo results */
addrinfo_len = cur_off;
alloc_ptr = uv__malloc(addrinfo_len);
/* do conversions */
if (alloc_ptr != NULL) {
struct addrinfo *addrinfo_ptr = (struct addrinfo *)alloc_ptr;
cur_off = 0;
addrinfow_ptr = req->addrinfow;
for (;;) {
cur_off += sizeof(struct addrinfo);
assert(cur_off <= addrinfo_len);
/* copy addrinfo struct data */
addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags;
addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen;
addrinfo_ptr->ai_canonname = NULL;
addrinfo_ptr->ai_addr = NULL;
addrinfo_ptr->ai_next = NULL;
/* copy sockaddr */
if (addrinfo_ptr->ai_addrlen > 0) {
cur_off = align_offset(cur_off, sizeof(void *));
addrinfo_ptr->ai_addr = (struct sockaddr *)(alloc_ptr + cur_off);
cur_off += addrinfo_ptr->ai_addrlen;
assert(cur_off <= addrinfo_len);
memcpy(addrinfo_ptr->ai_addr,
addrinfow_ptr->ai_addr,
addrinfo_ptr->ai_addrlen);
}
/* convert canonical name to UTF-8 */
if (addrinfow_ptr->ai_canonname != NULL) {
ssize_t name_len = addrinfo_len - cur_off;
addrinfo_ptr->ai_canonname = alloc_ptr + cur_off;
int r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
-1,
addrinfo_ptr->ai_canonname,
(size_t*)&name_len);
assert(r == 0);
cur_off += name_len + 1;
assert(cur_off <= addrinfo_len);
}
/* set next ptr */
addrinfow_ptr = addrinfow_ptr->ai_next;
if (addrinfow_ptr == NULL)
break;
cur_off = align_offset(cur_off, sizeof(void *));
struct addrinfo *next_addrinfo_ptr = (struct addrinfo *)(alloc_ptr + cur_off);
addrinfo_ptr->ai_next = next_addrinfo_ptr;
addrinfo_ptr = next_addrinfo_ptr;
}
req->addrinfo = (struct addrinfo*)alloc_ptr;
} else {
req->retcode = UV_EAI_MEMORY;
}
}
/* return memory to system */
if (req->addrinfow != NULL) {
FreeAddrInfoW(req->addrinfow);
req->addrinfow = NULL;
}
complete:
uv__req_unregister(req->loop);
/* finally do callback with converted result */
if (req->getaddrinfo_cb)
req->getaddrinfo_cb(req, req->retcode, req->addrinfo);
}
void uv_freeaddrinfo(struct addrinfo* ai) {
char* alloc_ptr = (char*)ai;
/* release copied result memory */
uv__free(alloc_ptr);
}
/*
* Entry point for getaddrinfo
* we convert the UTF-8 strings to UNICODE
* and save the UNICODE string pointers in the req
* We also copy hints so that caller does not need to keep memory until the
* callback.
* return 0 if a callback will be made
* return error code if validation fails
*
* To minimize allocation we calculate total size required,
* and copy all structs and referenced strings into the one block.
* Each size calculation is adjusted to avoid unaligned pointers.
*/
int 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) {
char hostname_ascii[256];
size_t off = 0;
size_t nodesize = 0;
size_t servicesize = 0;
size_t serviceoff = 0;
size_t hintssize = 0;
size_t hintoff = 0;
ssize_t rc;
if (req == NULL || (node == NULL && service == NULL)) {
return UV_EINVAL;
}
UV_REQ_INIT(req, UV_GETADDRINFO);
req->getaddrinfo_cb = getaddrinfo_cb;
req->addrinfo = NULL;
req->loop = loop;
req->retcode = 0;
/* calculate required memory size for all input values */
if (node != NULL) {
rc = uv__idna_toascii(node,
node + strlen(node),
hostname_ascii,
hostname_ascii + sizeof(hostname_ascii));
if (rc < 0)
return rc;
nodesize = strlen(hostname_ascii) + 1;
node = hostname_ascii;
off += nodesize * sizeof(WCHAR);
}
if (service != NULL) {
rc = uv_wtf8_length_as_utf16(service);
if (rc < 0)
return rc;
servicesize = rc;
off = align_offset(off, sizeof(WCHAR));
serviceoff = off;
off += servicesize * sizeof(WCHAR);
}
if (hints != NULL) {
off = align_offset(off, sizeof(void *));
hintoff = off;
hintssize = sizeof(struct addrinfoW);
off += hintssize;
}
/* allocate memory for inputs, and partition it as needed */
req->alloc = uv__malloc(off);
if (!req->alloc)
return UV_ENOMEM;
/* Convert node string to UTF16 into allocated memory and save pointer in the
* request. The node here has been converted to ascii. */
if (node != NULL) {
req->node = (WCHAR*) req->alloc;
uv_wtf8_to_utf16(node, req->node, nodesize);
} else {
req->node = NULL;
}
/* Convert service string to UTF16 into allocated memory and save pointer in
* the req. */
if (service != NULL) {
req->service = (WCHAR*) ((char*) req->alloc + serviceoff);
uv_wtf8_to_utf16(service, req->service, servicesize);
} else {
req->service = NULL;
}
/* copy hints to allocated memory and save pointer in req */
if (hints != NULL) {
req->addrinfow = (struct addrinfoW*) ((char*) req->alloc + hintoff);
req->addrinfow->ai_family = hints->ai_family;
req->addrinfow->ai_socktype = hints->ai_socktype;
req->addrinfow->ai_protocol = hints->ai_protocol;
req->addrinfow->ai_flags = hints->ai_flags;
req->addrinfow->ai_addrlen = 0;
req->addrinfow->ai_canonname = NULL;
req->addrinfow->ai_addr = NULL;
req->addrinfow->ai_next = NULL;
} else {
req->addrinfow = NULL;
}
uv__req_register(loop);
if (getaddrinfo_cb) {
uv__work_submit(loop,
&req->work_req,
UV__WORK_SLOW_IO,
uv__getaddrinfo_work,
uv__getaddrinfo_done);
return 0;
} else {
uv__getaddrinfo_work(&req->work_req);
uv__getaddrinfo_done(&req->work_req, 0);
return req->retcode;
}
}
int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
NET_LUID luid;
wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */
int r;
if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;
r = ConvertInterfaceIndexToLuid(ifindex, &luid);
if (r != 0)
return uv_translate_sys_error(r);
r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));
if (r != 0)
return uv_translate_sys_error(r);
return uv__copy_utf16_to_utf8(wname, -1, buffer, size);
}
int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
int r;
if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;
r = snprintf(buffer, *size, "%d", ifindex);
if (r < 0)
return uv_translate_sys_error(r);
if (r >= (int) *size) {
*size = r + 1;
return UV_ENOBUFS;
}
*size = r;
return 0;
}