blob: f635d47999a47d8a1830a0664ef8d936fce92f3b [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net
import (
"internal/syscall/windows"
"os"
"runtime"
"syscall"
"time"
"unsafe"
)
// Default values of KeepAliveTime and KeepAliveInterval on Windows,
// check out https://learn.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals#remarks for details.
const (
defaultKeepAliveIdle = 2 * time.Hour
defaultKeepAliveInterval = time.Second
)
func setKeepAliveIdle(fd *netFD, d time.Duration) error {
if !windows.SupportTCPKeepAliveIdle() {
return setKeepAliveIdleAndInterval(fd, d, -1)
}
if d == 0 {
d = defaultTCPKeepAliveIdle
} else if d < 0 {
return nil
}
// The kernel expects seconds so round to next highest second.
secs := int(roundDurationUp(d, time.Second))
err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, windows.TCP_KEEPIDLE, secs)
runtime.KeepAlive(fd)
return os.NewSyscallError("setsockopt", err)
}
func setKeepAliveInterval(fd *netFD, d time.Duration) error {
if !windows.SupportTCPKeepAliveInterval() {
return setKeepAliveIdleAndInterval(fd, -1, d)
}
if d == 0 {
d = defaultTCPKeepAliveInterval
} else if d < 0 {
return nil
}
// The kernel expects seconds so round to next highest second.
secs := int(roundDurationUp(d, time.Second))
err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, windows.TCP_KEEPINTVL, secs)
runtime.KeepAlive(fd)
return os.NewSyscallError("setsockopt", err)
}
func setKeepAliveCount(fd *netFD, n int) error {
if n == 0 {
n = defaultTCPKeepAliveCount
} else if n < 0 {
return nil
}
err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, windows.TCP_KEEPCNT, n)
runtime.KeepAlive(fd)
return os.NewSyscallError("setsockopt", err)
}
// setKeepAliveIdleAndInterval serves for kernels prior to Windows 10, version 1709.
func setKeepAliveIdleAndInterval(fd *netFD, idle, interval time.Duration) error {
// WSAIoctl with SIO_KEEPALIVE_VALS control code requires all fields in
// `tcp_keepalive` struct to be provided.
// Otherwise, if any of the fields were not provided, just leaving them
// zero will knock off any existing values of keep-alive.
// Unfortunately, Windows doesn't support retrieving current keep-alive
// settings in any form programmatically, which disable us to first retrieve
// the current keep-alive settings, then set it without unwanted corruption.
switch {
case idle < 0 && interval >= 0:
// Given that we can't set KeepAliveInterval alone, and this code path
// is new, it doesn't exist before, so we just return an error.
return syscall.WSAENOPROTOOPT
case idle >= 0 && interval < 0:
// Although we can't set KeepAliveTime alone either, this existing code
// path had been backing up [SetKeepAlivePeriod] which used to be set both
// KeepAliveTime and KeepAliveInterval to 15 seconds.
// Now we will use the default of KeepAliveInterval on Windows if user doesn't
// provide one.
interval = defaultKeepAliveInterval
case idle < 0 && interval < 0:
// Nothing to do, just bail out.
return nil
case idle >= 0 && interval >= 0:
// Go ahead.
}
if idle == 0 {
idle = defaultTCPKeepAliveIdle
}
if interval == 0 {
interval = defaultTCPKeepAliveInterval
}
// The kernel expects milliseconds so round to next highest
// millisecond.
tcpKeepAliveIdle := uint32(roundDurationUp(idle, time.Millisecond))
tcpKeepAliveInterval := uint32(roundDurationUp(interval, time.Millisecond))
ka := syscall.TCPKeepalive{
OnOff: 1,
Time: tcpKeepAliveIdle,
Interval: tcpKeepAliveInterval,
}
ret := uint32(0)
size := uint32(unsafe.Sizeof(ka))
err := fd.pfd.WSAIoctl(syscall.SIO_KEEPALIVE_VALS, (*byte)(unsafe.Pointer(&ka)), size, nil, 0, &ret, nil, 0)
runtime.KeepAlive(fd)
return os.NewSyscallError("wsaioctl", err)
}