From: Ally Sommers dropbear.sh@gmail.com
--- dlls/ws2_32/tests/sock.c | 2 +- server/sock.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 31dff1a11c7..1278dfb0fa3 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -2298,7 +2298,7 @@ static void test_reuseaddr(void) rc = listen(s1, 1); ok(!rc, "got error %d.\n", WSAGetLastError()); rc = listen(s2, 1); - todo_wine ok(!rc, "got error %d.\n", WSAGetLastError()); + ok(!rc, "got error %d.\n", WSAGetLastError()); rc = connect(s3, tests[i].addr_loopback, tests[i].addrlen); ok(!rc, "got error %d.\n", WSAGetLastError());
diff --git a/server/sock.c b/server/sock.c index ac55a5448f7..4150f36bd31 100644 --- a/server/sock.c +++ b/server/sock.c @@ -193,6 +193,7 @@ struct bound_addr union unix_sockaddr addr; int match_any_addr; int reuse_count; + struct sock *sock; };
#define MAX_ICMP_HISTORY_LENGTH 8 @@ -264,6 +265,9 @@ struct sock unsigned int reset : 1; /* did we get a TCP reset? */ unsigned int reuseaddr : 1; /* winsock SO_REUSEADDR option value */ unsigned int exclusiveaddruse : 1; /* winsock SO_EXCLUSIVEADDRUSE option value */ + unsigned int listen_queued : 1; /* listen() while bound to an in-use address? */ + int listen_backlog; /* stored argument for deferred listen() */ + struct list bind_list; /* ordered list of sockets bound to the same address */ };
static int is_tcp_socket( struct sock *sock ) @@ -397,10 +401,12 @@ static struct bound_addr *register_bound_address( struct sock *sock, const union return NULL; } ++bound_addr->reuse_count; + list_add_tail(&bound_addr->sock->bind_list, &sock->bind_list); } else { bound_addr->reuse_count = sock->reuseaddr ? 1 : -1; + bound_addr->sock = sock; } return bound_addr; } @@ -1663,7 +1669,29 @@ static int sock_close_handle( struct object *obj, struct process *process, obj_h
if (signaled) complete_async_poll( poll_req, STATUS_SUCCESS ); } + + list_remove(&sock->bind_list); + + close( get_unix_fd( sock->fd ) ); + + if (sock->bound_addr[0] && sock->bound_addr[0]->sock == sock) + { + struct sock *new_sock; + struct list *next_list_elem = list_next( &sock->bind_list, &sock->bind_list ); + if (!next_list_elem) + goto no_next_socket_or_deferred_listen; + + new_sock = LIST_ENTRY( next_list_elem, struct sock, bind_list ); + sock->bound_addr[0]->sock = new_sock; + if (sock->bound_addr[1]) + sock->bound_addr[1]->sock = new_sock; + + if (new_sock->listen_queued) + if (listen( get_unix_fd( new_sock->fd ), new_sock->listen_backlog ) == -1 && debug_level) + fprintf( stderr, "FIXME: deferred listen threw errno %d (%s)\n", errno, strerror(errno) ); + } } +no_next_socket_or_deferred_listen: return async_close_obj_handle( obj, process, handle ); }
@@ -1740,6 +1768,8 @@ static struct sock *create_socket(void) sock->rcvtimeo = 0; sock->sndtimeo = 0; sock->icmp_fixup_data_len = 0; + sock->listen_queued = 0; + sock->listen_backlog = 0; sock->bound_addr[0] = sock->bound_addr[1] = NULL; init_async_queue( &sock->read_q ); init_async_queue( &sock->write_q ); @@ -1749,6 +1779,7 @@ static struct sock *create_socket(void) init_async_queue( &sock->poll_q ); memset( sock->errors, 0, sizeof(sock->errors) ); list_init( &sock->accept_list ); + list_init( &sock->bind_list ); return sock; }
@@ -2539,12 +2570,20 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) return; }
+ if (sock->bound_addr[0] && sock->bound_addr[0]->sock != sock) + { + sock->listen_backlog = params->backlog; + sock->listen_queued = 1; + goto listen_skip_error; + } + if (listen( unix_fd, params->backlog ) < 0) { set_error( sock_get_ntstatus( errno ) ); return; }
+ listen_skip_error: sock->state = SOCK_LISTENING;
/* a listening socket can no longer be accepted into */