| // 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) |
| } |