1// Copyright 2017 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 poll_test
6
7import (
8	"errors"
9	"fmt"
10	"internal/poll"
11	"internal/syscall/windows"
12	"os"
13	"sync"
14	"syscall"
15	"testing"
16	"unsafe"
17)
18
19type loggedFD struct {
20	Net string
21	FD  *poll.FD
22	Err error
23}
24
25var (
26	logMu     sync.Mutex
27	loggedFDs map[syscall.Handle]*loggedFD
28)
29
30func logFD(net string, fd *poll.FD, err error) {
31	logMu.Lock()
32	defer logMu.Unlock()
33
34	loggedFDs[fd.Sysfd] = &loggedFD{
35		Net: net,
36		FD:  fd,
37		Err: err,
38	}
39}
40
41func init() {
42	loggedFDs = make(map[syscall.Handle]*loggedFD)
43	*poll.LogInitFD = logFD
44
45	poll.InitWSA()
46}
47
48func findLoggedFD(h syscall.Handle) (lfd *loggedFD, found bool) {
49	logMu.Lock()
50	defer logMu.Unlock()
51
52	lfd, found = loggedFDs[h]
53	return lfd, found
54}
55
56// checkFileIsNotPartOfNetpoll verifies that f is not managed by netpoll.
57// It returns error, if check fails.
58func checkFileIsNotPartOfNetpoll(f *os.File) error {
59	lfd, found := findLoggedFD(syscall.Handle(f.Fd()))
60	if !found {
61		return fmt.Errorf("%v fd=%v: is not found in the log", f.Name(), f.Fd())
62	}
63	if lfd.FD.IsPartOfNetpoll() {
64		return fmt.Errorf("%v fd=%v: is part of netpoll, but should not be (logged: net=%v err=%v)", f.Name(), f.Fd(), lfd.Net, lfd.Err)
65	}
66	return nil
67}
68
69func TestFileFdsAreInitialised(t *testing.T) {
70	exe, err := os.Executable()
71	if err != nil {
72		t.Fatal(err)
73	}
74	f, err := os.Open(exe)
75	if err != nil {
76		t.Fatal(err)
77	}
78	defer f.Close()
79
80	err = checkFileIsNotPartOfNetpoll(f)
81	if err != nil {
82		t.Fatal(err)
83	}
84}
85
86func TestSerialFdsAreInitialised(t *testing.T) {
87	for _, name := range []string{"COM1", "COM2", "COM3", "COM4"} {
88		t.Run(name, func(t *testing.T) {
89			h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(name),
90				syscall.GENERIC_READ|syscall.GENERIC_WRITE,
91				0,
92				nil,
93				syscall.OPEN_EXISTING,
94				syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED,
95				0)
96			if err != nil {
97				if errno, ok := err.(syscall.Errno); ok {
98					switch errno {
99					case syscall.ERROR_FILE_NOT_FOUND,
100						syscall.ERROR_ACCESS_DENIED:
101						t.Log("Skipping: ", err)
102						return
103					}
104				}
105				t.Fatal(err)
106			}
107			f := os.NewFile(uintptr(h), name)
108			defer f.Close()
109
110			err = checkFileIsNotPartOfNetpoll(f)
111			if err != nil {
112				t.Fatal(err)
113			}
114		})
115	}
116}
117
118func TestWSASocketConflict(t *testing.T) {
119	s, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_OVERLAPPED)
120	if err != nil {
121		t.Fatal(err)
122	}
123	fd := poll.FD{Sysfd: s, IsStream: true, ZeroReadIsEOF: true}
124	_, err = fd.Init("tcp", true)
125	if err != nil {
126		syscall.CloseHandle(s)
127		t.Fatal(err)
128	}
129	defer fd.Close()
130
131	const SIO_TCP_INFO = syscall.IOC_INOUT | syscall.IOC_VENDOR | 39
132	inbuf := uint32(0)
133	var outbuf _TCP_INFO_v0
134	cbbr := uint32(0)
135
136	var ov syscall.Overlapped
137	// Create an event so that we can efficiently wait for completion
138	// of a requested overlapped I/O operation.
139	ov.HEvent, _ = windows.CreateEvent(nil, 0, 0, nil)
140	if ov.HEvent == 0 {
141		t.Fatalf("could not create the event!")
142	}
143	defer syscall.CloseHandle(ov.HEvent)
144
145	if err = fd.WSAIoctl(
146		SIO_TCP_INFO,
147		(*byte)(unsafe.Pointer(&inbuf)),
148		uint32(unsafe.Sizeof(inbuf)),
149		(*byte)(unsafe.Pointer(&outbuf)),
150		uint32(unsafe.Sizeof(outbuf)),
151		&cbbr,
152		&ov,
153		0,
154	); err != nil && !errors.Is(err, syscall.ERROR_IO_PENDING) {
155		t.Fatalf("could not perform the WSAIoctl: %v", err)
156	}
157
158	if err != nil && errors.Is(err, syscall.ERROR_IO_PENDING) {
159		// It is possible that the overlapped I/O operation completed
160		// immediately so there is no need to wait for it to complete.
161		if res, err := syscall.WaitForSingleObject(ov.HEvent, syscall.INFINITE); res != 0 {
162			t.Fatalf("waiting for the completion of the overlapped IO failed: %v", err)
163		}
164	}
165}
166
167type _TCP_INFO_v0 struct {
168	State             uint32
169	Mss               uint32
170	ConnectionTimeMs  uint64
171	TimestampsEnabled bool
172	RttUs             uint32
173	MinRttUs          uint32
174	BytesInFlight     uint32
175	Cwnd              uint32
176	SndWnd            uint32
177	RcvWnd            uint32
178	RcvBuf            uint32
179	BytesOut          uint64
180	BytesIn           uint64
181	BytesReordered    uint32
182	BytesRetrans      uint32
183	FastRetrans       uint32
184	DupAcksIn         uint32
185	TimeoutEpisodes   uint32
186	SynRetrans        uint8
187}
188