From 6827fa34513b35ae3bb4fe5c0c229b17a484f959 Mon Sep 17 00:00:00 2001 From: "Maciej Szeptuch (Neverous)" Date: Mon, 31 Jul 2017 01:29:12 +0200 Subject: [PATCH] win: allow bound/connected socket in uv_tcp_open() On Unix you can pass bound and even connected socket to uv_tcp_open and it will work as expected: you can run uv_listen or other io functions (uv_read, uv_write) on it without problems. On windows on the other hand the function always assumes to have clean socket and for example uv_listen will try to bind it again, and uv_read/write will return with errors about unreadable streams (basically invalid internal flags). To check if socket is already bound uv_tcp_getsockname is called which on windows returns error when socket is unbound. To further differentiate connected one from just bound, uv_tcp_getpeername also returns error but when target socket is not connected. PR-URL: https://github.com/libuv/libuv/pull/1447 Reviewed-By: Bartosz Sosnowski Reviewed-By: Ben Noordhuis --- src/win/tcp.c | 15 ++++++++++++ test/test-list.h | 5 ++++ test/test-tcp-open.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/src/win/tcp.c b/src/win/tcp.c index 972539f4..e63a63e7 100644 --- a/src/win/tcp.c +++ b/src/win/tcp.c @@ -1446,6 +1446,8 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { WSAPROTOCOL_INFOW protocol_info; int opt_len; int err; + struct sockaddr_storage saddr; + int saddr_len; /* Detect the address family of the socket. */ opt_len = (int) sizeof protocol_info; @@ -1466,6 +1468,19 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { return uv_translate_sys_error(err); } + /* Support already active socket. */ + saddr_len = sizeof(saddr); + if (!uv_tcp_getsockname(handle, (struct sockaddr*) &saddr, &saddr_len)) { + /* Socket is already bound. */ + handle->flags |= UV_HANDLE_BOUND; + saddr_len = sizeof(saddr); + if (!uv_tcp_getpeername(handle, (struct sockaddr*) &saddr, &saddr_len)) { + /* Socket is already connected. */ + uv_connection_init((uv_stream_t*) handle); + handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; + } + } + return 0; } diff --git a/test/test-list.h b/test/test-list.h index c2bb5616..a0ff96a7 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -81,6 +81,8 @@ TEST_DECLARE (tcp_try_write) TEST_DECLARE (tcp_write_queue_order) TEST_DECLARE (tcp_open) TEST_DECLARE (tcp_open_twice) +TEST_DECLARE (tcp_open_bound) +TEST_DECLARE (tcp_open_connected) TEST_DECLARE (tcp_connect_error_after_write) TEST_DECLARE (tcp_shutdown_after_write) TEST_DECLARE (tcp_bind_error_addrinuse) @@ -491,6 +493,9 @@ TASK_LIST_START TEST_ENTRY (tcp_open) TEST_HELPER (tcp_open, tcp4_echo_server) TEST_ENTRY (tcp_open_twice) + TEST_ENTRY (tcp_open_bound) + TEST_ENTRY (tcp_open_connected) + TEST_HELPER (tcp_open_connected, tcp4_echo_server) TEST_ENTRY (tcp_shutdown_after_write) TEST_HELPER (tcp_shutdown_after_write, tcp4_echo_server) diff --git a/test/test-tcp-open.c b/test/test-tcp-open.c index 6c8d43d0..cb74c50e 100644 --- a/test/test-tcp-open.c +++ b/test/test-tcp-open.c @@ -218,3 +218,60 @@ TEST_IMPL(tcp_open_twice) { MAKE_VALGRIND_HAPPY(); return 0; } + + +TEST_IMPL(tcp_open_bound) { + struct sockaddr_in addr; + uv_tcp_t server; + uv_os_sock_t sock; + + startup(); + sock = create_tcp_socket(); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); + + ASSERT(0 == bind(sock, (struct sockaddr*) &addr, sizeof(addr))); + + ASSERT(0 == uv_tcp_open(&server, sock)); + + ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, NULL)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_open_connected) { + struct sockaddr_in addr; + uv_tcp_t client; + uv_os_sock_t sock; + uv_buf_t buf = uv_buf_init("PING", 4); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + startup(); + sock = create_tcp_socket(); + + ASSERT(0 == connect(sock, (struct sockaddr*) &addr, sizeof(addr))); + + ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); + + ASSERT(0 == uv_tcp_open(&client, sock)); + + ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &client, &buf, 1, write_cb)); + + ASSERT(0 == uv_shutdown(&shutdown_req, (uv_stream_t*) &client, shutdown_cb)); + + ASSERT(0 == uv_read_start((uv_stream_t*) &client, alloc_cb, read_cb)); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(shutdown_cb_called == 1); + ASSERT(write_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +}