diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 7ed48124a5a..15c9d0cc940 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -2289,6 +2289,8 @@ INT WINAPI WS_getsockopt(SOCKET s, INT level, return server_getsockopt( s, IOCTL_AFD_WINE_GET_SO_RCVBUF, optval, optlen ); case WS_SO_RCVTIMEO: + return server_getsockopt( s, IOCTL_AFD_WINE_GET_SO_RCVTIMEO, optval, optlen ); + case WS_SO_SNDTIMEO: { INT64 timeout; @@ -3565,6 +3567,9 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname, case WS_SO_RCVBUF: return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_RCVBUF, optval, optlen ); + case WS_SO_RCVTIMEO: + return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_RCVTIMEO, optval, optlen ); + /* Some options need some conversion before they can be sent to * setsockopt. The conversions are done here, then they will fall through * to the general case. Special options that are not passed to @@ -3631,13 +3636,8 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname, TRACE("setting global SO_OPENTYPE = 0x%x\n", *((const int*)optval) ); return 0; -#ifdef SO_RCVTIMEO - case WS_SO_RCVTIMEO: -#endif #ifdef SO_SNDTIMEO case WS_SO_SNDTIMEO: -#endif -#if defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO) if (optval && optlen == sizeof(UINT32)) { /* WinSock passes milliseconds instead of struct timeval */ tval.tv_usec = (*(const UINT32*)optval % 1000) * 1000; diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index ccaa04e4ef5..a3f7e002ce7 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -10919,7 +10919,7 @@ static void test_timeout(void) WSASetLastError(0xdeadbeef); ret = getsockopt(client, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, &len); ok(!ret, "expected success\n"); - todo_wine ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError()); + ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError()); ok(len == sizeof(timeout), "got size %u\n", len); ok(!timeout, "got timeout %u\n", timeout); @@ -10927,15 +10927,15 @@ static void test_timeout(void) WSASetLastError(0xdeadbeef); ret = setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); ok(!ret, "expected success\n"); - todo_wine ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError()); + ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError()); timeout = 0xdeadbeef; len = sizeof(timeout); WSASetLastError(0xdeadbeef); ret = getsockopt(client, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, &len); ok(!ret, "expected success\n"); - todo_wine ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError()); - todo_wine ok(timeout == 100, "got timeout %u\n", timeout); + ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError()); + ok(timeout == 100, "got timeout %u\n", timeout); WSASetLastError(0xdeadbeef); ret = recv(client, &buffer, 1, 0); diff --git a/include/wine/afd.h b/include/wine/afd.h index 0692394459e..c4f7c7a6413 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -171,6 +171,8 @@ struct afd_get_events_params #define IOCTL_AFD_WINE_SET_SO_OOBINLINE CTL_CODE(FILE_DEVICE_NETWORK, 228, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_AFD_WINE_SET_SO_RCVBUF CTL_CODE(FILE_DEVICE_NETWORK, 229, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_AFD_WINE_GET_SO_RCVBUF CTL_CODE(FILE_DEVICE_NETWORK, 230, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_AFD_WINE_SET_SO_RCVTIMEO CTL_CODE(FILE_DEVICE_NETWORK, 231, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_AFD_WINE_GET_SO_RCVTIMEO CTL_CODE(FILE_DEVICE_NETWORK, 232, METHOD_BUFFERED, FILE_ANY_ACCESS) struct afd_create_params { diff --git a/server/sock.c b/server/sock.c index 8bad31efdd1..94dbafe44ee 100644 --- a/server/sock.c +++ b/server/sock.c @@ -208,6 +208,7 @@ struct sock union win_sockaddr addr; /* socket name */ int addr_len; /* socket name length */ unsigned int rcvbuf; /* advisory recv buffer size */ + unsigned int rcvtimeo; /* receive timeout in ms */ unsigned int rd_shutdown : 1; /* is the read end shut down? */ unsigned int wr_shutdown : 1; /* is the write end shut down? */ unsigned int wr_shutdown_pending : 1; /* is a write shutdown pending? */ @@ -1388,6 +1389,7 @@ static struct sock *create_socket(void) sock->nonblocking = 0; sock->bound = 0; sock->rcvbuf = 0; + sock->rcvtimeo = 0; init_async_queue( &sock->read_q ); init_async_queue( &sock->write_q ); init_async_queue( &sock->ifchange_q ); @@ -2619,6 +2621,35 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) return 0; } + case IOCTL_AFD_WINE_GET_SO_RCVTIMEO: + { + DWORD rcvtimeo = sock->rcvtimeo; + + if (get_reply_max_size() < sizeof(rcvtimeo)) + { + set_error( STATUS_BUFFER_TOO_SMALL ); + return 0; + } + + set_reply_data( &rcvtimeo, sizeof(rcvtimeo) ); + return 1; + } + + case IOCTL_AFD_WINE_SET_SO_RCVTIMEO: + { + DWORD rcvtimeo; + + if (get_req_data_size() < sizeof(rcvtimeo)) + { + set_error( STATUS_BUFFER_TOO_SMALL ); + return 0; + } + rcvtimeo = *(DWORD *)get_req_data(); + + sock->rcvtimeo = rcvtimeo; + return 0; + } + default: set_error( STATUS_NOT_SUPPORTED ); return 0; @@ -3029,19 +3060,14 @@ DECL_HANDLER(recv_socket) /* recv() returned EWOULDBLOCK, i.e. no data available yet */ if (status == STATUS_DEVICE_NOT_READY && !sock->nonblocking) { -#ifdef SO_RCVTIMEO - struct timeval tv; - socklen_t len = sizeof(tv); - /* Set a timeout on the async if necessary. * * We want to do this *only* if the client gave us STATUS_DEVICE_NOT_READY. * If the client gave us STATUS_PENDING, it expects the async to always * block (it was triggered by WSARecv*() with a valid OVERLAPPED * structure) and for the timeout not to be respected. */ - if (is_fd_overlapped( fd ) && !getsockopt( get_unix_fd( fd ), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, &len )) - timeout = tv.tv_sec * -10000000 + tv.tv_usec * -10; -#endif + if (is_fd_overlapped( fd )) + timeout = (timeout_t)sock->rcvtimeo * -10000; status = STATUS_PENDING; }