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 5package net 6 7import ( 8 "context" 9 "internal/poll" 10 "internal/syscall/windows" 11 "os" 12 "runtime" 13 "syscall" 14 "unsafe" 15) 16 17const ( 18 readSyscallName = "wsarecv" 19 readFromSyscallName = "wsarecvfrom" 20 readMsgSyscallName = "wsarecvmsg" 21 writeSyscallName = "wsasend" 22 writeToSyscallName = "wsasendto" 23 writeMsgSyscallName = "wsasendmsg" 24) 25 26func init() { 27 poll.InitWSA() 28} 29 30// canUseConnectEx reports whether we can use the ConnectEx Windows API call 31// for the given network type. 32func 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 41func 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 55func (fd *netFD) init() error { 56 errcall, err := fd.pfd.Init(fd.net, true) 57 if errcall != "" { 58 err = wrapSyscallError(errcall, err) 59 } 60 return err 61} 62 63// Always returns nil for connected peer address result. 64func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (syscall.Sockaddr, error) { 65 // Do not need to call fd.writeLock here, 66 // because fd is not yet accessible to user, 67 // so no concurrent operations are possible. 68 if err := fd.init(); err != nil { 69 return nil, err 70 } 71 72 if ctx.Done() != nil { 73 // Propagate the Context's deadline and cancellation. 74 // If the context is already done, or if it has a nonzero deadline, 75 // ensure that that is applied before the call to ConnectEx begins 76 // so that we don't return spurious connections. 77 defer fd.pfd.SetWriteDeadline(noDeadline) 78 79 if ctx.Err() != nil { 80 fd.pfd.SetWriteDeadline(aLongTimeAgo) 81 } else { 82 if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() { 83 fd.pfd.SetWriteDeadline(deadline) 84 } 85 86 done := make(chan struct{}) 87 stop := context.AfterFunc(ctx, func() { 88 // Force the runtime's poller to immediately give 89 // up waiting for writability. 90 fd.pfd.SetWriteDeadline(aLongTimeAgo) 91 close(done) 92 }) 93 defer func() { 94 if !stop() { 95 // Wait for the call to SetWriteDeadline to complete so that we can 96 // reset the deadline if everything else succeeded. 97 <-done 98 } 99 }() 100 } 101 } 102 103 if !canUseConnectEx(fd.net) { 104 err := connectFunc(fd.pfd.Sysfd, ra) 105 return nil, os.NewSyscallError("connect", err) 106 } 107 // ConnectEx windows API requires an unconnected, previously bound socket. 108 if la == nil { 109 switch ra.(type) { 110 case *syscall.SockaddrInet4: 111 la = &syscall.SockaddrInet4{} 112 case *syscall.SockaddrInet6: 113 la = &syscall.SockaddrInet6{} 114 default: 115 panic("unexpected type in connect") 116 } 117 if err := syscall.Bind(fd.pfd.Sysfd, la); err != nil { 118 return nil, os.NewSyscallError("bind", err) 119 } 120 } 121 122 var isloopback bool 123 switch ra := ra.(type) { 124 case *syscall.SockaddrInet4: 125 isloopback = ra.Addr[0] == 127 126 case *syscall.SockaddrInet6: 127 isloopback = ra.Addr == [16]byte(IPv6loopback) 128 default: 129 panic("unexpected type in connect") 130 } 131 if isloopback { 132 // This makes ConnectEx() fails faster if the target port on the localhost 133 // is not reachable, instead of waiting for 2s. 134 params := windows.TCP_INITIAL_RTO_PARAMETERS{ 135 Rtt: windows.TCP_INITIAL_RTO_UNSPECIFIED_RTT, // use the default or overridden by the Administrator 136 MaxSynRetransmissions: 1, // minimum possible value before Windows 10.0.16299 137 } 138 if windows.SupportTCPInitialRTONoSYNRetransmissions() { 139 // In Windows 10.0.16299 TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS makes ConnectEx() fails instantly. 140 params.MaxSynRetransmissions = windows.TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS 141 } 142 var out uint32 143 // Don't abort the connection if WSAIoctl fails, as it is only an optimization. 144 // If it fails reliably, we expect TestDialClosedPortFailFast to detect it. 145 _ = fd.pfd.WSAIoctl(windows.SIO_TCP_INITIAL_RTO, (*byte)(unsafe.Pointer(¶ms)), uint32(unsafe.Sizeof(params)), nil, 0, &out, nil, 0) 146 } 147 148 // Call ConnectEx API. 149 if err := fd.pfd.ConnectEx(ra); err != nil { 150 select { 151 case <-ctx.Done(): 152 return nil, mapErr(ctx.Err()) 153 default: 154 if _, ok := err.(syscall.Errno); ok { 155 err = os.NewSyscallError("connectex", err) 156 } 157 return nil, err 158 } 159 } 160 // Refresh socket properties. 161 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)))) 162} 163 164func (c *conn) writeBuffers(v *Buffers) (int64, error) { 165 if !c.ok() { 166 return 0, syscall.EINVAL 167 } 168 n, err := c.fd.writeBuffers(v) 169 if err != nil { 170 return n, &OpError{Op: "wsasend", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} 171 } 172 return n, nil 173} 174 175func (fd *netFD) writeBuffers(buf *Buffers) (int64, error) { 176 n, err := fd.pfd.Writev((*[][]byte)(buf)) 177 runtime.KeepAlive(fd) 178 return n, wrapSyscallError("wsasend", err) 179} 180 181func (fd *netFD) accept() (*netFD, error) { 182 s, rawsa, rsan, errcall, err := fd.pfd.Accept(func() (syscall.Handle, error) { 183 return sysSocket(fd.family, fd.sotype, 0) 184 }) 185 186 if err != nil { 187 if errcall != "" { 188 err = wrapSyscallError(errcall, err) 189 } 190 return nil, err 191 } 192 193 // Associate our new socket with IOCP. 194 netfd, err := newFD(s, fd.family, fd.sotype, fd.net) 195 if err != nil { 196 poll.CloseFunc(s) 197 return nil, err 198 } 199 if err := netfd.init(); err != nil { 200 fd.Close() 201 return nil, err 202 } 203 204 // Get local and peer addr out of AcceptEx buffer. 205 var lrsa, rrsa *syscall.RawSockaddrAny 206 var llen, rlen int32 207 syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])), 208 0, rsan, rsan, &lrsa, &llen, &rrsa, &rlen) 209 lsa, _ := lrsa.Sockaddr() 210 rsa, _ := rrsa.Sockaddr() 211 212 netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa)) 213 return netfd, nil 214} 215 216// Unimplemented functions. 217 218func (fd *netFD) dup() (*os.File, error) { 219 // TODO: Implement this, perhaps using internal/poll.DupCloseOnExec. 220 return nil, syscall.EWINDOWS 221} 222