From 26880b0c908cefc037f5b0fb8f32222bdb3ee042 Mon Sep 17 00:00:00 2001 From: Henry Rawas Date: Fri, 1 Jul 2011 17:04:05 -0700 Subject: [PATCH] win: IPv6 connect --- test/echo-server.c | 35 ++++++++++++- test/test-list.h | 4 ++ test/test-ping-pong.c | 37 ++++++++++++++ uv-unix.c | 7 +++ uv-win.c | 113 ++++++++++++++++++++++++++++++++++++++---- uv.h | 1 + 6 files changed, 186 insertions(+), 11 deletions(-) diff --git a/test/echo-server.c b/test/echo-server.c index 1243a9a48..ab5491c2f 100644 --- a/test/echo-server.c +++ b/test/echo-server.c @@ -34,6 +34,9 @@ typedef struct { static int server_closed; static uv_tcp_t server; +static int server6_closed; +static uv_tcp_t server6; + static void after_write(uv_req_t* req, int status); static void after_read(uv_stream_t*, ssize_t nread, uv_buf_t buf); @@ -97,6 +100,8 @@ static void after_read(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { if (buf.base[i] == 'Q') { uv_close((uv_handle_t*)&server, on_server_close); server_closed = 1; + uv_close((uv_handle_t*)&server6, on_server_close); + server6_closed = 1; } } } @@ -129,6 +134,9 @@ static void on_connection(uv_handle_t* server, int status) { uv_tcp_t* handle; int r; + if (status != 0) { + fprintf(stderr, "Connect error %d\n", uv_last_error()); + } ASSERT(status == 0); handle = (uv_tcp_t*) malloc(sizeof *handle); @@ -136,6 +144,9 @@ static void on_connection(uv_handle_t* server, int status) { uv_tcp_init(handle); + /* associate server with stream */ + handle->data = server; + r = uv_accept(server, (uv_stream_t*)handle); ASSERT(r == 0); @@ -145,12 +156,13 @@ static void on_connection(uv_handle_t* server, int status) { static void on_server_close(uv_handle_t* handle) { - ASSERT(handle == (uv_handle_t*)&server); + ASSERT(handle == (uv_handle_t*)&server || handle == (uv_handle_t*)&server6); } static int echo_start(int port) { struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", port); + struct sockaddr_in6 addr6 = uv_ip6_addr("::1", port); int r; r = uv_tcp_init(&server); @@ -174,6 +186,27 @@ static int echo_start(int port) { return 1; } + r = uv_tcp_init(&server6); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Socket creation error\n"); + return 1; + } + + r = uv_tcp_bind6(&server6, addr6); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Bind6 error\n"); + return 1; + } + + r = uv_tcp_listen(&server6, 128, on_connection); + if (r) { + /* TODO: Error codes */ + fprintf(stderr, "Listen error\n"); + return 1; + } + return 0; } diff --git a/test/test-list.h b/test/test-list.h index ddedfa1be..190574aca 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -20,6 +20,7 @@ */ TEST_DECLARE (ping_pong) +TEST_DECLARE (ping_pong_v6) TEST_DECLARE (delayed_accept) TEST_DECLARE (tcp_writealot) TEST_DECLARE (bind_error_addrinuse) @@ -60,6 +61,9 @@ TASK_LIST_START TEST_ENTRY (ping_pong) TEST_HELPER (ping_pong, echo_server) + TEST_ENTRY (ping_pong_v6) + TEST_HELPER (ping_pong_v6, echo_server) + TEST_ENTRY (delayed_accept) TEST_ENTRY (tcp_writealot) diff --git a/test/test-ping-pong.c b/test/test-ping-pong.c index 1858adc82..14208e751 100644 --- a/test/test-ping-pong.c +++ b/test/test-ping-pong.c @@ -175,3 +175,40 @@ TEST_IMPL(ping_pong) { return 0; } + + +/* same ping-pong test, but using IPv6 connection */ +static void pinger_v6_new() { + int r; + struct sockaddr_in6 server_addr = uv_ip6_addr("::1", TEST_PORT); + pinger_t *pinger; + + pinger = (pinger_t*)malloc(sizeof(*pinger)); + pinger->state = 0; + pinger->pongs = 0; + + /* Try to connec to the server and do NUM_PINGS ping-pongs. */ + r = uv_tcp_init(&pinger->tcp); + pinger->tcp.data = pinger; + ASSERT(!r); + + /* We are never doing multiple reads/connects at a time anyway. */ + /* so these handles can be pre-initialized. */ + uv_req_init(&pinger->connect_req, (uv_handle_t*)(&pinger->tcp), + pinger_on_connect); + + r = uv_tcp_connect6(&pinger->connect_req, server_addr); + ASSERT(!r); +} + + +TEST_IMPL(ping_pong_v6) { + uv_init(); + + pinger_v6_new(); + uv_run(); + + ASSERT(completed_pingers == 1); + + return 0; +} diff --git a/uv-unix.c b/uv-unix.c index db3ce1187..3ca728377 100644 --- a/uv-unix.c +++ b/uv-unix.c @@ -943,6 +943,13 @@ int uv_tcp_connect(uv_req_t* req, struct sockaddr_in addr) { } +/* TODO: Implement IPv6 Connect for UNIX */ +int uv_tcp_connect6(uv_req_t* req, struct sockaddr_in6 addr) { + uv_err_new_artificial((uv_handle_t*)req->handle, UV_EAFNOSUPPORT); + return -1; +} + + static size_t uv__buf_count(uv_buf_t bufs[], int bufcnt) { size_t total = 0; int i; diff --git a/uv-win.c b/uv-win.c index 61719798d..e6b8492d4 100644 --- a/uv-win.c +++ b/uv-win.c @@ -139,6 +139,12 @@ static LPFN_ACCEPTEX pAcceptEx; static LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs; static LPFN_DISCONNECTEX pDisconnectEx; static LPFN_TRANSMITFILE pTransmitFile; +/* need IPv6 versions of winsock extension functions */ +static LPFN_CONNECTEX pConnectEx6; +static LPFN_ACCEPTEX pAcceptEx6; +static LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs6; +static LPFN_DISCONNECTEX pDisconnectEx6; +static LPFN_TRANSMITFILE pTransmitFile6; /* @@ -157,6 +163,7 @@ static LPFN_TRANSMITFILE pTransmitFile; #define UV_HANDLE_SHUT 0x0200 #define UV_HANDLE_ENDGAME_QUEUED 0x0400 #define UV_HANDLE_BIND_ERROR 0x1000 +#define UV_HANDLE_IPV6 0x2000 /* * Private uv_req flags. @@ -208,6 +215,7 @@ static int uv_refs_ = 0; /* Ip address used to bind to any port at any interface */ static struct sockaddr_in uv_addr_ip4_any_; +static struct sockaddr_in6 uv_addr_ip6_any_; /* A zero-size buffer for use by uv_read */ @@ -417,6 +425,7 @@ void uv_init() { int errorno; LARGE_INTEGER timer_frequency; SOCKET dummy; + SOCKET dummy6; /* Initialize winsock */ errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); @@ -426,6 +435,7 @@ void uv_init() { /* Set implicit binding address used by connectEx */ uv_addr_ip4_any_ = uv_ip4_addr("0.0.0.0", 0); + uv_addr_ip6_any_ = uv_ip6_addr("::1", 0); /* Retrieve the needed winsock extension function pointers. */ dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); @@ -453,6 +463,33 @@ void uv_init() { uv_fatal_error(WSAGetLastError(), "closesocket"); } +/* need IPv6 versions of winsock extension functions */ + dummy6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP); + if (dummy == INVALID_SOCKET) { + uv_fatal_error(WSAGetLastError(), "socket"); + } + + uv_get_extension_function(dummy6, + wsaid_connectex, + (void**)&pConnectEx6); + uv_get_extension_function(dummy6, + wsaid_acceptex, + (void**)&pAcceptEx6); + uv_get_extension_function(dummy6, + wsaid_getacceptexsockaddrs, + (void**)&pGetAcceptExSockAddrs6); + uv_get_extension_function(dummy6, + wsaid_disconnectex, + (void**)&pDisconnectEx6); + uv_get_extension_function(dummy6, + wsaid_transmitfile, + (void**)&pTransmitFile6); + + if (closesocket(dummy6) == SOCKET_ERROR) { + uv_fatal_error(WSAGetLastError(), "closesocket"); + } + + /* Create an I/O completion port */ uv_iocp_ = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); if (uv_iocp_ == NULL) { @@ -815,7 +852,7 @@ int uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr) { uv_set_sys_error(WSAEFAULT); return -1; } - + handle->flags |= UV_HANDLE_IPV6; return uv__bind(handle, AF_INET6, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6)); } @@ -825,6 +862,8 @@ static void uv_queue_accept(uv_tcp_t* handle) { BOOL success; DWORD bytes; SOCKET accept_socket; + short family; + LPFN_ACCEPTEX pAcceptExFamily; assert(handle->flags & UV_HANDLE_LISTENING); assert(handle->accept_socket == INVALID_SOCKET); @@ -835,8 +874,17 @@ static void uv_queue_accept(uv_tcp_t* handle) { req->type = UV_ACCEPT; req->flags |= UV_REQ_PENDING; + /* choose family and extension function */ + if ((handle->flags & UV_HANDLE_IPV6) != 0) { + family = AF_INET6; + pAcceptExFamily = pAcceptEx6; + } else { + family = AF_INET; + pAcceptExFamily = pAcceptEx; + } + /* Open a socket for the accepted connection. */ - accept_socket = socket(AF_INET, SOCK_STREAM, 0); + accept_socket = socket(family, SOCK_STREAM, 0); if (accept_socket == INVALID_SOCKET) { req->error = uv_new_sys_error(WSAGetLastError()); uv_insert_pending_req(req); @@ -846,14 +894,14 @@ static void uv_queue_accept(uv_tcp_t* handle) { /* Prepare the overlapped structure. */ memset(&(req->overlapped), 0, sizeof(req->overlapped)); - success = pAcceptEx(handle->socket, - accept_socket, - (void*)&handle->accept_buffer, - 0, - sizeof(struct sockaddr_storage), - sizeof(struct sockaddr_storage), - &bytes, - &req->overlapped); + success = pAcceptExFamily(handle->socket, + accept_socket, + (void*)&handle->accept_buffer, + 0, + sizeof(struct sockaddr_storage), + sizeof(struct sockaddr_storage), + &bytes, + &req->overlapped); if (!success && WSAGetLastError() != ERROR_IO_PENDING) { /* Make this req pending reporting an error. */ @@ -1045,6 +1093,51 @@ int uv_tcp_connect(uv_req_t* req, struct sockaddr_in addr) { } +int uv_tcp_connect6(uv_req_t* req, struct sockaddr_in6 addr) { + int addrsize = sizeof(struct sockaddr_in6); + BOOL success; + DWORD bytes; + uv_tcp_t* handle = (uv_tcp_t*)req->handle; + + assert(!(req->flags & UV_REQ_PENDING)); + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + uv_last_error_ = handle->error; + return -1; + } + + if (addr.sin6_family != AF_INET6) { + uv_set_sys_error(WSAEFAULT); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_tcp_bind6(handle, uv_addr_ip6_any_) < 0) + return -1; + + memset(&req->overlapped, 0, sizeof(req->overlapped)); + req->type = UV_CONNECT; + + success = pConnectEx6(handle->socket, + (struct sockaddr*)&addr, + addrsize, + NULL, + 0, + &bytes, + &req->overlapped); + + if (!success && WSAGetLastError() != ERROR_IO_PENDING) { + uv_set_sys_error(WSAGetLastError()); + return -1; + } + + req->flags |= UV_REQ_PENDING; + handle->reqs_pending++; + + return 0; +} + + static size_t uv_count_bufs(uv_buf_t bufs[], int count) { size_t bytes = 0; int i; diff --git a/uv.h b/uv.h index e7df5b659..bd84c516f 100644 --- a/uv.h +++ b/uv.h @@ -282,6 +282,7 @@ int uv_tcp_bind(uv_tcp_t* handle, struct sockaddr_in); int uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6); int uv_tcp_connect(uv_req_t* req, struct sockaddr_in); +int uv_tcp_connect6(uv_req_t* req, struct sockaddr_in6); int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb);