aboutsummaryrefslogtreecommitdiff
path: root/src/win/tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/win/tcp.c')
-rw-r--r--src/win/tcp.c126
1 files changed, 95 insertions, 31 deletions
diff --git a/src/win/tcp.c b/src/win/tcp.c
index c452c12e..5b7604a9 100644
--- a/src/win/tcp.c
+++ b/src/win/tcp.c
@@ -49,29 +49,99 @@ static int uv__tcp_nodelay(uv_tcp_t* handle, SOCKET socket, int enable) {
}
-static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsigned int delay) {
+/*
+ * Check if Windows version is 10.0.16299 (Windows 10, version 1709) or later.
+ */
+static int minimal_windows10_version1709(void) {
+ OSVERSIONINFOW os_info;
+ if (!pRtlGetVersion)
+ return 0;
+ pRtlGetVersion(&os_info);
+ if (os_info.dwMajorVersion < 10)
+ return 0;
+ if (os_info.dwMajorVersion > 10)
+ return 1;
+ if (os_info.dwMinorVersion > 0)
+ return 1;
+ return os_info.dwBuildNumber >= 16299;
+}
+
+
+static int uv__tcp_keepalive(uv_tcp_t* handle,
+ SOCKET socket,
+ int on,
+ unsigned int idle,
+ unsigned int intvl,
+ unsigned int cnt) {
if (setsockopt(socket,
SOL_SOCKET,
SO_KEEPALIVE,
- (const char*)&enable,
- sizeof enable) == -1) {
+ (const char*)&on,
+ sizeof on) == -1) {
return WSAGetLastError();
}
- if (!enable)
+ if (!on)
return 0;
- if (delay < 1)
+ if (idle < 1 || intvl < 1 || cnt < 1)
return UV_EINVAL;
- if (setsockopt(socket,
- IPPROTO_TCP,
- TCP_KEEPALIVE,
- (const char*)&delay,
- sizeof delay) == -1) {
- return WSAGetLastError();
+ /* Windows 10, version 1709 (build 10.0.16299) and later require second units
+ * for TCP keepalive options. */
+ if (minimal_windows10_version1709()) {
+ if (setsockopt(socket,
+ IPPROTO_TCP,
+ TCP_KEEPIDLE,
+ (const char*)&idle,
+ sizeof idle) == -1) {
+ return WSAGetLastError();
+ }
+
+ if (setsockopt(socket,
+ IPPROTO_TCP,
+ TCP_KEEPINTVL,
+ (const char*)&intvl,
+ sizeof intvl) == -1) {
+ return WSAGetLastError();
+ }
+
+ if (setsockopt(socket,
+ IPPROTO_TCP,
+ TCP_KEEPCNT,
+ (const char*)&cnt,
+ sizeof cnt) == -1) {
+ return WSAGetLastError();
+ }
+
+ return 0;
}
+ /* For those versions prior to Windows 10 version 1709,
+ * we fall back to SIO_KEEPALIVE_VALS that expects millisecond units.
+ * The SIO_KEEPALIVE_VALS IOCTL is supported on Windows 2000
+ * and later versions of the operating system. */
+ struct tcp_keepalive keepalive;
+ keepalive.onoff = on;
+ keepalive.keepalivetime = idle * 1000;
+ keepalive.keepaliveinterval = intvl * 1000;
+ /* On Windows Vista and later, the number of keep-alive probes
+ * (data retransmissions) is set to 10 and cannot be changed.
+ * On Windows Server 2003, Windows XP, and Windows 2000, the default setting
+ * for number of keep-alive probes is 5 and cannot be changed programmatically.
+ */
+ DWORD dummy;
+ if (WSAIoctl(socket,
+ SIO_KEEPALIVE_VALS,
+ (LPVOID) &keepalive,
+ sizeof keepalive,
+ NULL,
+ 0,
+ &dummy,
+ NULL,
+ NULL) == -1)
+ return WSAGetLastError();
+
return 0;
}
@@ -132,7 +202,7 @@ static int uv__tcp_set_socket(uv_loop_t* loop,
/* TODO: Use stored delay. */
if (handle->flags & UV_HANDLE_TCP_KEEPALIVE) {
- err = uv__tcp_keepalive(handle, socket, 1, 60);
+ err = uv__tcp_keepalive(handle, socket, 1, 60, 1, 10);
if (err)
return err;
}
@@ -749,20 +819,6 @@ static int uv__is_loopback(const struct sockaddr_storage* storage) {
return 0;
}
-// Check if Windows version is 10.0.16299 or later
-static int uv__is_fast_loopback_fail_supported(void) {
- OSVERSIONINFOW os_info;
- if (!pRtlGetVersion)
- return 0;
- pRtlGetVersion(&os_info);
- if (os_info.dwMajorVersion < 10)
- return 0;
- if (os_info.dwMajorVersion > 10)
- return 1;
- if (os_info.dwMinorVersion > 0)
- return 1;
- return os_info.dwBuildNumber >= 16299;
-}
static int uv__tcp_try_connect(uv_connect_t* req,
uv_tcp_t* handle,
@@ -809,7 +865,7 @@ static int uv__tcp_try_connect(uv_connect_t* req,
* is not reachable, instead of waiting for 2s. We do not care if this fails.
* This only works on Windows version 10.0.16299 and later.
*/
- if (uv__is_fast_loopback_fail_supported() && uv__is_loopback(&converted)) {
+ if (minimal_windows10_version1709() && uv__is_loopback(&converted)) {
memset(&retransmit_ioctl, 0, sizeof(retransmit_ioctl));
retransmit_ioctl.Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
retransmit_ioctl.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
@@ -1335,22 +1391,30 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int enable) {
}
-int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) {
+int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int idle) {
+ return uv_tcp_keepalive_ex(handle, on, idle, 1, 10);
+}
+
+int uv_tcp_keepalive_ex(uv_tcp_t* handle,
+ int on,
+ unsigned int idle,
+ unsigned int intvl,
+ unsigned int cnt) {
int err;
if (handle->socket != INVALID_SOCKET) {
- err = uv__tcp_keepalive(handle, handle->socket, enable, delay);
+ err = uv__tcp_keepalive(handle, handle->socket, on, idle, intvl, cnt);
if (err)
return uv_translate_sys_error(err);
}
- if (enable) {
+ if (on) {
handle->flags |= UV_HANDLE_TCP_KEEPALIVE;
} else {
handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE;
}
- /* TODO: Store delay if handle->socket isn't created yet. */
+ /* TODO: Store idle if handle->socket isn't created yet. */
return 0;
}