diff --git a/Makefile.am b/Makefile.am index a9531e2ba..75ec751b1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -157,6 +157,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-ping-pong.c \ test/test-pipe-bind-error.c \ test/test-pipe-connect-error.c \ + test/test-pipe-getsockname.c \ test/test-pipe-server-close.c \ test/test-platform-output.c \ test/test-poll-close.c \ diff --git a/include/uv.h b/include/uv.h index 89dfad3d5..c8536c234 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1195,6 +1195,15 @@ UV_EXTERN void uv_pipe_connect(uv_connect_t* req, const char* name, uv_connect_cb cb); +/* + * Get the name of the UNIX domain socket or the named pipe. + * + * A preallocated buffer must be provided. The len parameter holds the + * length of the buffer and it's set to the number of bytes written to the + * buffer on output. + */ +UV_EXTERN int uv_pipe_getsockname(const uv_pipe_t* handle, char* buf, size_t* len); + /* * This setting applies to Windows only. * Set the number of pending pipe instance handles when the pipe server diff --git a/src/unix/pipe.c b/src/unix/pipe.c index fd4afb637..2713abf4a 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -212,5 +212,37 @@ out: } +int uv_pipe_getsockname(const uv_pipe_t* handle, char* buf, size_t* len) { + struct sockaddr_un sa; + socklen_t addrlen; + int err; + + addrlen = sizeof(sa); + memset(&sa, 0, addrlen); + err = getsockname(uv__stream_fd(handle), (struct sockaddr*) &sa, &addrlen); + if (err < 0) { + *len = 0; + return -errno; + } + + if (sa.sun_path[0] == 0) + /* Linux abstract namespace */ + addrlen -= offsetof(struct sockaddr_un, sun_path); + else + addrlen = strlen(sa.sun_path) + 1; + + + if (addrlen > *len) { + *len = 0; + return UV_ENOBUFS; + } + + memcpy(buf, sa.sun_path, addrlen); + *len = addrlen; + + return 0; +} + + void uv_pipe_pending_instances(uv_pipe_t* handle, int count) { } diff --git a/src/win/pipe.c b/src/win/pipe.c index a47a69f4d..73a002640 100644 --- a/src/win/pipe.c +++ b/src/win/pipe.c @@ -44,6 +44,10 @@ static const int64_t eof_timeout = 50; /* ms */ static const int default_pending_pipe_instances = 4; +/* Pipe prefix */ +static char pipe_prefix[] = "\\\\?\\pipe"; +static const int pipe_prefix_len = sizeof(pipe_prefix) - 1; + /* IPC protocol flags. */ #define UV_IPC_RAW_DATA 0x0001 #define UV_IPC_TCP_SERVER 0x0002 @@ -70,7 +74,7 @@ static void eof_timer_close_cb(uv_handle_t* handle); static void uv_unique_pipe_name(char* ptr, char* name, size_t size) { - _snprintf(name, size, "\\\\.\\pipe\\uv\\%p-%u", ptr, GetCurrentProcessId()); + _snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%u", ptr, GetCurrentProcessId()); } @@ -433,7 +437,8 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { } if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(WCHAR))) { - return uv_translate_sys_error(GetLastError()); + err = GetLastError(); + goto error; } /* @@ -1746,3 +1751,105 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { } return 0; } + + +int uv_pipe_getsockname(const uv_pipe_t* handle, char* buf, size_t* len) { + NTSTATUS nt_status; + IO_STATUS_BLOCK io_status; + FILE_NAME_INFORMATION tmp_name_info; + FILE_NAME_INFORMATION* name_info; + WCHAR* name_buf; + unsigned int addrlen; + unsigned int name_size; + unsigned int name_len; + int err; + + name_info = NULL; + + if (handle->handle == INVALID_HANDLE_VALUE) { + *len = 0; + return UV_EINVAL; + } + + nt_status = pNtQueryInformationFile(handle->handle, + &io_status, + &tmp_name_info, + sizeof tmp_name_info, + FileNameInformation); + if (nt_status == STATUS_BUFFER_OVERFLOW) { + name_size = sizeof(*name_info) + tmp_name_info.FileNameLength; + name_info = malloc(name_size); + if (!name_info) + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + + nt_status = pNtQueryInformationFile(handle->handle, + &io_status, + name_info, + name_size, + FileNameInformation); + } + + if (nt_status != STATUS_SUCCESS) { + err = uv_translate_sys_error(pRtlNtStatusToDosError(nt_status)); + goto error; + } + + if (!name_info) { + /* the struct on stack was used */ + name_buf = tmp_name_info.FileName; + name_len = tmp_name_info.FileNameLength; + } else { + name_buf = name_info->FileName; + name_len = name_info->FileNameLength; + } + + if (name_len == 0) { + err = 0; + goto error; + } + + name_len /= sizeof(WCHAR); + + /* check how much space we need */ + addrlen = WideCharToMultiByte(CP_UTF8, + 0, + name_buf, + name_len, + NULL, + 0, + NULL, + NULL); + if (!addrlen) { + err = uv_translate_sys_error(GetLastError()); + goto error; + } else if (pipe_prefix_len + addrlen + 1 > *len) { + /* "\\\\.\\pipe" + name + '\0' */ + err = UV_ENOBUFS; + goto error; + } + + memcpy(buf, pipe_prefix, pipe_prefix_len); + addrlen = WideCharToMultiByte(CP_UTF8, + 0, + name_buf, + name_len, + buf+pipe_prefix_len, + *len-pipe_prefix_len, + NULL, + NULL); + if (!addrlen) { + err = uv_translate_sys_error(GetLastError()); + goto error; + } + + addrlen += pipe_prefix_len; + buf[addrlen++] = '\0'; + *len = addrlen; + + return 0; + +error: + free(name_info); + *len = 0; + return err; +} diff --git a/test/task.h b/test/task.h index b736c375c..3b786a8c6 100644 --- a/test/task.h +++ b/test/task.h @@ -41,8 +41,8 @@ #define TEST_PORT_2 9124 #ifdef _WIN32 -# define TEST_PIPENAME "\\\\.\\pipe\\uv-test" -# define TEST_PIPENAME_2 "\\\\.\\pipe\\uv-test2" +# define TEST_PIPENAME "\\\\?\\pipe\\uv-test" +# define TEST_PIPENAME_2 "\\\\?\\pipe\\uv-test2" #else # define TEST_PIPENAME "/tmp/uv-test-sock" # define TEST_PIPENAME_2 "/tmp/uv-test-sock2" diff --git a/test/test-list.h b/test/test-list.h index a0e323d0a..3c6509f52 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -94,6 +94,8 @@ TEST_DECLARE (pipe_bind_error_inval) TEST_DECLARE (pipe_listen_without_bind) TEST_DECLARE (pipe_connect_bad_name) TEST_DECLARE (pipe_connect_to_file) +TEST_DECLARE (pipe_getsockname) +TEST_DECLARE (pipe_getsockname_abstract) TEST_DECLARE (pipe_server_close) TEST_DECLARE (connection_fail) TEST_DECLARE (connection_fail_doesnt_auto_close) @@ -355,6 +357,8 @@ TASK_LIST_START TEST_ENTRY (pipe_bind_error_addrnotavail) TEST_ENTRY (pipe_bind_error_inval) TEST_ENTRY (pipe_listen_without_bind) + TEST_ENTRY (pipe_getsockname) + TEST_ENTRY (pipe_getsockname_abstract) TEST_ENTRY (connection_fail) TEST_ENTRY (connection_fail_doesnt_auto_close) diff --git a/test/test-pipe-getsockname.c b/test/test-pipe-getsockname.c new file mode 100644 index 000000000..b14f66f49 --- /dev/null +++ b/test/test-pipe-getsockname.c @@ -0,0 +1,118 @@ +/* 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 + +#if defined(__linux__) + #include + #include +#endif + + +static int close_cb_called = 0; +static char abstract_pipe[] = "\0test-pipe"; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +TEST_IMPL(pipe_getsockname) { + uv_pipe_t server; + char buf[1024]; + size_t len; + int r; + + r = uv_pipe_init(uv_default_loop(), &server, 0); + ASSERT(r == 0); + r = uv_pipe_bind(&server, TEST_PIPENAME); + ASSERT(r == 0); + + len = sizeof buf; + r = uv_pipe_getsockname(&server, buf, &len); + ASSERT(r == 0); + + ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(pipe_getsockname_abstract) { +#if defined(__linux__) + uv_pipe_t server; + char buf[1024]; + size_t len; + int r; + int sock; + struct sockaddr_un sun; + socklen_t sun_len; + + sock = socket(AF_LOCAL, SOCK_STREAM, 0); + ASSERT(sock != -1); + + sun_len = sizeof sun; + memset(&sun, 0, sun_len); + sun.sun_family = AF_UNIX; + memcpy(sun.sun_path, abstract_pipe, sizeof abstract_pipe); + + r = bind(sock, (struct sockaddr*)&sun, sun_len); + ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &server, 0); + ASSERT(r == 0); + r = uv_pipe_open(&server, sock); + ASSERT(r == 0); + + len = sizeof buf; + r = uv_pipe_getsockname(&server, buf, &len); + ASSERT(r == 0); + + ASSERT(memcmp(buf, abstract_pipe, sizeof abstract_pipe) == 0); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + close(sock); + + ASSERT(close_cb_called == 1); + MAKE_VALGRIND_HAPPY(); + return 0; +#else + MAKE_VALGRIND_HAPPY(); + return 0; +#endif +} + diff --git a/uv.gyp b/uv.gyp index 7c4c610b1..d84755aab 100644 --- a/uv.gyp +++ b/uv.gyp @@ -337,6 +337,7 @@ 'test/test-ping-pong.c', 'test/test-pipe-bind-error.c', 'test/test-pipe-connect-error.c', + 'test/test-pipe-getsockname.c', 'test/test-pipe-server-close.c', 'test/test-platform-output.c', 'test/test-poll.c',