-- v3: 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 which 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 f9cc1482c41..951c4abee2e 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -9416,6 +9416,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 52887259aa7..093a00f1b12 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" @@ -54,6 +55,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 @@ -111,7 +113,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) @@ -134,15 +136,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) { 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); free(conn); @@ -593,12 +602,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) @@ -653,7 +674,6 @@ static void receive_data(struct connection *conn) { WARN("Failed to parse request; shutting down connection.\n"); send_400(conn); - close_connection(conn); } }
@@ -714,7 +734,7 @@ 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, value; + ULONG one = 1, value; SOCKET s = INVALID_SOCKET;
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 951c4abee2e..9f1da1039c4 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -13660,6 +13660,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
This was reported upstream: https://bugzilla.kernel.org/show_bug.cgi?id=216259 --- dlls/ws2_32/tests/sock.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 9f1da1039c4..268b1e6d691 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -4422,6 +4422,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); @@ -4482,6 +4483,14 @@ 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]);
+ /* Linux has some odd behaviour (probably a bug) where receiving OOB, + * setting SO_OOBINLINE, and then calling recv() again will cause the same + * data to be received twice. Avoid that messing with further tests by + * calling recv() here. */ + 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 268b1e6d691..8530d80d3d9 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -10594,9 +10594,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; @@ -10604,11 +10604,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);
@@ -13666,8 +13666,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 f774cded733..f2d1dce8112 100644 --- a/server/sock.c +++ b/server/sock.c @@ -3830,7 +3830,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 8530d80d3d9..73eaeab734a 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -7134,7 +7134,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 f2d1dce8112..bd441d822a8 100644 --- a/server/sock.c +++ b/server/sock.c @@ -800,7 +800,11 @@ static void post_sock_messages( 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 9da3ccb285f..6aab249a1b8 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -3629,7 +3629,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 73eaeab734a..c2345c1fa60 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -6290,7 +6290,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),
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=140129
Your paranoid android.
=== w1064_adm (64 bit report) ===
ws2_32: sock.c:6198: Test failed: failed to wait for thread termination: 0
=== w10pro64_ar (64 bit report) ===
ws2_32: sock.c:4736: Test failed: expected -1, got 0 sock.c:4737: Test failed: got 0
Rebased again, after over a year. Looks like the test failures are gone now.