-- v2: ws2_32: Translate AFD_POLL_RESET to FD_CLOSE plus WSAECONNABORTED in WSAEnumNetworkEvents(). server: Translate AFD_POLL_RESET to FD_CLOSE plus WSAECONNABORTED in window messages. server: Return ERROR_CONNECTION_RESET when trying to recv() on a reset socket. ws2_32/tests: Work around a Linux bug. ws2_32/tests: Test send() after TCP reset. http.sys: Keep connection sockets open after sending a 400 response. ws2_32/tests: Test sending data to a peer is closed().
From: Zebediah Figura zfigura@codeweavers.com
--- dlls/ws2_32/tests/sock.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 3e6faaffa4c..644f88ece66 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -8675,6 +8675,26 @@ static void test_shutdown(void) closesocket(client); closesocket(server);
+ /* Send data to a peer which is closed. */ + + tcp_socketpair(&client, &server); + + WSASetLastError(0xdeadbeef); + ret = shutdown(client, SD_SEND); + ok(!ret, "expected success\n"); + ok(!WSAGetLastError() || WSAGetLastError() == 0xdeadbeef /* < 7 */, "got error %u\n", WSAGetLastError()); + closesocket(client); + + ret = send(server, "test", 5, 0); + ok(ret == 5, "got %d\n", ret); + + WSASetLastError(0xdeadbeef); + ret = recv(server, buffer, sizeof(buffer), 0); + ok(ret == -1, "got %d\n", ret); + todo_wine ok(WSAGetLastError() == WSAECONNABORTED, "got error %u\n", WSAGetLastError()); + + closesocket(server); + /* Test shutting down with async I/O pending. */
client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
From: Zebediah Figura zfigura@codeweavers.com
Tests show that the socket is shut down, but sending further data doesn't result in a TCP reset, so we need to keep the socket itself open until the TCP connection is closed. --- dlls/http.sys/http.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-)
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index e16805ccbdb..a1cb77cdfe3 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -19,6 +19,7 @@ */
#include <assert.h> +#include <stdbool.h> #include "ntstatus.h" #define WIN32_NO_STATUS #include "wine/http.h" @@ -55,6 +56,7 @@ struct connection
char *buffer; unsigned int len, size; + bool shutdown;
/* If there is a request fully received and waiting to be read, the * "available" parameter will be TRUE. Either there is no queue matching @@ -112,7 +114,7 @@ static struct list request_queues = LIST_INIT(request_queues); static void accept_connection(SOCKET socket) { struct connection *conn; - ULONG true = 1; + ULONG one = 1; SOCKET peer;
if ((peer = accept(socket, NULL, NULL)) == INVALID_SOCKET) @@ -135,15 +137,22 @@ static void accept_connection(SOCKET socket) } conn->size = 8192; WSAEventSelect(peer, request_event, FD_READ | FD_CLOSE); - ioctlsocket(peer, FIONBIO, &true); + ioctlsocket(peer, FIONBIO, &one); conn->socket = peer; list_add_head(&connections, &conn->entry); }
-static void close_connection(struct connection *conn) +static void shutdown_connection(struct connection *conn) { heap_free(conn->buffer); shutdown(conn->socket, SD_BOTH); + conn->shutdown = true; +} + +static void close_connection(struct connection *conn) +{ + if (!conn->shutdown) + shutdown_connection(conn); closesocket(conn->socket); list_remove(&conn->entry); heap_free(conn); @@ -594,12 +603,24 @@ static void send_400(struct connection *conn) strcat(buffer, response_body); if (send(conn->socket, buffer, strlen(buffer), 0) < 0) ERR("Failed to send 400 response, error %u.\n", WSAGetLastError()); + shutdown_connection(conn); }
static void receive_data(struct connection *conn) { int len, ret;
+ if (conn->shutdown) + { + WSANETWORKEVENTS events; + + if ((ret = WSAEnumNetworkEvents(conn->socket, NULL, &events)) < 0) + ERR("Failed to enumerate network events, error %u.\n", WSAGetLastError()); + if (events.lNetworkEvents & FD_CLOSE) + close_connection(conn); + return; + } + /* We might be waiting for an IRP, but always call recv() anyway, since we * might have been woken up by the socket closing. */ if ((len = recv(conn->socket, conn->buffer + conn->len, conn->size - conn->len, 0)) <= 0) @@ -654,7 +675,6 @@ static void receive_data(struct connection *conn) { WARN("Failed to parse request; shutting down connection.\n"); send_400(conn); - close_connection(conn); } }
@@ -715,8 +735,8 @@ static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp) struct listening_socket *listening_sock; char *url, *endptr; size_t queue_url_len, new_url_len; - ULONG true = 1; SOCKET s = INVALID_SOCKET; + ULONG one = 1;
TRACE("host %s, context %s.\n", debugstr_a(params->url), wine_dbgstr_longlong(params->context));
@@ -823,7 +843,7 @@ static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp) listening_sock->socket = s; list_add_head(&listening_sockets, &listening_sock->entry);
- ioctlsocket(s, FIONBIO, &true); + ioctlsocket(s, FIONBIO, &one); WSAEventSelect(s, request_event, FD_ACCEPT); }
@@ -1002,7 +1022,6 @@ static NTSTATUS http_send_response(struct request_queue *queue, IRP *irp) { WARN("Failed to parse request; shutting down connection.\n"); send_400(conn); - close_connection(conn); } } else
From: Zebediah Figura zfigura@codeweavers.com
--- dlls/ws2_32/tests/sock.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 644f88ece66..a83c06839db 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -12891,6 +12891,11 @@ static void test_tcp_reset(void) todo_wine ok(ret == -1, "got %d\n", ret); todo_wine ok(WSAGetLastError() == WSAECONNRESET, "got error %u\n", WSAGetLastError());
+ WSASetLastError(0xdeadbeef); + ret = send(client, "data", 5, 0); + ok(ret == -1, "got %d\n", ret); + ok(WSAGetLastError() == WSAECONNRESET, "got error %u\n", WSAGetLastError()); + check_poll(client, POLLERR | POLLHUP | POLLWRNORM);
FD_ZERO(&readfds);
From: Zebediah Figura zfigura@codeweavers.com
--- dlls/ws2_32/tests/sock.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index a83c06839db..4247a54d157 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -3900,6 +3900,7 @@ static void test_select(void) ret = getsockopt(fdWrite, SOL_SOCKET, SO_ERROR, (char*)&id, &len); ok(!ret, "getsockopt failed with %d\n", WSAGetLastError()); ok(id == 0, "expected 0, got %ld\n", id); + set_blocking(fdRead, FALSE);
/* When data is received the receiver gets the read descriptor */ ret = send(fdWrite, "1234", 4, 0); @@ -3960,6 +3961,11 @@ static void test_select(void) ok(ret == 1, "expected 1, got %d\n", ret); ok(tmp_buf[0] == 'A', "expected 'A', got 0x%02X\n", tmp_buf[0]);
+ /* work around some odd/buggy Linux behaviour */ + ret = recv(fdRead, tmp_buf, sizeof(tmp_buf), 0); + todo_wine ok(ret == -1, "got %d\n", ret); + todo_wine ok(GetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError()); + /* When the connection is closed the socket is set in the read descriptor */ ret = closesocket(fdRead); ok(ret == 0, "expected 0, got %d\n", ret);
From: Zebediah Figura zfigura@codeweavers.com
--- dlls/ws2_32/tests/sock.c | 20 ++++++++++---------- server/sock.c | 5 ++++- 2 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 4247a54d157..e37887fadc8 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -9850,9 +9850,9 @@ static void test_completion_port(void)
/* Somehow a hard shutdown doesn't work on my Linux box. It seems SO_LINGER is ignored. */ iret = WSARecv(dest, &bufs, 1, &num_bytes, &flags, &ov, NULL); - todo_wine ok(iret == SOCKET_ERROR, "WSARecv failed - %d\n", iret); - todo_wine ok(GetLastError() == WSAECONNRESET, "Last error was %ld\n", GetLastError()); - todo_wine ok(num_bytes == 0xdeadbeef, "Managed to read %ld\n", num_bytes); + ok(iret == SOCKET_ERROR, "WSARecv failed - %d\n", iret); + ok(GetLastError() == WSAECONNRESET, "Last error was %ld\n", GetLastError()); + ok(num_bytes == 0xdeadbeef, "Managed to read %ld\n", num_bytes);
SetLastError(0xdeadbeef); key = 0xdeadbeef; @@ -9860,11 +9860,11 @@ static void test_completion_port(void) olp = (WSAOVERLAPPED *)0xdeadbeef;
bret = GetQueuedCompletionStatus( io_port, &num_bytes, &key, &olp, 200 ); - todo_wine ok(bret == FALSE, "GetQueuedCompletionStatus returned %u\n", bret ); - todo_wine ok(GetLastError() == WAIT_TIMEOUT, "Last error was %ld\n", GetLastError()); - todo_wine ok(key == 0xdeadbeef, "Key is %Iu\n", key); - todo_wine ok(num_bytes == 0xdeadbeef, "Number of bytes transferred is %lu\n", num_bytes); - todo_wine ok(!olp, "Overlapped structure is at %p\n", olp); + ok(bret == FALSE, "GetQueuedCompletionStatus returned %u\n", bret ); + ok(GetLastError() == WAIT_TIMEOUT, "Last error was %ld\n", GetLastError()); + ok(key == 0xdeadbeef, "Key is %Iu\n", key); + ok(num_bytes == 0xdeadbeef, "Number of bytes transferred is %lu\n", num_bytes); + ok(!olp, "Overlapped structure is at %p\n", olp);
closesocket(dest);
@@ -12894,8 +12894,8 @@ static void test_tcp_reset(void) WSASetLastError(0xdeadbeef); size = 0xdeadbeef; ret = WSARecv(client, &wsabuf, 1, &size, &flags, &overlapped, NULL); - todo_wine ok(ret == -1, "got %d\n", ret); - todo_wine ok(WSAGetLastError() == WSAECONNRESET, "got error %u\n", WSAGetLastError()); + ok(ret == -1, "got %d\n", ret); + ok(WSAGetLastError() == WSAECONNRESET, "got error %u\n", WSAGetLastError());
WSASetLastError(0xdeadbeef); ret = send(client, "data", 5, 0); diff --git a/server/sock.c b/server/sock.c index 7d7e470be28..49ce4d254f6 100644 --- a/server/sock.c +++ b/server/sock.c @@ -3461,7 +3461,10 @@ DECL_HANDLER(recv_socket) if (!req->force_async && !sock->nonblocking && is_fd_overlapped( fd )) timeout = (timeout_t)sock->rcvtimeo * -10000;
- if (sock->rd_shutdown) status = STATUS_PIPE_DISCONNECTED; + if (sock->rd_shutdown) + status = STATUS_PIPE_DISCONNECTED; + else if (sock->reset) + status = STATUS_CONNECTION_RESET; else if (!async_queued( &sock->read_q )) { /* If read_q is not empty, we cannot really tell if the already queued
From: Zebediah Figura zfigura@codeweavers.com
--- dlls/ws2_32/tests/sock.c | 2 +- server/sock.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index e37887fadc8..d777be96df3 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -6446,7 +6446,7 @@ static void test_close_events(struct event_test_ctx *ctx)
close_with_rst(client);
- check_events_todo_msg(ctx, MAKELONG(FD_CLOSE, WSAECONNABORTED), 0, 200); + check_events(ctx, MAKELONG(FD_CLOSE, WSAECONNABORTED), 0, 200); check_events(ctx, 0, 0, 0); select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ); if (ctx->is_message) diff --git a/server/sock.c b/server/sock.c index 49ce4d254f6..072aac67723 100644 --- a/server/sock.c +++ b/server/sock.c @@ -631,7 +631,11 @@ static void sock_wake_up( struct sock *sock ) enum afd_poll_bit event = event_bitorder[i]; if (events & (1 << event)) { - lparam_t lparam = afd_poll_flag_to_win32(1 << event) | (sock_get_error( sock->errors[event] ) << 16); + lparam_t lparam; + if (event == AFD_POLL_BIT_RESET) + lparam = FD_CLOSE | (WSAECONNABORTED << 16); + else + lparam = afd_poll_flag_to_win32(1 << event) | (sock_get_error( sock->errors[event] ) << 16); post_message( sock->window, sock->message, sock->wparam, lparam ); } }
From: Zebediah Figura zfigura@codeweavers.com
--- dlls/ws2_32/socket.c | 5 ++++- dlls/ws2_32/tests/sock.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 9d51fef1adb..3171f0cd5ca 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -3584,7 +3584,10 @@ int WINAPI WSAEnumNetworkEvents( SOCKET s, WSAEVENT event, WSANETWORKEVENTS *ret if (ret_events->lNetworkEvents & FD_CLOSE) { if (!(ret_events->iErrorCode[FD_CLOSE_BIT] = NtStatusToWSAError( params.status[AFD_POLL_BIT_HUP] ))) - ret_events->iErrorCode[FD_CLOSE_BIT] = NtStatusToWSAError( params.status[AFD_POLL_BIT_RESET] ); + { + if (params.flags & AFD_POLL_RESET) + ret_events->iErrorCode[FD_CLOSE_BIT] = WSAECONNABORTED; + } } } SetLastError( NtStatusToWSAError( status ) ); diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index d777be96df3..c652534890c 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -5602,7 +5602,7 @@ static void check_events_(int line, struct event_test_ctx *ctx, for (i = 0; i < ARRAY_SIZE(events.iErrorCode); ++i) { if ((1u << i) == LOWORD(flag1) && (events.lNetworkEvents & LOWORD(flag1))) - todo_wine_if (HIWORD(flag1)) ok_(__FILE__, line)(events.iErrorCode[i] == HIWORD(flag1), + ok_(__FILE__, line)(events.iErrorCode[i] == HIWORD(flag1), "got error code %d for event %#x\n", events.iErrorCode[i], 1u << i); if ((1u << i) == LOWORD(flag2) && (events.lNetworkEvents & LOWORD(flag2))) ok_(__FILE__, line)(events.iErrorCode[i] == HIWORD(flag2),