github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/net/fd_windows.go (about) 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 "os" 11 "runtime" 12 "syscall" 13 "unsafe" 14 ) 15 16 const ( 17 readSyscallName = "wsarecv" 18 readFromSyscallName = "wsarecvfrom" 19 readMsgSyscallName = "wsarecvmsg" 20 writeSyscallName = "wsasend" 21 writeToSyscallName = "wsasendto" 22 writeMsgSyscallName = "wsasendmsg" 23 ) 24 25 // canUseConnectEx reports whether we can use the ConnectEx Windows API call 26 // for the given network type. 27 func canUseConnectEx(net string) bool { 28 switch net { 29 case "tcp", "tcp4", "tcp6": 30 return true 31 } 32 // ConnectEx windows API does not support connectionless sockets. 33 return false 34 } 35 36 func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) { 37 ret := &netFD{ 38 pfd: poll.FD{ 39 Sysfd: sysfd, 40 IsStream: sotype == syscall.SOCK_STREAM, 41 ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW, 42 }, 43 family: family, 44 sotype: sotype, 45 net: net, 46 } 47 return ret, nil 48 } 49 50 func (fd *netFD) init() error { 51 errcall, err := fd.pfd.Init(fd.net, true) 52 if errcall != "" { 53 err = wrapSyscallError(errcall, err) 54 } 55 return err 56 } 57 58 // Always returns nil for connected peer address result. 59 func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (syscall.Sockaddr, error) { 60 // Do not need to call fd.writeLock here, 61 // because fd is not yet accessible to user, 62 // so no concurrent operations are possible. 63 if err := fd.init(); err != nil { 64 return nil, err 65 } 66 if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() { 67 fd.pfd.SetWriteDeadline(deadline) 68 defer fd.pfd.SetWriteDeadline(noDeadline) 69 } 70 if !canUseConnectEx(fd.net) { 71 err := connectFunc(fd.pfd.Sysfd, ra) 72 return nil, os.NewSyscallError("connect", err) 73 } 74 // ConnectEx windows API requires an unconnected, previously bound socket. 75 if la == nil { 76 switch ra.(type) { 77 case *syscall.SockaddrInet4: 78 la = &syscall.SockaddrInet4{} 79 case *syscall.SockaddrInet6: 80 la = &syscall.SockaddrInet6{} 81 default: 82 panic("unexpected type in connect") 83 } 84 if err := syscall.Bind(fd.pfd.Sysfd, la); err != nil { 85 return nil, os.NewSyscallError("bind", err) 86 } 87 } 88 89 // Wait for the goroutine converting context.Done into a write timeout 90 // to exist, otherwise our caller might cancel the context and 91 // cause fd.setWriteDeadline(aLongTimeAgo) to cancel a successful dial. 92 done := make(chan bool) // must be unbuffered 93 defer func() { done <- true }() 94 go func() { 95 select { 96 case <-ctx.Done(): 97 // Force the runtime's poller to immediately give 98 // up waiting for writability. 99 fd.pfd.SetWriteDeadline(aLongTimeAgo) 100 <-done 101 case <-done: 102 } 103 }() 104 105 // Call ConnectEx API. 106 if err := fd.pfd.ConnectEx(ra); err != nil { 107 select { 108 case <-ctx.Done(): 109 return nil, mapErr(ctx.Err()) 110 default: 111 if _, ok := err.(syscall.Errno); ok { 112 err = os.NewSyscallError("connectex", err) 113 } 114 return nil, err 115 } 116 } 117 // Refresh socket properties. 118 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)))) 119 } 120 121 func (c *conn) writeBuffers(v *Buffers) (int64, error) { 122 if !c.ok() { 123 return 0, syscall.EINVAL 124 } 125 n, err := c.fd.writeBuffers(v) 126 if err != nil { 127 return n, &OpError{Op: "wsasend", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} 128 } 129 return n, nil 130 } 131 132 func (fd *netFD) writeBuffers(buf *Buffers) (int64, error) { 133 n, err := fd.pfd.Writev((*[][]byte)(buf)) 134 runtime.KeepAlive(fd) 135 return n, wrapSyscallError("wsasend", err) 136 } 137 138 func (fd *netFD) accept() (*netFD, error) { 139 s, rawsa, rsan, errcall, err := fd.pfd.Accept(func() (syscall.Handle, error) { 140 return sysSocket(fd.family, fd.sotype, 0) 141 }) 142 143 if err != nil { 144 if errcall != "" { 145 err = wrapSyscallError(errcall, err) 146 } 147 return nil, err 148 } 149 150 // Associate our new socket with IOCP. 151 netfd, err := newFD(s, fd.family, fd.sotype, fd.net) 152 if err != nil { 153 poll.CloseFunc(s) 154 return nil, err 155 } 156 if err := netfd.init(); err != nil { 157 fd.Close() 158 return nil, err 159 } 160 161 // Get local and peer addr out of AcceptEx buffer. 162 var lrsa, rrsa *syscall.RawSockaddrAny 163 var llen, rlen int32 164 syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])), 165 0, rsan, rsan, &lrsa, &llen, &rrsa, &rlen) 166 lsa, _ := lrsa.Sockaddr() 167 rsa, _ := rrsa.Sockaddr() 168 169 netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa)) 170 return netfd, nil 171 } 172 173 // Unimplemented functions. 174 175 func (fd *netFD) dup() (*os.File, error) { 176 // TODO: Implement this 177 return nil, syscall.EWINDOWS 178 }