fd_windows.mx raw

   1  // Copyright 2010 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  	"context"
   9  	"internal/poll"
  10  	"internal/syscall/windows"
  11  	"os"
  12  	"runtime"
  13  	"syscall"
  14  	"unsafe"
  15  )
  16  
  17  const (
  18  	readSyscallName     = "wsarecv"
  19  	readFromSyscallName = "wsarecvfrom"
  20  	readMsgSyscallName  = "wsarecvmsg"
  21  	writeSyscallName    = "wsasend"
  22  	writeToSyscallName  = "wsasendto"
  23  	writeMsgSyscallName = "wsasendmsg"
  24  )
  25  
  26  func init() {
  27  	poll.InitWSA()
  28  }
  29  
  30  // canUseConnectEx reports whether we can use the ConnectEx Windows API call
  31  // for the given network type.
  32  func canUseConnectEx(net string) bool {
  33  	switch net {
  34  	case "tcp", "tcp4", "tcp6":
  35  		return true
  36  	}
  37  	// ConnectEx windows API does not support connectionless sockets.
  38  	return false
  39  }
  40  
  41  func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) {
  42  	ret := &netFD{
  43  		pfd: poll.FD{
  44  			Sysfd:         sysfd,
  45  			IsStream:      sotype == syscall.SOCK_STREAM,
  46  			ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW,
  47  		},
  48  		family: family,
  49  		sotype: sotype,
  50  		net:    net,
  51  	}
  52  	return ret, nil
  53  }
  54  
  55  func (fd *netFD) init() error {
  56  	if err := fd.pfd.Init(fd.net, true); err != nil {
  57  		return err
  58  	}
  59  	switch fd.net {
  60  	case "udp", "udp4", "udp6":
  61  		// Disable reporting of PORT_UNREACHABLE errors.
  62  		// See https://go.dev/issue/5834.
  63  		ret := uint32(0)
  64  		flag := uint32(0)
  65  		size := uint32(unsafe.Sizeof(flag))
  66  		err := syscall.WSAIoctl(fd.pfd.Sysfd, syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0)
  67  		if err != nil {
  68  			return wrapSyscallError("wsaioctl", err)
  69  		}
  70  		// Disable reporting of NET_UNREACHABLE errors.
  71  		// See https://go.dev/issue/68614.
  72  		ret = 0
  73  		flag = 0
  74  		size = uint32(unsafe.Sizeof(flag))
  75  		err = syscall.WSAIoctl(fd.pfd.Sysfd, windows.SIO_UDP_NETRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0)
  76  		if err != nil {
  77  			return wrapSyscallError("wsaioctl", err)
  78  		}
  79  	}
  80  	return nil
  81  }
  82  
  83  // Always returns nil for connected peer address result.
  84  func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (syscall.Sockaddr, error) {
  85  	// Do not need to call fd.writeLock here,
  86  	// because fd is not yet accessible to user,
  87  	// so no concurrent operations are possible.
  88  	if err := fd.init(); err != nil {
  89  		return nil, err
  90  	}
  91  
  92  	if ctx.Done() != nil {
  93  		// Propagate the Context's deadline and cancellation.
  94  		// If the context is already done, or if it has a nonzero deadline,
  95  		// ensure that that is applied before the call to ConnectEx begins
  96  		// so that we don't return spurious connections.
  97  		defer fd.pfd.SetWriteDeadline(noDeadline)
  98  
  99  		if ctx.Err() != nil {
 100  			fd.pfd.SetWriteDeadline(aLongTimeAgo)
 101  		} else {
 102  			if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
 103  				fd.pfd.SetWriteDeadline(deadline)
 104  			}
 105  
 106  			done := chan struct{}{}
 107  			stop := context.AfterFunc(ctx, func() {
 108  				// Force the runtime's poller to immediately give
 109  				// up waiting for writability.
 110  				fd.pfd.SetWriteDeadline(aLongTimeAgo)
 111  				close(done)
 112  			})
 113  			defer func() {
 114  				if !stop() {
 115  					// Wait for the call to SetWriteDeadline to complete so that we can
 116  					// reset the deadline if everything else succeeded.
 117  					<-done
 118  				}
 119  			}()
 120  		}
 121  	}
 122  
 123  	if !canUseConnectEx(fd.net) {
 124  		err := connectFunc(fd.pfd.Sysfd, ra)
 125  		return nil, os.NewSyscallError("connect", err)
 126  	}
 127  	// ConnectEx windows API requires an unconnected, previously bound socket.
 128  	if la == nil {
 129  		switch ra.(type) {
 130  		case *syscall.SockaddrInet4:
 131  			la = &syscall.SockaddrInet4{}
 132  		case *syscall.SockaddrInet6:
 133  			la = &syscall.SockaddrInet6{}
 134  		default:
 135  			panic("unexpected type in connect")
 136  		}
 137  		if err := syscall.Bind(fd.pfd.Sysfd, la); err != nil {
 138  			return nil, os.NewSyscallError("bind", err)
 139  		}
 140  	}
 141  
 142  	var isloopback bool
 143  	switch ra := ra.(type) {
 144  	case *syscall.SockaddrInet4:
 145  		isloopback = ra.Addr[0] == 127
 146  	case *syscall.SockaddrInet6:
 147  		isloopback = ra.Addr == [16]byte(IPv6loopback)
 148  	default:
 149  		panic("unexpected type in connect")
 150  	}
 151  	if isloopback {
 152  		// This makes ConnectEx() fails faster if the target port on the localhost
 153  		// is not reachable, instead of waiting for 2s.
 154  		params := windows.TCP_INITIAL_RTO_PARAMETERS{
 155  			Rtt:                   windows.TCP_INITIAL_RTO_UNSPECIFIED_RTT, // use the default or overridden by the Administrator
 156  			MaxSynRetransmissions: 1,                                       // minimum possible value before Windows 10.0.16299
 157  		}
 158  		if windows.SupportTCPInitialRTONoSYNRetransmissions() {
 159  			// In Windows 10.0.16299 TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS makes ConnectEx() fails instantly.
 160  			params.MaxSynRetransmissions = windows.TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS
 161  		}
 162  		var out uint32
 163  		// Don't abort the connection if WSAIoctl fails, as it is only an optimization.
 164  		// If it fails reliably, we expect TestDialClosedPortFailFast to detect it.
 165  		_ = fd.pfd.WSAIoctl(windows.SIO_TCP_INITIAL_RTO, (*byte)(unsafe.Pointer(&params)), uint32(unsafe.Sizeof(params)), nil, 0, &out, nil, 0)
 166  	}
 167  
 168  	// Call ConnectEx API.
 169  	if err := fd.pfd.ConnectEx(ra); err != nil {
 170  		select {
 171  		case <-ctx.Done():
 172  			return nil, mapErr(ctx.Err())
 173  		default:
 174  			if _, ok := err.(syscall.Errno); ok {
 175  				err = os.NewSyscallError("connectex", err)
 176  			}
 177  			return nil, err
 178  		}
 179  	}
 180  	// Refresh socket properties.
 181  	return nil, os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.pfd.Sysfd)), int32(unsafe.Sizeof(fd.pfd.Sysfd))))
 182  }
 183  
 184  func (c *conn) writeBuffers(v *Buffers) (int64, error) {
 185  	if !c.ok() {
 186  		return 0, syscall.EINVAL
 187  	}
 188  	n, err := c.fd.writeBuffers(v)
 189  	if err != nil {
 190  		return n, &OpError{Op: "wsasend", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
 191  	}
 192  	return n, nil
 193  }
 194  
 195  func (fd *netFD) writeBuffers(buf *Buffers) (int64, error) {
 196  	n, err := fd.pfd.Writev((*[][]byte)(buf))
 197  	runtime.KeepAlive(fd)
 198  	return n, wrapSyscallError("wsasend", err)
 199  }
 200  
 201  func (fd *netFD) accept() (*netFD, error) {
 202  	s, rawsa, rsan, errcall, err := fd.pfd.Accept(func() (syscall.Handle, error) {
 203  		return sysSocket(fd.family, fd.sotype, 0)
 204  	})
 205  
 206  	if err != nil {
 207  		if errcall != "" {
 208  			err = wrapSyscallError(errcall, err)
 209  		}
 210  		return nil, err
 211  	}
 212  
 213  	// Associate our new socket with IOCP.
 214  	netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
 215  	if err != nil {
 216  		poll.CloseFunc(s)
 217  		return nil, err
 218  	}
 219  	if err := netfd.init(); err != nil {
 220  		fd.Close()
 221  		return nil, err
 222  	}
 223  
 224  	// Get local and peer addr out of AcceptEx buffer.
 225  	var lrsa, rrsa *syscall.RawSockaddrAny
 226  	var llen, rlen int32
 227  	syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])),
 228  		0, rsan, rsan, &lrsa, &llen, &rrsa, &rlen)
 229  	lsa, _ := lrsa.Sockaddr()
 230  	rsa, _ := rrsa.Sockaddr()
 231  
 232  	netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
 233  	return netfd, nil
 234  }
 235  
 236  // Defined in os package.
 237  func newWindowsFile(h syscall.Handle, name string) *os.File
 238  
 239  func (fd *netFD) dup() (*os.File, error) {
 240  	// Disassociate the IOCP from the socket,
 241  	// it is not safe to share a duplicated handle
 242  	// that is associated with IOCP.
 243  	if err := fd.pfd.DisassociateIOCP(); err != nil {
 244  		return nil, err
 245  	}
 246  	var h syscall.Handle
 247  	var syserr error
 248  	err := fd.pfd.RawControl(func(fd uintptr) {
 249  		h, syserr = dupSocket(syscall.Handle(fd))
 250  	})
 251  	if err != nil {
 252  		err = syserr
 253  	}
 254  	if err != nil {
 255  		return nil, err
 256  	}
 257  	// All WSASocket calls must be match with a syscall.Closesocket call,
 258  	// but os.NewFile calls syscall.CloseHandle instead. We need to use
 259  	// a hidden function so that the returned file is aware of this fact.
 260  	return newWindowsFile(h, fd.name()), nil
 261  }
 262