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(&params)), 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