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 dc34498d18 #19527
Closes #19559
This commit is contained in:
Viktor Szakats 2025-11-17 01:16:07 +01:00
parent f2460e2cb5
commit f0de14168a
No known key found for this signature in database
GPG key ID: B5ABD165E2AEF201

View file

@ -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);
}
#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);
#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
#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
#else /* !Windows */
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 /* !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 */
}
}