uquic/integrationtests/self/stateless_reset_test.go
Gaukas Wang 95575f5fe7
break: update repo url [ci skip]
uTLS is not yet bumped to the new version, so this commit breaks the dependencies relationship by getting rid of the local replace.
2023-08-03 18:58:52 -06:00

126 lines
3.7 KiB
Go

package self_test
import (
"context"
"crypto/rand"
"fmt"
"net"
"sync/atomic"
"time"
quic "github.com/refraction-networking/uquic"
quicproxy "github.com/refraction-networking/uquic/integrationtests/tools/proxy"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Stateless Resets", func() {
connIDLens := []int{0, 10}
for i := range connIDLens {
connIDLen := connIDLens[i]
It(fmt.Sprintf("sends and recognizes stateless resets, for %d byte connection IDs", connIDLen), func() {
var statelessResetKey quic.StatelessResetKey
rand.Read(statelessResetKey[:])
c, err := net.ListenUDP("udp", nil)
Expect(err).ToNot(HaveOccurred())
tr := &quic.Transport{
Conn: c,
StatelessResetKey: &statelessResetKey,
ConnectionIDLength: connIDLen,
}
defer tr.Close()
ln, err := tr.Listen(getTLSConfig(), getQuicConfig(nil))
Expect(err).ToNot(HaveOccurred())
serverPort := ln.Addr().(*net.UDPAddr).Port
closeServer := make(chan struct{})
go func() {
defer GinkgoRecover()
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
str, err := conn.OpenStream()
Expect(err).ToNot(HaveOccurred())
_, err = str.Write([]byte("foobar"))
Expect(err).ToNot(HaveOccurred())
<-closeServer
Expect(ln.Close()).To(Succeed())
Expect(tr.Close()).To(Succeed())
}()
var drop atomic.Bool
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: fmt.Sprintf("localhost:%d", serverPort),
DropPacket: func(quicproxy.Direction, []byte) bool {
return drop.Load()
},
})
Expect(err).ToNot(HaveOccurred())
defer proxy.Close()
addr, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
udpConn, err := net.ListenUDP("udp", addr)
Expect(err).ToNot(HaveOccurred())
defer udpConn.Close()
cl := &quic.Transport{
Conn: udpConn,
ConnectionIDLength: connIDLen,
}
defer cl.Close()
conn, err := cl.Dial(
context.Background(),
&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: proxy.LocalPort()},
getTLSClientConfig(),
getQuicConfig(&quic.Config{MaxIdleTimeout: 2 * time.Second}),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptStream(context.Background())
Expect(err).ToNot(HaveOccurred())
data := make([]byte, 6)
_, err = str.Read(data)
Expect(err).ToNot(HaveOccurred())
Expect(data).To(Equal([]byte("foobar")))
// make sure that the CONNECTION_CLOSE is dropped
drop.Store(true)
close(closeServer)
time.Sleep(100 * time.Millisecond)
// We need to create a new Transport here, since the old one is still sending out
// CONNECTION_CLOSE packets for (recently) closed connections).
tr2 := &quic.Transport{
Conn: c,
ConnectionIDLength: connIDLen,
StatelessResetKey: &statelessResetKey,
}
defer tr2.Close()
ln2, err := tr2.Listen(getTLSConfig(), getQuicConfig(nil))
Expect(err).ToNot(HaveOccurred())
drop.Store(false)
acceptStopped := make(chan struct{})
go func() {
defer GinkgoRecover()
_, err := ln2.Accept(context.Background())
Expect(err).To(HaveOccurred())
close(acceptStopped)
}()
// Trigger something (not too small) to be sent, so that we receive the stateless reset.
// If the client already sent another packet, it might already have received a packet.
_, serr := str.Write([]byte("Lorem ipsum dolor sit amet."))
if serr == nil {
_, serr = str.Read([]byte{0})
}
Expect(serr).To(HaveOccurred())
Expect(serr).To(BeAssignableToTypeOf(&quic.StatelessResetError{}))
Expect(ln2.Close()).To(Succeed())
Eventually(acceptStopped).Should(BeClosed())
})
}
})