From f0de14168a4d1c3a4ed43a04af92c5755c84b9fc Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Mon, 17 Nov 2025 01:16:07 +0100 Subject: [PATCH] cf-socket: limit use of `TCP_KEEP*` to Windows 10.0.16299+ at runtime Before this patch `TCP_KEEP*` socket options were unconditionally used if the build-time SDK supported them. This caused curl logging errors (or trace messages since #19527) on Windows versions missing support for them. After this patch, use them only when the runtime environment supports it and fall back to the alternate method (`SIO_KEEPALIVE_VALS`) dynamically. Also: - log a trace message when using the Win10 method. - document which SDK versions offer `TCP_KEEP*` macros. Ref: https://learn.microsoft.com/windows/win32/winsock/ipproto-tcp-socket-options Ref: https://learn.microsoft.com/windows/win32/winsock/sio-keepalive-vals Reported-by: Aleksandr Sergeev Fixes #19520 Follow-up to dc34498d18d3303d67364423b4aa0daab4afb3ba #19527 Closes #19559 --- lib/cf-socket.c | 118 ++++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 60 deletions(-) diff --git a/lib/cf-socket.c b/lib/cf-socket.c index d1a99d3ece..8661647ba5 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -149,13 +149,7 @@ static void nosigpipe(struct Curl_cfilter *cf, #define nosigpipe(x,y,z) Curl_nop_stmt #endif -#if defined(USE_WINSOCK) && \ - defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT) -/* Win 10, v 1709 (10.0.16299) and later can use SetSockOpt TCP_KEEP____ - * so should use seconds */ -#define CURL_WINSOCK_KEEP_SSO -#define KEEPALIVE_FACTOR(x) -#elif defined(USE_WINSOCK) || \ +#if defined(USE_WINSOCK) || \ (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ (defined(__DragonFly__) && __DragonFly_version < 500702) || \ (defined(_WIN32) && !defined(TCP_KEEPIDLE)) @@ -166,17 +160,6 @@ static void nosigpipe(struct Curl_cfilter *cf, #define KEEPALIVE_FACTOR(x) #endif -/* Offered by mingw-w64 and MS SDK. Latter only when targeting Win7+. */ -#if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS) -#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) - -struct tcp_keepalive { - u_long onoff; - u_long keepalivetime; - u_long keepaliveinterval; -}; -#endif - static void tcpkeepalive(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -192,49 +175,64 @@ tcpkeepalive(struct Curl_cfilter *cf, sockfd, SOCKERRNO); } else { -#ifdef SIO_KEEPALIVE_VALS /* Windows */ -/* Windows 10, version 1709 (10.0.16299) and later versions */ -#ifdef CURL_WINSOCK_KEEP_SSO - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, - (const char *)&optval, sizeof(optval)) < 0) { - CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPIDLE on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); - } - optval = curlx_sltosi(data->set.tcp_keepintvl); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, - (const char *)&optval, sizeof(optval)) < 0) { - CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPINTVL on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); - } - optval = curlx_sltosi(data->set.tcp_keepcnt); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, - (const char *)&optval, sizeof(optval)) < 0) { - CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPCNT on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); +#ifdef USE_WINSOCK +/* Offered by mingw-w64 v12+. MS SDK ~10+/~VS2017+. */ +#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT) + /* Windows 10, version 1709 (10.0.16299) and later versions can use + setsockopt() TCP_KEEP*. Older versions return with failure. */ + if(curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + CURL_TRC_CF(data, cf, "Set TCP_KEEP* on fd=%" FMT_SOCKET_T, sockfd); + optval = curlx_sltosi(data->set.tcp_keepidle); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, + (const char *)&optval, sizeof(optval)) < 0) { + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPIDLE on fd " + "%" FMT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } + optval = curlx_sltosi(data->set.tcp_keepintvl); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, + (const char *)&optval, sizeof(optval)) < 0) { + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPINTVL on fd " + "%" FMT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } + optval = curlx_sltosi(data->set.tcp_keepcnt); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, + (const char *)&optval, sizeof(optval)) < 0) { + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPCNT on fd " + "%" FMT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } } -#else /* Windows < 10.0.16299 */ - struct tcp_keepalive vals; - DWORD dummy; - vals.onoff = 1; - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - vals.keepalivetime = (u_long)optval; - optval = curlx_sltosi(data->set.tcp_keepintvl); - KEEPALIVE_FACTOR(optval); - vals.keepaliveinterval = (u_long)optval; - if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), - NULL, 0, &dummy, NULL, NULL) != 0) { - CURL_TRC_CF(data, cf, "Failed to set SIO_KEEPALIVE_VALS on fd " - "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); + else +#endif /* TCP_KEEPIDLE && TCP_KEEPINTVL && TCP_KEEPCNT */ + { +/* Offered by mingw-w64 and MS SDK. Latter only when targeting Win7+. */ +#ifndef SIO_KEEPALIVE_VALS +#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) + struct tcp_keepalive { + u_long onoff; + u_long keepalivetime; + u_long keepaliveinterval; + }; +#endif + struct tcp_keepalive vals; + DWORD dummy; + vals.onoff = 1; + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + vals.keepalivetime = (u_long)optval; + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + vals.keepaliveinterval = (u_long)optval; + if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID)&vals, sizeof(vals), + NULL, 0, &dummy, NULL, NULL) != 0) { + CURL_TRC_CF(data, cf, "Failed to set SIO_KEEPALIVE_VALS on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); + } } -#endif -#else /* !Windows */ +#else /* !USE_WINSOCK */ #ifdef TCP_KEEPIDLE optval = curlx_sltosi(data->set.tcp_keepidle); KEEPALIVE_FACTOR(optval); @@ -303,7 +301,7 @@ tcpkeepalive(struct Curl_cfilter *cf, "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } #endif -#endif +#endif /* USE_WINSOCK */ } } -- 2.47.3