1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package net
6
7import (
8	"internal/syscall/windows"
9	"os"
10	"runtime"
11	"syscall"
12	"time"
13	"unsafe"
14)
15
16// Default values of KeepAliveTime and KeepAliveInterval on Windows,
17// check out https://learn.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals#remarks for details.
18const (
19	defaultKeepAliveIdle     = 2 * time.Hour
20	defaultKeepAliveInterval = time.Second
21)
22
23func setKeepAliveIdle(fd *netFD, d time.Duration) error {
24	if !windows.SupportTCPKeepAliveIdle() {
25		return setKeepAliveIdleAndInterval(fd, d, -1)
26	}
27
28	if d == 0 {
29		d = defaultTCPKeepAliveIdle
30	} else if d < 0 {
31		return nil
32	}
33	// The kernel expects seconds so round to next highest second.
34	secs := int(roundDurationUp(d, time.Second))
35	err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, windows.TCP_KEEPIDLE, secs)
36	runtime.KeepAlive(fd)
37	return os.NewSyscallError("setsockopt", err)
38}
39
40func setKeepAliveInterval(fd *netFD, d time.Duration) error {
41	if !windows.SupportTCPKeepAliveInterval() {
42		return setKeepAliveIdleAndInterval(fd, -1, d)
43	}
44
45	if d == 0 {
46		d = defaultTCPKeepAliveInterval
47	} else if d < 0 {
48		return nil
49	}
50	// The kernel expects seconds so round to next highest second.
51	secs := int(roundDurationUp(d, time.Second))
52	err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, windows.TCP_KEEPINTVL, secs)
53	runtime.KeepAlive(fd)
54	return os.NewSyscallError("setsockopt", err)
55}
56
57func setKeepAliveCount(fd *netFD, n int) error {
58	if n == 0 {
59		n = defaultTCPKeepAliveCount
60	} else if n < 0 {
61		return nil
62	}
63
64	err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, windows.TCP_KEEPCNT, n)
65	runtime.KeepAlive(fd)
66	return os.NewSyscallError("setsockopt", err)
67}
68
69// setKeepAliveIdleAndInterval serves for kernels prior to Windows 10, version 1709.
70func setKeepAliveIdleAndInterval(fd *netFD, idle, interval time.Duration) error {
71	// WSAIoctl with SIO_KEEPALIVE_VALS control code requires all fields in
72	// `tcp_keepalive` struct to be provided.
73	// Otherwise, if any of the fields were not provided, just leaving them
74	// zero will knock off any existing values of keep-alive.
75	// Unfortunately, Windows doesn't support retrieving current keep-alive
76	// settings in any form programmatically, which disable us to first retrieve
77	// the current keep-alive settings, then set it without unwanted corruption.
78	switch {
79	case idle < 0 && interval >= 0:
80		// Given that we can't set KeepAliveInterval alone, and this code path
81		// is new, it doesn't exist before, so we just return an error.
82		return syscall.WSAENOPROTOOPT
83	case idle >= 0 && interval < 0:
84		// Although we can't set KeepAliveTime alone either, this existing code
85		// path had been backing up [SetKeepAlivePeriod] which used to be set both
86		// KeepAliveTime and KeepAliveInterval to 15 seconds.
87		// Now we will use the default of KeepAliveInterval on Windows if user doesn't
88		// provide one.
89		interval = defaultKeepAliveInterval
90	case idle < 0 && interval < 0:
91		// Nothing to do, just bail out.
92		return nil
93	case idle >= 0 && interval >= 0:
94		// Go ahead.
95	}
96
97	if idle == 0 {
98		idle = defaultTCPKeepAliveIdle
99	}
100	if interval == 0 {
101		interval = defaultTCPKeepAliveInterval
102	}
103
104	// The kernel expects milliseconds so round to next highest
105	// millisecond.
106	tcpKeepAliveIdle := uint32(roundDurationUp(idle, time.Millisecond))
107	tcpKeepAliveInterval := uint32(roundDurationUp(interval, time.Millisecond))
108	ka := syscall.TCPKeepalive{
109		OnOff:    1,
110		Time:     tcpKeepAliveIdle,
111		Interval: tcpKeepAliveInterval,
112	}
113	ret := uint32(0)
114	size := uint32(unsafe.Sizeof(ka))
115	err := fd.pfd.WSAIoctl(syscall.SIO_KEEPALIVE_VALS, (*byte)(unsafe.Pointer(&ka)), size, nil, 0, &ret, nil, 0)
116	runtime.KeepAlive(fd)
117	return os.NewSyscallError("wsaioctl", err)
118}
119