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(¶ms)), 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