Source file src/internal/poll/fd_windows_test.go

     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  
     5  package poll_test
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"internal/poll"
    11  	"internal/syscall/windows"
    12  	"os"
    13  	"sync"
    14  	"syscall"
    15  	"testing"
    16  	"unsafe"
    17  )
    18  
    19  type loggedFD struct {
    20  	Net string
    21  	FD  *poll.FD
    22  	Err error
    23  }
    24  
    25  var (
    26  	logMu     sync.Mutex
    27  	loggedFDs map[syscall.Handle]*loggedFD
    28  )
    29  
    30  func 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  
    41  func init() {
    42  	loggedFDs = make(map[syscall.Handle]*loggedFD)
    43  	*poll.LogInitFD = logFD
    44  
    45  	poll.InitWSA()
    46  }
    47  
    48  func 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.
    58  func 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  
    69  func 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  
    86  func 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  
   118  func 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  
   167  type _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  

View as plain text