diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index d31f0b4..28a9ab1 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1004,8 +1004,8 @@ static inline DWORD NtStatusToWSAError( const DWORD status ) { case STATUS_SUCCESS: wserr = 0; break; case STATUS_PENDING: wserr = WSA_IO_PENDING; break; + case STATUS_INVALID_HANDLE: case STATUS_OBJECT_TYPE_MISMATCH: wserr = WSAENOTSOCK; break; - case STATUS_INVALID_HANDLE: wserr = WSAEBADF; break; case STATUS_INVALID_PARAMETER: wserr = WSAEINVAL; break; case STATUS_PIPE_DISCONNECTED: wserr = WSAESHUTDOWN; break; case STATUS_NETWORK_BUSY: wserr = WSAEALREADY; break; @@ -1109,6 +1109,50 @@ static void _sync_sock_state(SOCKET s) (void)_is_blocking(s, &dummy); } +static SOCKET* _get_all_sockets(void) +{ +#define MAX_SOCKS_PER_REQ 128 + unsigned int index = 0, sock_count = 0, buf_sz; + SOCKET *socks, *new_socks; + NTSTATUS status; + + /* +1 for list end */ + buf_sz = sizeof(SOCKET) * (MAX_SOCKS_PER_REQ + 1); + socks = HeapAlloc(GetProcessHeap(), 0, buf_sz); + if (!socks) return NULL; + + do + { + SERVER_START_REQ( get_socks_batch ) + { + req->start_index = index; + wine_server_set_reply( req, socks + sock_count, sizeof(SOCKET) * MAX_SOCKS_PER_REQ ); + status = wine_server_call( req ); + if (!status) + { + sock_count += reply->sock_count; + socks[sock_count] = -1; + index = reply->enum_index; + + /* Enumeration ended? */ + if (!reply->sock_count) return socks; + + buf_sz += sizeof(SOCKET) * MAX_SOCKS_PER_REQ; + new_socks = HeapReAlloc(GetProcessHeap(), 0, socks, buf_sz); + if (!new_socks) break; + socks = new_socks; + } + else break; + } + SERVER_END_REQ; + } + while(1); + + HeapFree(GetProcessHeap(), 0, socks); + return NULL; +#undef MAX_SOCKS_PER_REQ +} + static void _get_sock_errors(SOCKET s, int *events) { SERVER_START_REQ( get_socket_event ) @@ -1487,13 +1531,27 @@ int WINAPI WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData) */ INT WINAPI WSACleanup(void) { - if (num_startup) { - num_startup--; - TRACE("pending cleanups: %d\n", num_startup); - return 0; + if (!num_startup) + { + SetLastError(WSANOTINITIALISED); + return SOCKET_ERROR; } - SetLastError(WSANOTINITIALISED); - return SOCKET_ERROR; + if (num_startup == 1) + { + SOCKET *fds = _get_all_sockets(); + int i; + + if (fds) + { + for (i = 0; fds[i] != -1; i++) + WS_closesocket(fds[i]); + HeapFree(GetProcessHeap(), 0, fds); + TRACE("closed %d sockets\n", i); + } + } + num_startup--; + TRACE("pending cleanups: %d\n", num_startup); + return 0; } @@ -2117,7 +2175,7 @@ static BOOL ws_protocol_info(SOCKET s, int unicode, WSAPROTOCOL_INFOW *buffer, i if (status) { unsigned int err = NtStatusToWSAError( status ); - SetLastError( err == WSAEBADF ? WSAENOTSOCK : err ); + SetLastError( err ); return FALSE; } diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 8676b07..8245ad4 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -1123,7 +1123,6 @@ static void test_WithWSAStartup(void) ok(res == 0, "WSAStartup() failed unexpectedly: %d\n", res); /* show that sockets are destroyed automatically after WSACleanup */ - todo_wine { SetLastError(0xdeadbeef); res = send(src, "TEST", 4, 0); error = WSAGetLastError(); @@ -1135,7 +1134,6 @@ static void test_WithWSAStartup(void) error = WSAGetLastError(); ok(res == SOCKET_ERROR, "closesocket should have failed\n"); ok(error == WSAENOTSOCK, "expected 10038, got %d\n", error); - } closesocket(src); closesocket(dst); diff --git a/server/protocol.def b/server/protocol.def index aa37c66..1852d73 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1232,6 +1232,14 @@ enum server_fd_type file_pos_t count; /* count of bytes to unlock */ @END +/* Get socket handles batch */ +@REQ(get_socks_batch) + unsigned int start_index; +@REPLY + unsigned int enum_index; + unsigned int sock_count; + VARARG(socks,ints); +@END /* Create a socket */ @REQ(create_socket) diff --git a/server/sock.c b/server/sock.c index 1767dea..160a946 100644 --- a/server/sock.c +++ b/server/sock.c @@ -1207,6 +1207,23 @@ static void sock_destroy_ifchange_q( struct sock *sock ) } } +DECL_HANDLER(get_socks_batch) +{ + SOCKET socks[128]; + obj_handle_t handle; + unsigned int enum_index = req->start_index, sock_index = 0, max_socks; + + max_socks = min(sizeof(socks) / sizeof(socks[0]), get_reply_max_size() / sizeof(socks[0])); + while ( (handle = enumerate_handles(current->process, &sock_ops, &enum_index)) ) + { + socks[sock_index++] = handle; + if (sock_index == max_socks) break; + } + reply->enum_index = enum_index; + reply->sock_count = sock_index; + set_reply_data( socks, sock_index * sizeof(socks[0]) ); +} + /* create a socket */ DECL_HANDLER(create_socket) {