github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/integrationtests/self/stateless_reset_test.go (about)

     1  package self_test
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"fmt"
     7  	"net"
     8  	"sync/atomic"
     9  	"time"
    10  
    11  	"github.com/metacubex/quic-go"
    12  	quicproxy "github.com/metacubex/quic-go/integrationtests/tools/proxy"
    13  
    14  	. "github.com/onsi/ginkgo/v2"
    15  	. "github.com/onsi/gomega"
    16  )
    17  
    18  var _ = Describe("Stateless Resets", func() {
    19  	connIDLens := []int{0, 10}
    20  
    21  	for i := range connIDLens {
    22  		connIDLen := connIDLens[i]
    23  
    24  		It(fmt.Sprintf("sends and recognizes stateless resets, for %d byte connection IDs", connIDLen), func() {
    25  			var statelessResetKey quic.StatelessResetKey
    26  			rand.Read(statelessResetKey[:])
    27  
    28  			c, err := net.ListenUDP("udp", nil)
    29  			Expect(err).ToNot(HaveOccurred())
    30  			tr := &quic.Transport{
    31  				Conn:               c,
    32  				StatelessResetKey:  &statelessResetKey,
    33  				ConnectionIDLength: connIDLen,
    34  			}
    35  			defer tr.Close()
    36  			ln, err := tr.Listen(getTLSConfig(), getQuicConfig(nil))
    37  			Expect(err).ToNot(HaveOccurred())
    38  			serverPort := ln.Addr().(*net.UDPAddr).Port
    39  
    40  			closeServer := make(chan struct{})
    41  
    42  			go func() {
    43  				defer GinkgoRecover()
    44  				conn, err := ln.Accept(context.Background())
    45  				Expect(err).ToNot(HaveOccurred())
    46  				str, err := conn.OpenStream()
    47  				Expect(err).ToNot(HaveOccurred())
    48  				_, err = str.Write([]byte("foobar"))
    49  				Expect(err).ToNot(HaveOccurred())
    50  				<-closeServer
    51  				Expect(ln.Close()).To(Succeed())
    52  				Expect(tr.Close()).To(Succeed())
    53  			}()
    54  
    55  			var drop atomic.Bool
    56  			proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
    57  				RemoteAddr: fmt.Sprintf("localhost:%d", serverPort),
    58  				DropPacket: func(quicproxy.Direction, []byte) bool {
    59  					return drop.Load()
    60  				},
    61  			})
    62  			Expect(err).ToNot(HaveOccurred())
    63  			defer proxy.Close()
    64  
    65  			addr, err := net.ResolveUDPAddr("udp", "localhost:0")
    66  			Expect(err).ToNot(HaveOccurred())
    67  			udpConn, err := net.ListenUDP("udp", addr)
    68  			Expect(err).ToNot(HaveOccurred())
    69  			defer udpConn.Close()
    70  			cl := &quic.Transport{
    71  				Conn:               udpConn,
    72  				ConnectionIDLength: connIDLen,
    73  			}
    74  			defer cl.Close()
    75  			conn, err := cl.Dial(
    76  				context.Background(),
    77  				&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: proxy.LocalPort()},
    78  				getTLSClientConfig(),
    79  				getQuicConfig(&quic.Config{MaxIdleTimeout: 2 * time.Second}),
    80  			)
    81  			Expect(err).ToNot(HaveOccurred())
    82  			str, err := conn.AcceptStream(context.Background())
    83  			Expect(err).ToNot(HaveOccurred())
    84  			data := make([]byte, 6)
    85  			_, err = str.Read(data)
    86  			Expect(err).ToNot(HaveOccurred())
    87  			Expect(data).To(Equal([]byte("foobar")))
    88  
    89  			// make sure that the CONNECTION_CLOSE is dropped
    90  			drop.Store(true)
    91  			close(closeServer)
    92  			time.Sleep(100 * time.Millisecond)
    93  
    94  			// We need to create a new Transport here, since the old one is still sending out
    95  			// CONNECTION_CLOSE packets for (recently) closed connections).
    96  			tr2 := &quic.Transport{
    97  				Conn:               c,
    98  				ConnectionIDLength: connIDLen,
    99  				StatelessResetKey:  &statelessResetKey,
   100  			}
   101  			defer tr2.Close()
   102  			ln2, err := tr2.Listen(getTLSConfig(), getQuicConfig(nil))
   103  			Expect(err).ToNot(HaveOccurred())
   104  			drop.Store(false)
   105  
   106  			acceptStopped := make(chan struct{})
   107  			go func() {
   108  				defer GinkgoRecover()
   109  				_, err := ln2.Accept(context.Background())
   110  				Expect(err).To(HaveOccurred())
   111  				close(acceptStopped)
   112  			}()
   113  
   114  			// Trigger something (not too small) to be sent, so that we receive the stateless reset.
   115  			// If the client already sent another packet, it might already have received a packet.
   116  			_, serr := str.Write([]byte("Lorem ipsum dolor sit amet."))
   117  			if serr == nil {
   118  				_, serr = str.Read([]byte{0})
   119  			}
   120  			Expect(serr).To(HaveOccurred())
   121  			Expect(serr).To(BeAssignableToTypeOf(&quic.StatelessResetError{}))
   122  			Expect(ln2.Close()).To(Succeed())
   123  			Eventually(acceptStopped).Should(BeClosed())
   124  		})
   125  	}
   126  })