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