366 lines
11 KiB
C
366 lines
11 KiB
C
/* 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 <malloc.h>
|
|
|
|
#include "uv.h"
|
|
#include "internal.h"
|
|
#include "req-inl.h"
|
|
|
|
|
|
/*
|
|
* 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_t* ai_canonname;
|
|
struct sockaddr* ai_addr;
|
|
struct addrinfoW* ai_next;
|
|
} ADDRINFOW, *PADDRINFOW;
|
|
|
|
DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const wchar_t* node,
|
|
const wchar_t* service,
|
|
const ADDRINFOW* hints,
|
|
PADDRINFOW* result);
|
|
|
|
DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
|
|
#endif
|
|
|
|
|
|
/* adjust size value to be multiple of 4. Use to keep pointer aligned */
|
|
/* Do we need different versions of this for different architectures? */
|
|
#define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2)
|
|
|
|
|
|
/*
|
|
* getaddrinfo error code mapping
|
|
* Falls back to uv_translate_sys_error if no match
|
|
*/
|
|
static uv_err_code uv_translate_eai_error(int eai_errno) {
|
|
switch (eai_errno) {
|
|
case ERROR_SUCCESS: return UV_OK;
|
|
case EAI_BADFLAGS: return UV_EBADF;
|
|
case EAI_FAIL: return UV_EFAULT;
|
|
case EAI_FAMILY: return UV_EAIFAMNOSUPPORT;
|
|
case EAI_MEMORY: return UV_ENOMEM;
|
|
case EAI_NONAME: return UV_ENOENT;
|
|
case EAI_AGAIN: return UV_EAGAIN;
|
|
case EAI_SERVICE: return UV_EAISERVICE;
|
|
case EAI_SOCKTYPE: return UV_EAISOCKTYPE;
|
|
default: return uv_translate_sys_error(eai_errno);
|
|
}
|
|
}
|
|
|
|
|
|
/* getaddrinfo worker thread implementation */
|
|
static DWORD WINAPI getaddrinfo_thread_proc(void* parameter) {
|
|
uv_getaddrinfo_t* req = (uv_getaddrinfo_t*) parameter;
|
|
uv_loop_t* loop = req->loop;
|
|
int ret;
|
|
|
|
assert(req != NULL);
|
|
|
|
/* call OS function on this thread */
|
|
ret = GetAddrInfoW(req->node,
|
|
req->service,
|
|
req->hints,
|
|
&req->res);
|
|
req->retcode = ret;
|
|
|
|
/* post getaddrinfo completed */
|
|
POST_COMPLETION_FOR_REQ(loop, req);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* req) {
|
|
int addrinfo_len = 0;
|
|
int name_len = 0;
|
|
size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo));
|
|
struct addrinfoW* addrinfow_ptr;
|
|
struct addrinfo* addrinfo_ptr;
|
|
char* alloc_ptr = NULL;
|
|
char* cur_ptr = NULL;
|
|
int status = 0;
|
|
|
|
/* release input parameter memory */
|
|
if (req->alloc != NULL) {
|
|
free(req->alloc);
|
|
req->alloc = NULL;
|
|
}
|
|
|
|
if (req->retcode == 0) {
|
|
/* convert addrinfoW to addrinfo */
|
|
/* first calculate required length */
|
|
addrinfow_ptr = req->res;
|
|
while (addrinfow_ptr != NULL) {
|
|
addrinfo_len += addrinfo_struct_len +
|
|
ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
|
|
if (addrinfow_ptr->ai_canonname != NULL) {
|
|
name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0);
|
|
if (name_len == 0) {
|
|
uv__set_sys_error(loop, GetLastError());
|
|
status = -1;
|
|
goto complete;
|
|
}
|
|
addrinfo_len += ALIGNED_SIZE(name_len);
|
|
}
|
|
addrinfow_ptr = addrinfow_ptr->ai_next;
|
|
}
|
|
|
|
/* allocate memory for addrinfo results */
|
|
alloc_ptr = (char*)malloc(addrinfo_len);
|
|
|
|
/* do conversions */
|
|
if (alloc_ptr != NULL) {
|
|
cur_ptr = alloc_ptr;
|
|
addrinfow_ptr = req->res;
|
|
|
|
while (addrinfow_ptr != NULL) {
|
|
/* copy addrinfo struct data */
|
|
assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len);
|
|
addrinfo_ptr = (struct addrinfo*)cur_ptr;
|
|
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;
|
|
|
|
cur_ptr += addrinfo_struct_len;
|
|
|
|
/* copy sockaddr */
|
|
if (addrinfo_ptr->ai_addrlen > 0) {
|
|
assert(cur_ptr + addrinfo_ptr->ai_addrlen <=
|
|
alloc_ptr + addrinfo_len);
|
|
memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);
|
|
addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;
|
|
cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);
|
|
}
|
|
|
|
/* convert canonical name to UTF-8 */
|
|
if (addrinfow_ptr->ai_canonname != NULL) {
|
|
name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname,
|
|
-1,
|
|
NULL,
|
|
0);
|
|
assert(name_len > 0);
|
|
assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len);
|
|
name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname,
|
|
-1,
|
|
cur_ptr,
|
|
name_len);
|
|
assert(name_len > 0);
|
|
addrinfo_ptr->ai_canonname = cur_ptr;
|
|
cur_ptr += ALIGNED_SIZE(name_len);
|
|
}
|
|
assert(cur_ptr <= alloc_ptr + addrinfo_len);
|
|
|
|
/* set next ptr */
|
|
addrinfow_ptr = addrinfow_ptr->ai_next;
|
|
if (addrinfow_ptr != NULL) {
|
|
addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;
|
|
}
|
|
}
|
|
} else {
|
|
uv__set_artificial_error(loop, UV_ENOMEM);
|
|
status = -1;
|
|
}
|
|
} else {
|
|
/* GetAddrInfo failed */
|
|
uv__set_artificial_error(loop, uv_translate_eai_error(req->retcode));
|
|
status = -1;
|
|
}
|
|
|
|
/* return memory to system */
|
|
if (req->res != NULL) {
|
|
FreeAddrInfoW(req->res);
|
|
req->res = NULL;
|
|
}
|
|
|
|
complete:
|
|
uv__req_unregister(loop, req);
|
|
|
|
/* finally do callback with converted result */
|
|
req->getaddrinfo_cb(req, status, (struct addrinfo*)alloc_ptr);
|
|
}
|
|
|
|
|
|
void uv_freeaddrinfo(struct addrinfo* ai) {
|
|
char* alloc_ptr = (char*)ai;
|
|
|
|
/* release copied result memory */
|
|
if (alloc_ptr != NULL) {
|
|
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 UV_OK 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) {
|
|
int nodesize = 0;
|
|
int servicesize = 0;
|
|
int hintssize = 0;
|
|
char* alloc_ptr = NULL;
|
|
|
|
if (req == NULL || getaddrinfo_cb == NULL ||
|
|
(node == NULL && service == NULL)) {
|
|
uv__set_sys_error(loop, WSAEINVAL);
|
|
goto error;
|
|
}
|
|
|
|
uv_req_init(loop, (uv_req_t*)req);
|
|
|
|
req->getaddrinfo_cb = getaddrinfo_cb;
|
|
req->res = NULL;
|
|
req->type = UV_GETADDRINFO;
|
|
req->loop = loop;
|
|
|
|
/* calculate required memory size for all input values */
|
|
if (node != NULL) {
|
|
nodesize = ALIGNED_SIZE(uv_utf8_to_utf16(node, NULL, 0) * sizeof(wchar_t));
|
|
if (nodesize == 0) {
|
|
uv__set_sys_error(loop, GetLastError());
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (service != NULL) {
|
|
servicesize = ALIGNED_SIZE(uv_utf8_to_utf16(service, NULL, 0) *
|
|
sizeof(wchar_t));
|
|
if (servicesize == 0) {
|
|
uv__set_sys_error(loop, GetLastError());
|
|
goto error;
|
|
}
|
|
}
|
|
if (hints != NULL) {
|
|
hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));
|
|
}
|
|
|
|
/* allocate memory for inputs, and partition it as needed */
|
|
alloc_ptr = (char*)malloc(nodesize + servicesize + hintssize);
|
|
if (!alloc_ptr) {
|
|
uv__set_sys_error(loop, WSAENOBUFS);
|
|
goto error;
|
|
}
|
|
|
|
/* save alloc_ptr now so we can free if error */
|
|
req->alloc = (void*)alloc_ptr;
|
|
|
|
/* convert node string to UTF16 into allocated memory and save pointer in */
|
|
/* the reques. */
|
|
if (node != NULL) {
|
|
req->node = (wchar_t*)alloc_ptr;
|
|
if (uv_utf8_to_utf16(node,
|
|
(wchar_t*) alloc_ptr,
|
|
nodesize / sizeof(wchar_t)) == 0) {
|
|
uv__set_sys_error(loop, GetLastError());
|
|
goto error;
|
|
}
|
|
alloc_ptr += 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_t*)alloc_ptr;
|
|
if (uv_utf8_to_utf16(service,
|
|
(wchar_t*) alloc_ptr,
|
|
servicesize / sizeof(wchar_t)) == 0) {
|
|
uv__set_sys_error(loop, GetLastError());
|
|
goto error;
|
|
}
|
|
alloc_ptr += servicesize;
|
|
} else {
|
|
req->service = NULL;
|
|
}
|
|
|
|
/* copy hints to allocated memory and save pointer in req */
|
|
if (hints != NULL) {
|
|
req->hints = (struct addrinfoW*)alloc_ptr;
|
|
req->hints->ai_family = hints->ai_family;
|
|
req->hints->ai_socktype = hints->ai_socktype;
|
|
req->hints->ai_protocol = hints->ai_protocol;
|
|
req->hints->ai_flags = hints->ai_flags;
|
|
req->hints->ai_addrlen = 0;
|
|
req->hints->ai_canonname = NULL;
|
|
req->hints->ai_addr = NULL;
|
|
req->hints->ai_next = NULL;
|
|
} else {
|
|
req->hints = NULL;
|
|
}
|
|
|
|
/* Ask thread to run. Treat this as a long operation */
|
|
if (QueueUserWorkItem(&getaddrinfo_thread_proc,
|
|
req,
|
|
WT_EXECUTELONGFUNCTION) == 0) {
|
|
uv__set_sys_error(loop, GetLastError());
|
|
goto error;
|
|
}
|
|
|
|
uv__req_register(loop, req);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
if (req != NULL && req->alloc != NULL) {
|
|
free(req->alloc);
|
|
}
|
|
return -1;
|
|
}
|