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