golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/udp_test.go (about)

     1  // Copyright 2023 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  //go:build go1.21
     6  
     7  package quic
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"net"
    13  	"net/netip"
    14  	"runtime"
    15  	"testing"
    16  )
    17  
    18  func TestUDPSourceUnspecified(t *testing.T) {
    19  	// Send datagram with no source address set.
    20  	runUDPTest(t, func(t *testing.T, test udpTest) {
    21  		t.Logf("%v", test.dstAddr)
    22  		data := []byte("source unspecified")
    23  		if err := test.src.Write(datagram{
    24  			b:        data,
    25  			peerAddr: test.dstAddr,
    26  		}); err != nil {
    27  			t.Fatalf("Write: %v", err)
    28  		}
    29  		got := <-test.dgramc
    30  		if !bytes.Equal(got.b, data) {
    31  			t.Errorf("got datagram {%x}, want {%x}", got.b, data)
    32  		}
    33  	})
    34  }
    35  
    36  func TestUDPSourceSpecified(t *testing.T) {
    37  	// Send datagram with source address set.
    38  	runUDPTest(t, func(t *testing.T, test udpTest) {
    39  		data := []byte("source specified")
    40  		if err := test.src.Write(datagram{
    41  			b:         data,
    42  			peerAddr:  test.dstAddr,
    43  			localAddr: test.src.LocalAddr(),
    44  		}); err != nil {
    45  			t.Fatalf("Write: %v", err)
    46  		}
    47  		got := <-test.dgramc
    48  		if !bytes.Equal(got.b, data) {
    49  			t.Errorf("got datagram {%x}, want {%x}", got.b, data)
    50  		}
    51  	})
    52  }
    53  
    54  func TestUDPSourceInvalid(t *testing.T) {
    55  	// Send datagram with source address set to an address not associated with the connection.
    56  	if !udpInvalidLocalAddrIsError {
    57  		t.Skipf("%v: sending from invalid source succeeds", runtime.GOOS)
    58  	}
    59  	runUDPTest(t, func(t *testing.T, test udpTest) {
    60  		var localAddr netip.AddrPort
    61  		if test.src.LocalAddr().Addr().Is4() {
    62  			localAddr = netip.MustParseAddrPort("127.0.0.2:1234")
    63  		} else {
    64  			localAddr = netip.MustParseAddrPort("[::2]:1234")
    65  		}
    66  		data := []byte("source invalid")
    67  		if err := test.src.Write(datagram{
    68  			b:         data,
    69  			peerAddr:  test.dstAddr,
    70  			localAddr: localAddr,
    71  		}); err == nil {
    72  			t.Errorf("Write with invalid localAddr succeeded; want error")
    73  		}
    74  	})
    75  }
    76  
    77  func TestUDPECN(t *testing.T) {
    78  	if !udpECNSupport {
    79  		t.Skipf("%v: no ECN support", runtime.GOOS)
    80  	}
    81  	// Send datagrams with ECN bits set, verify the ECN bits are received.
    82  	runUDPTest(t, func(t *testing.T, test udpTest) {
    83  		for _, ecn := range []ecnBits{ecnNotECT, ecnECT1, ecnECT0, ecnCE} {
    84  			if err := test.src.Write(datagram{
    85  				b:        []byte{1, 2, 3, 4},
    86  				peerAddr: test.dstAddr,
    87  				ecn:      ecn,
    88  			}); err != nil {
    89  				t.Fatalf("Write: %v", err)
    90  			}
    91  			got := <-test.dgramc
    92  			if got.ecn != ecn {
    93  				t.Errorf("sending ECN bits %x, got %x", ecn, got.ecn)
    94  			}
    95  		}
    96  	})
    97  }
    98  
    99  type udpTest struct {
   100  	src     *netUDPConn
   101  	dst     *netUDPConn
   102  	dstAddr netip.AddrPort
   103  	dgramc  chan *datagram
   104  }
   105  
   106  // runUDPTest calls f with a pair of UDPConns in a matrix of network variations:
   107  // udp, udp4, and udp6, and variations on binding to an unspecified address (0.0.0.0)
   108  // or a specified one.
   109  func runUDPTest(t *testing.T, f func(t *testing.T, u udpTest)) {
   110  	for _, test := range []struct {
   111  		srcNet, srcAddr, dstNet, dstAddr string
   112  	}{
   113  		{"udp4", "127.0.0.1", "udp", ""},
   114  		{"udp4", "127.0.0.1", "udp4", ""},
   115  		{"udp4", "127.0.0.1", "udp4", "127.0.0.1"},
   116  		{"udp6", "::1", "udp", ""},
   117  		{"udp6", "::1", "udp6", ""},
   118  		{"udp6", "::1", "udp6", "::1"},
   119  	} {
   120  		spec := "spec"
   121  		if test.dstAddr == "" {
   122  			spec = "unspec"
   123  		}
   124  		t.Run(fmt.Sprintf("%v/%v/%v", test.srcNet, test.dstNet, spec), func(t *testing.T) {
   125  			// See: https://go.googlesource.com/go/+/refs/tags/go1.22.0/src/net/ipsock.go#47
   126  			// On these platforms, conns with network="udp" cannot accept IPv6.
   127  			switch runtime.GOOS {
   128  			case "dragonfly", "openbsd":
   129  				if test.srcNet == "udp6" && test.dstNet == "udp" {
   130  					t.Skipf("%v: no support for mapping IPv4 address to IPv6", runtime.GOOS)
   131  				}
   132  			}
   133  			if runtime.GOARCH == "wasm" && test.srcNet == "udp6" {
   134  				t.Skipf("%v: IPv6 tests fail when using wasm fake net", runtime.GOARCH)
   135  			}
   136  
   137  			srcAddr := netip.AddrPortFrom(netip.MustParseAddr(test.srcAddr), 0)
   138  			srcConn, err := net.ListenUDP(test.srcNet, net.UDPAddrFromAddrPort(srcAddr))
   139  			if err != nil {
   140  				// If ListenUDP fails here, we presumably don't have
   141  				// IPv4/IPv6 configured.
   142  				t.Skipf("ListenUDP(%q, %v) = %v", test.srcNet, srcAddr, err)
   143  			}
   144  			t.Cleanup(func() { srcConn.Close() })
   145  			src, err := newNetUDPConn(srcConn)
   146  			if err != nil {
   147  				t.Fatalf("newNetUDPConn: %v", err)
   148  			}
   149  
   150  			var dstAddr netip.AddrPort
   151  			if test.dstAddr != "" {
   152  				dstAddr = netip.AddrPortFrom(netip.MustParseAddr(test.dstAddr), 0)
   153  			}
   154  			dstConn, err := net.ListenUDP(test.dstNet, net.UDPAddrFromAddrPort(dstAddr))
   155  			if err != nil {
   156  				t.Skipf("ListenUDP(%q, nil) = %v", test.dstNet, err)
   157  			}
   158  			dst, err := newNetUDPConn(dstConn)
   159  			if err != nil {
   160  				dstConn.Close()
   161  				t.Fatalf("newNetUDPConn: %v", err)
   162  			}
   163  
   164  			dgramc := make(chan *datagram)
   165  			go func() {
   166  				defer close(dgramc)
   167  				dst.Read(func(dgram *datagram) {
   168  					dgramc <- dgram
   169  				})
   170  			}()
   171  			t.Cleanup(func() {
   172  				dstConn.Close()
   173  				for range dgramc {
   174  					t.Errorf("test read unexpected datagram")
   175  				}
   176  			})
   177  
   178  			f(t, udpTest{
   179  				src: src,
   180  				dst: dst,
   181  				dstAddr: netip.AddrPortFrom(
   182  					srcAddr.Addr(),
   183  					dst.LocalAddr().Port(),
   184  				),
   185  				dgramc: dgramc,
   186  			})
   187  		})
   188  	}
   189  }