integration_windows_test.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. //go:build windows
  2. package windivert
  3. import (
  4. "errors"
  5. "net/netip"
  6. "testing"
  7. "time"
  8. "github.com/stretchr/testify/require"
  9. "golang.org/x/sys/windows"
  10. )
  11. func openHandle(t *testing.T, filter *Filter, flags Flag) *Handle {
  12. t.Helper()
  13. h, err := Open(filter, LayerNetwork, 0, flags)
  14. require.NoError(t, err)
  15. return h
  16. }
  17. // A send-only handle installs+opens the driver but does not attach a
  18. // receive filter, so it exercises the full driver-install path without
  19. // diverting any live traffic on the host.
  20. func TestIntegrationOpenSendOnly(t *testing.T) {
  21. h := openHandle(t, nil, FlagSendOnly)
  22. require.NoError(t, h.Close())
  23. }
  24. // Close is idempotent per the doc contract.
  25. func TestIntegrationCloseTwice(t *testing.T) {
  26. h := openHandle(t, nil, FlagSendOnly)
  27. require.NoError(t, h.Close())
  28. require.NoError(t, h.Close())
  29. }
  30. // Recv must unblock when the handle is closed concurrently. Without this,
  31. // the spoofer's run goroutine could deadlock on shutdown.
  32. func TestIntegrationRecvAbortsOnClose(t *testing.T) {
  33. // A filter no live traffic will match, so Recv blocks indefinitely
  34. // until Close aborts the overlapped I/O.
  35. filter, err := OutboundTCP(
  36. netip.MustParseAddrPort("10.255.255.254:1"),
  37. netip.MustParseAddrPort("10.255.255.253:2"),
  38. )
  39. require.NoError(t, err)
  40. h := openHandle(t, filter, 0)
  41. errCh := make(chan error, 1)
  42. go func() {
  43. buf := make([]byte, MTUMax)
  44. _, _, recvErr := h.Recv(buf)
  45. errCh <- recvErr
  46. }()
  47. // Let Recv reach the blocking DeviceIoControl before Close races in.
  48. time.Sleep(200 * time.Millisecond)
  49. require.NoError(t, h.Close())
  50. select {
  51. case err := <-errCh:
  52. require.Error(t, err)
  53. require.True(t, errors.Is(err, windows.ERROR_OPERATION_ABORTED),
  54. "Recv should return ERROR_OPERATION_ABORTED, got %v", err)
  55. case <-time.After(3 * time.Second):
  56. t.Fatal("Recv did not unblock within 3s after Close")
  57. }
  58. }
  59. // Two concurrent Open calls must both succeed: the first wins the driver
  60. // install race, the second reuses the already-running service.
  61. func TestIntegrationConcurrentOpen(t *testing.T) {
  62. errCh := make(chan error, 2)
  63. handles := make(chan *Handle, 2)
  64. for i := 0; i < 2; i++ {
  65. go func() {
  66. h, err := Open(nil, LayerNetwork, 0, FlagSendOnly)
  67. handles <- h
  68. errCh <- err
  69. }()
  70. }
  71. for i := 0; i < 2; i++ {
  72. err := <-errCh
  73. h := <-handles
  74. require.NoError(t, err)
  75. require.NoError(t, h.Close())
  76. }
  77. }