Source file src/net/tcpsockopt_windows.go

     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  

View as plain text