diff --git a/Makefile.am b/Makefile.am index 41833c982..409f5b7dc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,6 +51,7 @@ libuv_la_SOURCES += src/win/async.c \ src/win/fs-event.c \ src/win/fs.c \ src/win/getaddrinfo.c \ + src/win/getnameinfo.c \ src/win/handle.c \ src/win/handle-inl.h \ src/win/internal.h \ @@ -86,6 +87,7 @@ libuv_la_SOURCES += src/unix/async.c \ src/unix/dl.c \ src/unix/fs.c \ src/unix/getaddrinfo.c \ + src/unix/getnameinfo.c \ src/unix/internal.h \ src/unix/loop-watcher.c \ src/unix/loop.c \ @@ -138,6 +140,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-get-loadavg.c \ test/test-get-memory.c \ test/test-getaddrinfo.c \ + test/test-getnameinfo.c \ test/test-getsockname.c \ test/test-hrtime.c \ test/test-idle.c \ diff --git a/include/uv-unix.h b/include/uv-unix.h index 40c49894c..8636d5d2e 100644 --- a/include/uv-unix.h +++ b/include/uv-unix.h @@ -281,6 +281,15 @@ typedef struct { struct addrinfo* res; \ int retcode; +#define UV_GETNAMEINFO_PRIVATE_FIELDS \ + struct uv__work work_req; \ + uv_getnameinfo_cb getnameinfo_cb; \ + struct sockaddr_storage storage; \ + int flags; \ + char host[NI_MAXHOST]; \ + char service[NI_MAXSERV]; \ + int retcode; + #define UV_PROCESS_PRIVATE_FIELDS \ void* queue[2]; \ int status; \ diff --git a/include/uv-win.h b/include/uv-win.h index 64b894ee5..2f24dfe89 100644 --- a/include/uv-win.h +++ b/include/uv-win.h @@ -528,6 +528,14 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); struct addrinfoW* res; \ int retcode; +#define UV_GETNAMEINFO_PRIVATE_FIELDS \ + uv_getnameinfo_cb getnameinfo_cb; \ + struct sockaddr_storage storage; \ + int flags; \ + char host[NI_MAXHOST]; \ + char service[NI_MAXSERV]; \ + int retcode; + #define UV_PROCESS_PRIVATE_FIELDS \ struct uv_process_exit_s { \ UV_REQ_FIELDS \ diff --git a/include/uv.h b/include/uv.h index 65aa32db9..1ce965717 100644 --- a/include/uv.h +++ b/include/uv.h @@ -167,6 +167,7 @@ extern "C" { XX(FS, fs) \ XX(WORK, work) \ XX(GETADDRINFO, getaddrinfo) \ + XX(GETNAMEINFO, getnameinfo) \ typedef enum { #define XX(code, _) UV_ ## code = UV__ ## code, @@ -216,6 +217,7 @@ typedef struct uv_signal_s uv_signal_t; /* Request types. */ typedef struct uv_req_s uv_req_t; typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; +typedef struct uv_getnameinfo_s uv_getnameinfo_t; typedef struct uv_shutdown_s uv_shutdown_t; typedef struct uv_write_s uv_write_t; typedef struct uv_connect_s uv_connect_t; @@ -419,6 +421,10 @@ typedef void (*uv_after_work_cb)(uv_work_t* req, int status); typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req, int status, struct addrinfo* res); +typedef void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req, + int status, + char* hostname, + char* service); typedef struct { long tv_sec; @@ -1451,6 +1457,33 @@ UV_EXTERN int uv_getaddrinfo(uv_loop_t* loop, UV_EXTERN void uv_freeaddrinfo(struct addrinfo* ai); +/* +* uv_getnameinfo_t is a subclass of uv_req_t +* +* Request object for uv_getnameinfo. +*/ +struct uv_getnameinfo_s { + UV_REQ_FIELDS + /* read-only */ + uv_loop_t* loop; + UV_GETNAMEINFO_PRIVATE_FIELDS +}; + +/* + * Asynchronous getnameinfo. + * + * Returns 0 on success or an error code < 0 on failure. + * + * If successful, your callback gets called sometime in the future with the + * lookup result. + */ +UV_EXTERN int uv_getnameinfo(uv_loop_t* loop, + uv_getnameinfo_t* req, + uv_getnameinfo_cb getnameinfo_cb, + const struct sockaddr* addr, + int flags); + + /* uv_spawn() options */ typedef enum { UV_IGNORE = 0x00, @@ -2252,6 +2285,7 @@ struct uv_loop_s { #undef UV_ASYNC_PRIVATE_FIELDS #undef UV_TIMER_PRIVATE_FIELDS #undef UV_GETADDRINFO_PRIVATE_FIELDS +#undef UV_GETNAMEINFO_PRIVATE_FIELDS #undef UV_FS_REQ_PRIVATE_FIELDS #undef UV_WORK_PRIVATE_FIELDS #undef UV_FS_EVENT_PRIVATE_FIELDS diff --git a/src/unix/getnameinfo.c b/src/unix/getnameinfo.c new file mode 100644 index 000000000..b800d9da3 --- /dev/null +++ b/src/unix/getnameinfo.c @@ -0,0 +1,112 @@ +/* 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 +#include +#include +#include + +#include "uv.h" +#include "internal.h" + + +static void uv__getnameinfo_work(struct uv__work* w) { + uv_getnameinfo_t* req; + int err; + socklen_t salen; + + req = container_of(w, uv_getnameinfo_t, work_req); + + if (req->storage.ss_family == AF_INET) + salen = sizeof(struct sockaddr_in); + else if (req->storage.ss_family == AF_INET6) + salen = sizeof(struct sockaddr_in6); + else + abort(); + + err = getnameinfo((struct sockaddr*) &req->storage, + salen, + req->host, + sizeof(req->host), + req->service, + sizeof(req->service), + req->flags); + req->retcode = uv__getaddrinfo_translate_error(err); +} + +static void uv__getnameinfo_done(struct uv__work* w, int status) { + uv_getnameinfo_t* req; + char* host; + char* service; + + req = container_of(w, uv_getnameinfo_t, work_req); + uv__req_unregister(req->loop, req); + + if (req->retcode == 0) { + host = req->host; + service = req->service; + } else { + host = NULL; + service = NULL; + } + + req->getnameinfo_cb(req, req->retcode, host, service); +} + +/* +* Entry point for getnameinfo +* return 0 if a callback will be made +* return error code if validation fails +*/ +int uv_getnameinfo(uv_loop_t* loop, + uv_getnameinfo_t* req, + uv_getnameinfo_cb getnameinfo_cb, + const struct sockaddr* addr, + int flags) { + if (req == NULL || getnameinfo_cb == NULL || addr == NULL) + return UV_EINVAL; + + if (addr->sa_family == AF_INET) { + memcpy(&req->storage, + addr, + sizeof(struct sockaddr_in)); + } else if (addr->sa_family == AF_INET6) { + memcpy(&req->storage, + addr, + sizeof(struct sockaddr_in6)); + } else { + return UV_EINVAL; + } + + uv__req_init(loop, (uv_req_t*)req, UV_GETNAMEINFO); + + req->getnameinfo_cb = getnameinfo_cb; + req->flags = flags; + req->type = UV_GETNAMEINFO; + req->loop = loop; + + uv__work_submit(loop, + &req->work_req, + uv__getnameinfo_work, + uv__getnameinfo_done); + + return 0; +} diff --git a/src/unix/threadpool.c b/src/unix/threadpool.c index f2eb1cb5c..18687249b 100644 --- a/src/unix/threadpool.c +++ b/src/unix/threadpool.c @@ -268,6 +268,10 @@ int uv_cancel(uv_req_t* req) { loop = ((uv_getaddrinfo_t*) req)->loop; wreq = &((uv_getaddrinfo_t*) req)->work_req; break; + case UV_GETNAMEINFO: + loop = ((uv_getnameinfo_t*) req)->loop; + wreq = &((uv_getnameinfo_t*) req)->work_req; + break; case UV_WORK: loop = ((uv_work_t*) req)->loop; wreq = &((uv_work_t*) req)->work_req; diff --git a/src/win/getnameinfo.c b/src/win/getnameinfo.c new file mode 100644 index 000000000..48eb16d8c --- /dev/null +++ b/src/win/getnameinfo.c @@ -0,0 +1,138 @@ +/* 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 +#include +#include + +#include "uv.h" +#include "internal.h" +#include "req-inl.h" + + +/* getnameinfo worker thread implementation */ +static DWORD WINAPI getnameinfo_thread_proc(void* parameter) { + uv_getnameinfo_t* req = (uv_getnameinfo_t*)parameter; + uv_loop_t* loop = req->loop; + WCHAR host[NI_MAXHOST]; + WCHAR service[NI_MAXSERV]; + int ret = 0; + + assert(req != NULL); + + ret = GetNameInfoW((struct sockaddr*)&req->storage, + sizeof(req->storage), + host, + sizeof(host), + service, + sizeof(service), + req->flags); + req->retcode = uv__getaddrinfo_translate_error(ret); + + /* convert results to UTF-8 */ + WideCharToMultiByte(CP_UTF8, + 0, + host, + -1, + req->host, + sizeof(req->host), + NULL, + NULL); + + WideCharToMultiByte(CP_UTF8, + 0, + service, + -1, + req->service, + sizeof(req->service), + NULL, + NULL); + + /* post getnameinfo completed */ + POST_COMPLETION_FOR_REQ(loop, req); + + return 0; +} + + +/* +* Called from uv_run when complete. +*/ +void uv_process_getnameinfo_req(uv_loop_t* loop, uv_getnameinfo_t* req) { + char* host; + char* service; + + if (req->retcode == 0) { + host = req->host; + service = req->service; + } else { + host = NULL; + service = NULL; + } + + uv__req_unregister(loop, req); + req->getnameinfo_cb(req, req->retcode, host, service); +} + + +/* +* Entry point for getnameinfo +* return 0 if a callback will be made +* return error code if validation fails +*/ +int uv_getnameinfo(uv_loop_t* loop, + uv_getnameinfo_t* req, + uv_getnameinfo_cb getnameinfo_cb, + const struct sockaddr* addr, + int flags) { + if (req == NULL || getnameinfo_cb == NULL || addr == NULL) + return UV_EINVAL; + + if (addr->sa_family == AF_INET) { + memcpy(&req->storage, + addr, + sizeof(struct sockaddr_in)); + } else if (addr->sa_family == AF_INET6) { + memcpy(&req->storage, + addr, + sizeof(struct sockaddr_in6)); + } else { + return UV_EINVAL; + } + + uv_req_init(loop, (uv_req_t*)req); + + req->getnameinfo_cb = getnameinfo_cb; + req->flags = flags; + req->type = UV_GETNAMEINFO; + req->loop = loop; + + /* Ask thread to run. Treat this as a long operation. */ + if (QueueUserWorkItem(&getnameinfo_thread_proc, + req, + WT_EXECUTELONGFUNCTION) == 0) { + return uv_translate_sys_error(GetLastError()); + } + + uv__req_register(loop, req); + + return 0; +} diff --git a/src/win/internal.h b/src/win/internal.h index 45ce177e6..f910c4ffc 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -291,6 +291,12 @@ int uv_translate_sys_error(int sys_errno); void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* req); +/* +* Getnameinfo +*/ +void uv_process_getnameinfo_req(uv_loop_t* loop, uv_getnameinfo_t* req); + + /* * FS */ diff --git a/src/win/req-inl.h b/src/win/req-inl.h index 353fe90b6..cbc2ba8e1 100644 --- a/src/win/req-inl.h +++ b/src/win/req-inl.h @@ -199,6 +199,10 @@ INLINE static void uv_process_reqs(uv_loop_t* loop) { uv_process_getaddrinfo_req(loop, (uv_getaddrinfo_t*) req); break; + case UV_GETNAMEINFO: + uv_process_getnameinfo_req(loop, (uv_getnameinfo_t*)req); + break; + case UV_PROCESS_EXIT: uv_process_proc_exit(loop, (uv_process_t*) req->data); break; diff --git a/test/test-getnameinfo.c b/test/test-getnameinfo.c new file mode 100644 index 000000000..707fce014 --- /dev/null +++ b/test/test-getnameinfo.c @@ -0,0 +1,83 @@ +/* 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 "uv.h" +#include "task.h" +#include +#include +#include + + +static const char* address_ip4 = "127.0.0.1"; +static const char* address_ip6 = "::1"; +static const int port = 80; + +static struct sockaddr_in addr4; +static struct sockaddr_in6 addr6; +static uv_getnameinfo_t req; + +static void getnameinfo_req(uv_getnameinfo_t* handle, + int status, + char* hostname, + char* service) { + ASSERT(handle != NULL); + ASSERT(status == 0); + ASSERT(hostname != NULL); + ASSERT(service != NULL); +} + +TEST_IMPL(getnameinfo_basic_ip4) { + int r; + + r = uv_ip4_addr(address_ip4, port, &addr4); + ASSERT(r == 0); + + r = uv_getnameinfo(uv_default_loop(), + &req, + &getnameinfo_req, + (const struct sockaddr*)&addr4, + 0); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(getnameinfo_basic_ip6) { + int r; + + r = uv_ip6_addr(address_ip6, port, &addr6); + ASSERT(r == 0); + + r = uv_getnameinfo(uv_default_loop(), + &req, + &getnameinfo_req, + (const struct sockaddr*)&addr6, + 0); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-list.h b/test/test-list.h index 9f9c4ca30..9961cacf2 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -163,6 +163,8 @@ TEST_DECLARE (hrtime) TEST_DECLARE (getaddrinfo_fail) TEST_DECLARE (getaddrinfo_basic) TEST_DECLARE (getaddrinfo_concurrent) +TEST_DECLARE (getnameinfo_basic_ip4) +TEST_DECLARE (getnameinfo_basic_ip6) TEST_DECLARE (getsockname_tcp) TEST_DECLARE (getsockname_udp) TEST_DECLARE (fail_always) @@ -472,6 +474,9 @@ TASK_LIST_START TEST_ENTRY (getaddrinfo_basic) TEST_ENTRY (getaddrinfo_concurrent) + TEST_ENTRY (getnameinfo_basic_ip4) + TEST_ENTRY (getnameinfo_basic_ip6) + TEST_ENTRY (getsockname_tcp) TEST_ENTRY (getsockname_udp) diff --git a/uv.gyp b/uv.gyp index 710bd40bb..c207b51f0 100644 --- a/uv.gyp +++ b/uv.gyp @@ -86,6 +86,7 @@ 'src/win/fs.c', 'src/win/fs-event.c', 'src/win/getaddrinfo.c', + 'src/win/getnameinfo.c', 'src/win/handle.c', 'src/win/handle-inl.h', 'src/win/internal.h', @@ -141,6 +142,7 @@ 'src/unix/dl.c', 'src/unix/fs.c', 'src/unix/getaddrinfo.c', + 'src/unix/getnameinfo.c', 'src/unix/internal.h', 'src/unix/loop.c', 'src/unix/loop-watcher.c', @@ -318,6 +320,7 @@ 'test/test-get-currentexe.c', 'test/test-get-memory.c', 'test/test-getaddrinfo.c', + 'test/test-getnameinfo.c', 'test/test-getsockname.c', 'test/test-hrtime.c', 'test/test-idle.c',