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 5 package net
6 7 import (
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.
18 const (
19 defaultKeepAliveIdle = 2 * time.Hour
20 defaultKeepAliveInterval = time.Second
21 )
22 23 func 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 40 func 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 57 func 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.
70 func 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