Source file src/net/fd_windows.go

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

View as plain text