tcpsockopt_windows.mx raw

   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