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  // canUseConnectEx reports whether we can use the ConnectEx Windows API call
    27  // for the given network type.
    28  func canUseConnectEx(net string) bool {
    29  	switch net {
    30  	case "tcp", "tcp4", "tcp6":
    31  		return true
    32  	}
    33  	// ConnectEx windows API does not support connectionless sockets.
    34  	return false
    35  }
    36  
    37  func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) {
    38  	ret := &netFD{
    39  		pfd: poll.FD{
    40  			Sysfd:         sysfd,
    41  			IsStream:      sotype == syscall.SOCK_STREAM,
    42  			ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW,
    43  		},
    44  		family: family,
    45  		sotype: sotype,
    46  		net:    net,
    47  	}
    48  	return ret, nil
    49  }
    50  
    51  func (fd *netFD) init() error {
    52  	errcall, err := fd.pfd.Init(fd.net, true)
    53  	if errcall != "" {
    54  		err = wrapSyscallError(errcall, err)
    55  	}
    56  	return err
    57  }
    58  
    59  // Always returns nil for connected peer address result.
    60  func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (syscall.Sockaddr, error) {
    61  	// Do not need to call fd.writeLock here,
    62  	// because fd is not yet accessible to user,
    63  	// so no concurrent operations are possible.
    64  	if err := fd.init(); err != nil {
    65  		return nil, err
    66  	}
    67  	if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
    68  		fd.pfd.SetWriteDeadline(deadline)
    69  		defer fd.pfd.SetWriteDeadline(noDeadline)
    70  	}
    71  	if !canUseConnectEx(fd.net) {
    72  		err := connectFunc(fd.pfd.Sysfd, ra)
    73  		return nil, os.NewSyscallError("connect", err)
    74  	}
    75  	// ConnectEx windows API requires an unconnected, previously bound socket.
    76  	if la == nil {
    77  		switch ra.(type) {
    78  		case *syscall.SockaddrInet4:
    79  			la = &syscall.SockaddrInet4{}
    80  		case *syscall.SockaddrInet6:
    81  			la = &syscall.SockaddrInet6{}
    82  		default:
    83  			panic("unexpected type in connect")
    84  		}
    85  		if err := syscall.Bind(fd.pfd.Sysfd, la); err != nil {
    86  			return nil, os.NewSyscallError("bind", err)
    87  		}
    88  	}
    89  
    90  	var isloopback bool
    91  	switch ra := ra.(type) {
    92  	case *syscall.SockaddrInet4:
    93  		isloopback = ra.Addr[0] == 127
    94  	case *syscall.SockaddrInet6:
    95  		isloopback = ra.Addr == [16]byte(IPv6loopback)
    96  	default:
    97  		panic("unexpected type in connect")
    98  	}
    99  	if isloopback {
   100  		// This makes ConnectEx() fails faster if the target port on the localhost
   101  		// is not reachable, instead of waiting for 2s.
   102  		params := windows.TCP_INITIAL_RTO_PARAMETERS{
   103  			Rtt:                   windows.TCP_INITIAL_RTO_UNSPECIFIED_RTT, // use the default or overridden by the Administrator
   104  			MaxSynRetransmissions: 1,                                       // minimum possible value before Windows 10.0.16299
   105  		}
   106  		if windows.Support_TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS() {
   107  			// In Windows 10.0.16299 TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS makes ConnectEx() fails instantly.
   108  			params.MaxSynRetransmissions = windows.TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS
   109  		}
   110  		var out uint32
   111  		// Don't abort the connection if WSAIoctl fails, as it is only an optimization.
   112  		// If it fails reliably, we expect TestDialClosedPortFailFast to detect it.
   113  		_ = fd.pfd.WSAIoctl(windows.SIO_TCP_INITIAL_RTO, (*byte)(unsafe.Pointer(&params)), uint32(unsafe.Sizeof(params)), nil, 0, &out, nil, 0)
   114  	}
   115  
   116  	// Wait for the goroutine converting context.Done into a write timeout
   117  	// to exist, otherwise our caller might cancel the context and
   118  	// cause fd.setWriteDeadline(aLongTimeAgo) to cancel a successful dial.
   119  	done := make(chan bool) // must be unbuffered
   120  	defer func() { done <- true }()
   121  	go func() {
   122  		select {
   123  		case <-ctx.Done():
   124  			// Force the runtime's poller to immediately give
   125  			// up waiting for writability.
   126  			fd.pfd.SetWriteDeadline(aLongTimeAgo)
   127  			<-done
   128  		case <-done:
   129  		}
   130  	}()
   131  
   132  	// Call ConnectEx API.
   133  	if err := fd.pfd.ConnectEx(ra); err != nil {
   134  		select {
   135  		case <-ctx.Done():
   136  			return nil, mapErr(ctx.Err())
   137  		default:
   138  			if _, ok := err.(syscall.Errno); ok {
   139  				err = os.NewSyscallError("connectex", err)
   140  			}
   141  			return nil, err
   142  		}
   143  	}
   144  	// Refresh socket properties.
   145  	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))))
   146  }
   147  
   148  func (c *conn) writeBuffers(v *Buffers) (int64, error) {
   149  	if !c.ok() {
   150  		return 0, syscall.EINVAL
   151  	}
   152  	n, err := c.fd.writeBuffers(v)
   153  	if err != nil {
   154  		return n, &OpError{Op: "wsasend", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
   155  	}
   156  	return n, nil
   157  }
   158  
   159  func (fd *netFD) writeBuffers(buf *Buffers) (int64, error) {
   160  	n, err := fd.pfd.Writev((*[][]byte)(buf))
   161  	runtime.KeepAlive(fd)
   162  	return n, wrapSyscallError("wsasend", err)
   163  }
   164  
   165  func (fd *netFD) accept() (*netFD, error) {
   166  	s, rawsa, rsan, errcall, err := fd.pfd.Accept(func() (syscall.Handle, error) {
   167  		return sysSocket(fd.family, fd.sotype, 0)
   168  	})
   169  
   170  	if err != nil {
   171  		if errcall != "" {
   172  			err = wrapSyscallError(errcall, err)
   173  		}
   174  		return nil, err
   175  	}
   176  
   177  	// Associate our new socket with IOCP.
   178  	netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
   179  	if err != nil {
   180  		poll.CloseFunc(s)
   181  		return nil, err
   182  	}
   183  	if err := netfd.init(); err != nil {
   184  		fd.Close()
   185  		return nil, err
   186  	}
   187  
   188  	// Get local and peer addr out of AcceptEx buffer.
   189  	var lrsa, rrsa *syscall.RawSockaddrAny
   190  	var llen, rlen int32
   191  	syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])),
   192  		0, rsan, rsan, &lrsa, &llen, &rrsa, &rlen)
   193  	lsa, _ := lrsa.Sockaddr()
   194  	rsa, _ := rrsa.Sockaddr()
   195  
   196  	netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
   197  	return netfd, nil
   198  }
   199  
   200  // Unimplemented functions.
   201  
   202  func (fd *netFD) dup() (*os.File, error) {
   203  	// TODO: Implement this
   204  	return nil, syscall.EWINDOWS
   205  }
   206  

View as plain text