From f9f68cf0a3b493117356940183252f76c7ff0053 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 30 May 2023 18:17:27 +0200 Subject: [PATCH] Update quic-go --- dnscrypt-proxy/xtransport.go | 5 +- go.mod | 10 +- go.sum | 23 +- vendor/github.com/quic-go/quic-go/README.md | 25 +- vendor/github.com/quic-go/quic-go/client.go | 316 ++++------- vendor/github.com/quic-go/quic-go/config.go | 35 +- .../github.com/quic-go/quic-go/connection.go | 32 +- .../quic-go/quic-go/http3/client.go | 8 +- .../quic-go/quic-go/http3/mockgen.go | 2 + .../quic-go/quic-go/http3/response_writer.go | 11 + .../quic-go/quic-go/http3/roundtrip.go | 32 +- .../quic-go/quic-go/http3/server.go | 38 +- .../github.com/quic-go/quic-go/interface.go | 51 +- .../quic-go/internal/ackhandler/packet.go | 2 +- .../quic-go/internal/congestion/pacer.go | 19 +- .../internal/handshake/crypto_setup.go | 10 +- .../quic-go/internal/protocol/params.go | 3 + .../quic-go/internal/protocol/protocol.go | 3 + .../quic-go/internal/utils/rtt_stats.go | 6 +- .../quic-go/internal/wire/ack_frame.go | 19 +- .../quic-go/quic-go/internal/wire/header.go | 2 - .../internal/wire/transport_parameters.go | 12 +- .../quic-go/quic-go/logging/interface.go | 7 - .../quic-go/quic-go/logging/multiplex.go | 11 - .../quic-go/quic-go/logging/null_tracer.go | 4 - vendor/github.com/quic-go/quic-go/mockgen.go | 3 - .../github.com/quic-go/quic-go/multiplexer.go | 69 +-- .../quic-go/quic-go/packet_handler_map.go | 344 ++---------- .../quic-go/quic-go/packet_packer.go | 2 +- .../quic-go/quic-go/quicvarint/varint.go | 19 - .../github.com/quic-go/quic-go/send_conn.go | 23 +- vendor/github.com/quic-go/quic-go/server.go | 524 ++++++++++++------ .../quic-go/quic-go/sys_conn_buffers.go | 68 +++ .../quic-go/quic-go/sys_conn_buffers_write.go | 70 +++ .../quic-go/quic-go/sys_conn_helper_linux.go | 26 +- .../quic-go/sys_conn_helper_nonlinux.go | 6 + .../quic-go/quic-go/sys_conn_no_oob.go | 5 +- .../quic-go/quic-go/sys_conn_oob.go | 26 +- .../quic-go/quic-go/sys_conn_windows.go | 28 +- .../github.com/quic-go/quic-go/transport.go | 416 ++++++++++++++ .../quic-go/quic-go/zero_rtt_queue.go | 34 -- vendor/modules.txt | 16 +- 42 files changed, 1371 insertions(+), 994 deletions(-) create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_buffers.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_buffers_write.go create mode 100644 vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go create mode 100644 vendor/github.com/quic-go/quic-go/transport.go delete mode 100644 vendor/github.com/quic-go/quic-go/zero_rtt_queue.go diff --git a/dnscrypt-proxy/xtransport.go b/dnscrypt-proxy/xtransport.go index b30d362e..18a1f702 100644 --- a/dnscrypt-proxy/xtransport.go +++ b/dnscrypt-proxy/xtransport.go @@ -279,7 +279,7 @@ func (xTransport *XTransport) rebuildTransport() { ipOnly = "[" + cachedIP.String() + "]" } } else { - dlog.Debugf("[%s] IP address was not cached in H3 DialContext", host) + dlog.Debugf("[%s] IP address was not cached in H3 context", host) } addrStr = ipOnly + ":" + strconv.Itoa(port) udpAddr, err := net.ResolveUDPAddr("udp", addrStr) @@ -292,7 +292,8 @@ func (xTransport *XTransport) rebuildTransport() { return nil, err } } - return quic.DialEarlyContext(ctx, xTransport.h3UDPConn, udpAddr, host, tlsCfg, cfg) + tlsCfg.ServerName = host + return quic.DialEarly(ctx, xTransport.h3UDPConn, udpAddr, tlsCfg, cfg) }} xTransport.h3Transport = h3Transport } diff --git a/go.mod b/go.mod index 308547af..29043cbb 100644 --- a/go.mod +++ b/go.mod @@ -10,17 +10,17 @@ require ( github.com/hashicorp/go-immutable-radix v1.3.1 github.com/hashicorp/golang-lru v0.5.0 github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb - github.com/jedisct1/dlog v0.0.0-20230211133026-0338add8743f + github.com/jedisct1/dlog v0.0.0-20230513092435-93bc2a55ad59 github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e github.com/jedisct1/go-dnsstamps v0.0.0-20230211133001-124a632de565 - github.com/jedisct1/go-hpke-compact v0.0.0-20230211184420-51d4440017ce - github.com/jedisct1/go-minisign v0.0.0-20230410063418-3d885d71f62c - github.com/jedisct1/xsecretbox v0.0.0-20230211185120-2025cdbb9f0f + github.com/jedisct1/go-hpke-compact v0.0.0-20230513092519-91c912752223 + github.com/jedisct1/go-minisign v0.0.0-20230513092556-d96eb068239a + github.com/jedisct1/xsecretbox v0.0.0-20230513092623-8c0b2dff5e24 github.com/k-sone/critbitgo v1.4.0 github.com/kardianos/service v1.2.2 github.com/miekg/dns v1.1.54 github.com/powerman/check v1.7.0 - github.com/quic-go/quic-go v0.34.0 + github.com/quic-go/quic-go v0.35.0 golang.org/x/crypto v0.9.0 golang.org/x/net v0.10.0 golang.org/x/sys v0.8.0 diff --git a/go.sum b/go.sum index e2dd5972..32f6da30 100644 --- a/go.sum +++ b/go.sum @@ -36,18 +36,18 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jedisct1/dlog v0.0.0-20230211133026-0338add8743f h1:FBZqNKGn+mB/9PZG/1bNGKQ1azw2o7dZV0ZGmTPoboA= -github.com/jedisct1/dlog v0.0.0-20230211133026-0338add8743f/go.mod h1:35aII3PkLMvmc8daWy0vcZXDU+a40lJczHHTFRJmnvw= +github.com/jedisct1/dlog v0.0.0-20230513092435-93bc2a55ad59 h1:Bw7a+o84VjOun21ieO9Y6d9GuE2MAjlR9aFcvA0K7RU= +github.com/jedisct1/dlog v0.0.0-20230513092435-93bc2a55ad59/go.mod h1:Lfl1ng5dZ6ywtV5i6DoJ1ZXkpB6GbIaVitwG/vWrzgc= github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e h1:tzG4EjKgHIqKVkLIAC4pXTIapuM2BR05uXokEEysAXA= github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw= github.com/jedisct1/go-dnsstamps v0.0.0-20230211133001-124a632de565 h1:BPBMaUCgtmiHvqgugbSuegXjADJfERsPbmRqgdq8Pjo= github.com/jedisct1/go-dnsstamps v0.0.0-20230211133001-124a632de565/go.mod h1:mEGEFZsGe4sG5Mb3Xi89pmsy+TZ0946ArbYMGKAM5uA= -github.com/jedisct1/go-hpke-compact v0.0.0-20230211184420-51d4440017ce h1:RvDulxEuYjqIWSphi54mQGXWDAp0yjeCJQKhBj6Qkmc= -github.com/jedisct1/go-hpke-compact v0.0.0-20230211184420-51d4440017ce/go.mod h1:T9nvXa0PqmoSt9JhtW0rv9iXg5xoxW/ZfVhdm9Y4QJw= -github.com/jedisct1/go-minisign v0.0.0-20230410063418-3d885d71f62c h1:SJIUnIcy3NTjPM1acfLsIDQ6dZkBXkQLt/vrg7pshX0= -github.com/jedisct1/go-minisign v0.0.0-20230410063418-3d885d71f62c/go.mod h1:euIjSueBwqJWwVr7rS2bowelVBVoN827tx5n87jnSJM= -github.com/jedisct1/xsecretbox v0.0.0-20230211185120-2025cdbb9f0f h1:1qPoL+ur1rCouHMB737ujJGXVFoXGafDY+kmgXjpicA= -github.com/jedisct1/xsecretbox v0.0.0-20230211185120-2025cdbb9f0f/go.mod h1:39HtRgNq2rdKzzeqfPNFLY000t4ktVThX3ncyK58lsA= +github.com/jedisct1/go-hpke-compact v0.0.0-20230513092519-91c912752223 h1:6q4bclrFDC6XhljyqpgnQQJHB0jJ9JCxamxznYjO6e8= +github.com/jedisct1/go-hpke-compact v0.0.0-20230513092519-91c912752223/go.mod h1:WchTSQZ7F2frIm7p+CtDAWQ7HuTj+bTIMC612B0iMKY= +github.com/jedisct1/go-minisign v0.0.0-20230513092556-d96eb068239a h1:NuwTR1jHId8kDrB+tYYIb6zLfNFqNh4N5sLJLuFcie0= +github.com/jedisct1/go-minisign v0.0.0-20230513092556-d96eb068239a/go.mod h1:o7E4qbRBjPKXV4D4gtm+Eaqsr3obg6zOZyRqQk6JwjQ= +github.com/jedisct1/xsecretbox v0.0.0-20230513092623-8c0b2dff5e24 h1:LhA515GzR+6uvKiZUAcAaXWvmcaJcruJ0he7aerypIE= +github.com/jedisct1/xsecretbox v0.0.0-20230513092623-8c0b2dff5e24/go.mod h1:MaGh/D9rZfuMUZ3+Qv/OEP7vGGPe404xiGtF1nRqdNk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/k-sone/critbitgo v1.4.0 h1:l71cTyBGeh6X5ATh6Fibgw3+rtNT80BA0uNNWgkPrbE= @@ -73,15 +73,15 @@ github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc8 github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= -github.com/quic-go/quic-go v0.34.0 h1:OvOJ9LFjTySgwOTYUZmNoq0FzVicP8YujpV0kB7m2lU= -github.com/quic-go/quic-go v0.34.0/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g= +github.com/quic-go/quic-go v0.35.0 h1:JXIf219xJK+4qGeY52rlnrVqeB2AXUAwfLU9JSoWXwg= +github.com/quic-go/quic-go v0.35.0/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -138,4 +138,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/vendor/github.com/quic-go/quic-go/README.md b/vendor/github.com/quic-go/quic-go/README.md index 977bb928..e518f1e4 100644 --- a/vendor/github.com/quic-go/quic-go/README.md +++ b/vendor/github.com/quic-go/quic-go/README.md @@ -45,18 +45,19 @@ http.Client{ ## Projects using quic-go -| Project | Description | Stars | -|-----------------------------------------------------------|---------------------------------------------------------------------------------------------------------|-------| -| [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) | Free and open source, powerful network-wide ads & trackers blocking DNS server. | ![GitHub Repo stars](https://img.shields.io/github/stars/AdguardTeam/AdGuardHome?style=flat-square) | -| [algernon](https://github.com/xyproto/algernon) | Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support | ![GitHub Repo stars](https://img.shields.io/github/stars/xyproto/algernon?style=flat-square) | -| [caddy](https://github.com/caddyserver/caddy/) | Fast, multi-platform web server with automatic HTTPS | ![GitHub Repo stars](https://img.shields.io/github/stars/caddyserver/caddy?style=flat-square) | -| [cloudflared](https://github.com/cloudflare/cloudflared) | A tunneling daemon that proxies traffic from the Cloudflare network to your origins | ![GitHub Repo stars](https://img.shields.io/github/stars/cloudflare/cloudflared?style=flat-square) | -| [go-libp2p](https://github.com/libp2p/go-libp2p) | libp2p implementation in Go, powering [Kubo](https://github.com/ipfs/kubo) (IPFS) and [Lotus](https://github.com/filecoin-project/lotus) (Filecoin), among others | ![GitHub Repo stars](https://img.shields.io/github/stars/libp2p/go-libp2p?style=flat-square) | -| [OONI Probe](https://github.com/ooni/probe-cli) | Next generation OONI Probe. Library and CLI tool. | ![GitHub Repo stars](https://img.shields.io/github/stars/ooni/probe-cli?style=flat-square) | -| [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization | ![GitHub Repo stars](https://img.shields.io/github/stars/syncthing/syncthing?style=flat-square) | -| [traefik](https://github.com/traefik/traefik) | The Cloud Native Application Proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/traefik/traefik?style=flat-square) | -| [v2ray-core](https://github.com/v2fly/v2ray-core) | A platform for building proxies to bypass network restrictions | ![GitHub Repo stars](https://img.shields.io/github/stars/v2fly/v2ray-core?style=flat-square) | -| [YoMo](https://github.com/yomorun/yomo) | Streaming Serverless Framework for Geo-distributed System | ![GitHub Repo stars](https://img.shields.io/github/stars/yomorun/yomo?style=flat-square) | +| Project | Description | Stars | +|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| +| [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) | Free and open source, powerful network-wide ads & trackers blocking DNS server. | ![GitHub Repo stars](https://img.shields.io/github/stars/AdguardTeam/AdGuardHome?style=flat-square) | +| [algernon](https://github.com/xyproto/algernon) | Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support | ![GitHub Repo stars](https://img.shields.io/github/stars/xyproto/algernon?style=flat-square) | +| [caddy](https://github.com/caddyserver/caddy/) | Fast, multi-platform web server with automatic HTTPS | ![GitHub Repo stars](https://img.shields.io/github/stars/caddyserver/caddy?style=flat-square) | +| [cloudflared](https://github.com/cloudflare/cloudflared) | A tunneling daemon that proxies traffic from the Cloudflare network to your origins | ![GitHub Repo stars](https://img.shields.io/github/stars/cloudflare/cloudflared?style=flat-square) | +| [go-libp2p](https://github.com/libp2p/go-libp2p) | libp2p implementation in Go, powering [Kubo](https://github.com/ipfs/kubo) (IPFS) and [Lotus](https://github.com/filecoin-project/lotus) (Filecoin), among others | ![GitHub Repo stars](https://img.shields.io/github/stars/libp2p/go-libp2p?style=flat-square) | +| [Mercure](https://github.com/dunglas/mercure) | An open, easy, fast, reliable and battery-efficient solution for real-time communications | ![GitHub Repo stars](https://img.shields.io/github/stars/dunglas/mercure?style=flat-square) | +| [OONI Probe](https://github.com/ooni/probe-cli) | Next generation OONI Probe. Library and CLI tool. | ![GitHub Repo stars](https://img.shields.io/github/stars/ooni/probe-cli?style=flat-square) | +| [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization | ![GitHub Repo stars](https://img.shields.io/github/stars/syncthing/syncthing?style=flat-square) | +| [traefik](https://github.com/traefik/traefik) | The Cloud Native Application Proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/traefik/traefik?style=flat-square) | +| [v2ray-core](https://github.com/v2fly/v2ray-core) | A platform for building proxies to bypass network restrictions | ![GitHub Repo stars](https://img.shields.io/github/stars/v2fly/v2ray-core?style=flat-square) | +| [YoMo](https://github.com/yomorun/yomo) | Streaming Serverless Framework for Geo-distributed System | ![GitHub Repo stars](https://img.shields.io/github/stars/yomorun/yomo?style=flat-square) | ## Contributing diff --git a/vendor/github.com/quic-go/quic-go/client.go b/vendor/github.com/quic-go/quic-go/client.go index 98359c22..3d0bca3d 100644 --- a/vendor/github.com/quic-go/quic-go/client.go +++ b/vendor/github.com/quic-go/quic-go/client.go @@ -4,7 +4,6 @@ import ( "context" "crypto/tls" "errors" - "fmt" "net" "github.com/quic-go/quic-go/internal/protocol" @@ -13,20 +12,19 @@ import ( ) type client struct { - sconn sendConn - // If the client is created with DialAddr, we create a packet conn. - // If it is started with Dial, we take a packet conn as a parameter. - createdPacketConn bool + sendConn sendConn use0RTT bool packetHandlers packetHandlerManager + onClose func() tlsConf *tls.Config config *Config - srcConnID protocol.ConnectionID - destConnID protocol.ConnectionID + connIDGenerator ConnectionIDGenerator + srcConnID protocol.ConnectionID + destConnID protocol.ConnectionID initialPacketNumber protocol.PacketNumber hasNegotiatedVersion bool @@ -46,152 +44,101 @@ var generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial // DialAddr establishes a new QUIC connection to a server. // It uses a new UDP connection and closes this connection when the QUIC connection is closed. -// The hostname for SNI is taken from the given address. -func DialAddr( - addr string, - tlsConf *tls.Config, - config *Config, -) (Connection, error) { - return DialAddrContext(context.Background(), addr, tlsConf, config) -} - -// DialAddrEarly establishes a new 0-RTT QUIC connection to a server. -// It uses a new UDP connection and closes this connection when the QUIC connection is closed. -// The hostname for SNI is taken from the given address. -func DialAddrEarly( - addr string, - tlsConf *tls.Config, - config *Config, -) (EarlyConnection, error) { - return DialAddrEarlyContext(context.Background(), addr, tlsConf, config) -} - -// DialAddrEarlyContext establishes a new 0-RTT QUIC connection to a server using provided context. -// See DialAddrEarly for details -func DialAddrEarlyContext( - ctx context.Context, - addr string, - tlsConf *tls.Config, - config *Config, -) (EarlyConnection, error) { - conn, err := dialAddrContext(ctx, addr, tlsConf, config, true) - if err != nil { - return nil, err - } - utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early connection") - return conn, nil -} - -// DialAddrContext establishes a new QUIC connection to a server using the provided context. -// See DialAddr for details. -func DialAddrContext( - ctx context.Context, - addr string, - tlsConf *tls.Config, - config *Config, -) (Connection, error) { - return dialAddrContext(ctx, addr, tlsConf, config, false) -} - -func dialAddrContext( - ctx context.Context, - addr string, - tlsConf *tls.Config, - config *Config, - use0RTT bool, -) (quicConn, error) { - udpAddr, err := net.ResolveUDPAddr("udp", addr) - if err != nil { - return nil, err - } +func DialAddr(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (Connection, error) { udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { return nil, err } - return dialContext(ctx, udpConn, udpAddr, addr, tlsConf, config, use0RTT, true) + udpAddr, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return nil, err + } + dl, err := setupTransport(udpConn, tlsConf, true) + if err != nil { + return nil, err + } + return dl.Dial(ctx, udpAddr, tlsConf, conf) +} + +// DialAddrEarly establishes a new 0-RTT QUIC connection to a server. +// It uses a new UDP connection and closes this connection when the QUIC connection is closed. +func DialAddrEarly(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) { + udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) + if err != nil { + return nil, err + } + udpAddr, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return nil, err + } + dl, err := setupTransport(udpConn, tlsConf, true) + if err != nil { + return nil, err + } + conn, err := dl.DialEarly(ctx, udpAddr, tlsConf, conf) + if err != nil { + dl.Close() + return nil, err + } + return conn, nil +} + +// DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn using the provided context. +// See DialEarly for details. +func DialEarly(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) { + dl, err := setupTransport(c, tlsConf, false) + if err != nil { + return nil, err + } + conn, err := dl.DialEarly(ctx, addr, tlsConf, conf) + if err != nil { + dl.Close() + return nil, err + } + return conn, nil } // Dial establishes a new QUIC connection to a server using a net.PacketConn. If // the PacketConn satisfies the OOBCapablePacketConn interface (as a net.UDPConn // does), ECN and packet info support will be enabled. In this case, ReadMsgUDP // and WriteMsgUDP will be used instead of ReadFrom and WriteTo to read/write -// packets. The same PacketConn can be used for multiple calls to Dial and -// Listen, QUIC connection IDs are used for demultiplexing the different -// connections. The host parameter is used for SNI. The tls.Config must define -// an application protocol (using NextProtos). -func Dial( - pconn net.PacketConn, - remoteAddr net.Addr, - host string, - tlsConf *tls.Config, - config *Config, -) (Connection, error) { - return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, false, false) -} - -// DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn. -// The same PacketConn can be used for multiple calls to Dial and Listen, -// QUIC connection IDs are used for demultiplexing the different connections. -// The host parameter is used for SNI. +// packets. // The tls.Config must define an application protocol (using NextProtos). -func DialEarly( - pconn net.PacketConn, - remoteAddr net.Addr, - host string, - tlsConf *tls.Config, - config *Config, -) (EarlyConnection, error) { - return DialEarlyContext(context.Background(), pconn, remoteAddr, host, tlsConf, config) -} - -// DialEarlyContext establishes a new 0-RTT QUIC connection to a server using a net.PacketConn using the provided context. -// See DialEarly for details. -func DialEarlyContext( - ctx context.Context, - pconn net.PacketConn, - remoteAddr net.Addr, - host string, - tlsConf *tls.Config, - config *Config, -) (EarlyConnection, error) { - return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, true, false) -} - -// DialContext establishes a new QUIC connection to a server using a net.PacketConn using the provided context. -// See Dial for details. -func DialContext( - ctx context.Context, - pconn net.PacketConn, - remoteAddr net.Addr, - host string, - tlsConf *tls.Config, - config *Config, -) (Connection, error) { - return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, false, false) -} - -func dialContext( - ctx context.Context, - pconn net.PacketConn, - remoteAddr net.Addr, - host string, - tlsConf *tls.Config, - config *Config, - use0RTT bool, - createdPacketConn bool, -) (quicConn, error) { - if tlsConf == nil { - return nil, errors.New("quic: tls.Config not set") - } - if err := validateConfig(config); err != nil { - return nil, err - } - config = populateClientConfig(config, createdPacketConn) - packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDGenerator.ConnectionIDLen(), config.StatelessResetKey, config.Tracer) +func Dial(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (Connection, error) { + dl, err := setupTransport(c, tlsConf, false) if err != nil { return nil, err } - c, err := newClient(pconn, remoteAddr, config, tlsConf, host, use0RTT, createdPacketConn) + conn, err := dl.Dial(ctx, addr, tlsConf, conf) + if err != nil { + dl.Close() + return nil, err + } + return conn, nil +} + +func setupTransport(c net.PacketConn, tlsConf *tls.Config, createdPacketConn bool) (*Transport, error) { + if tlsConf == nil { + return nil, errors.New("quic: tls.Config not set") + } + return &Transport{ + Conn: c, + createdConn: createdPacketConn, + isSingleUse: true, + }, nil +} + +func dial( + ctx context.Context, + conn sendConn, + connIDGenerator ConnectionIDGenerator, + packetHandlers packetHandlerManager, + tlsConf *tls.Config, + config *Config, + onClose func(), + use0RTT bool, +) (quicConn, error) { + c, err := newClient(conn, connIDGenerator, config, tlsConf, onClose, use0RTT) if err != nil { return nil, err } @@ -199,14 +146,10 @@ func dialContext( c.tracingID = nextConnTracingID() if c.config.Tracer != nil { - c.tracer = c.config.Tracer.TracerForConnection( - context.WithValue(ctx, ConnectionTracingKey, c.tracingID), - protocol.PerspectiveClient, - c.destConnID, - ) + c.tracer = c.config.Tracer(context.WithValue(ctx, ConnectionTracingKey, c.tracingID), protocol.PerspectiveClient, c.destConnID) } if c.tracer != nil { - c.tracer.StartedConnection(c.sconn.LocalAddr(), c.sconn.RemoteAddr(), c.srcConnID, c.destConnID) + c.tracer.StartedConnection(c.sendConn.LocalAddr(), c.sendConn.RemoteAddr(), c.srcConnID, c.destConnID) } if err := c.dial(ctx); err != nil { return nil, err @@ -214,40 +157,14 @@ func dialContext( return c.conn, nil } -func newClient( - pconn net.PacketConn, - remoteAddr net.Addr, - config *Config, - tlsConf *tls.Config, - host string, - use0RTT bool, - createdPacketConn bool, -) (*client, error) { +func newClient(sendConn sendConn, connIDGenerator ConnectionIDGenerator, config *Config, tlsConf *tls.Config, onClose func(), use0RTT bool) (*client, error) { if tlsConf == nil { tlsConf = &tls.Config{} } else { tlsConf = tlsConf.Clone() } - if tlsConf.ServerName == "" { - sni, _, err := net.SplitHostPort(host) - if err != nil { - // It's ok if net.SplitHostPort returns an error - it could be a hostname/IP address without a port. - sni = host - } - tlsConf.ServerName = sni - } - - // check that all versions are actually supported - if config != nil { - for _, v := range config.Versions { - if !protocol.IsValidVersion(v) { - return nil, fmt.Errorf("%s is not a valid QUIC version", v) - } - } - } - - srcConnID, err := config.ConnectionIDGenerator.GenerateConnectionID() + srcConnID, err := connIDGenerator.GenerateConnectionID() if err != nil { return nil, err } @@ -256,28 +173,30 @@ func newClient( return nil, err } c := &client{ - srcConnID: srcConnID, - destConnID: destConnID, - sconn: newSendPconn(pconn, remoteAddr), - createdPacketConn: createdPacketConn, - use0RTT: use0RTT, - tlsConf: tlsConf, - config: config, - version: config.Versions[0], - handshakeChan: make(chan struct{}), - logger: utils.DefaultLogger.WithPrefix("client"), + connIDGenerator: connIDGenerator, + srcConnID: srcConnID, + destConnID: destConnID, + sendConn: sendConn, + use0RTT: use0RTT, + onClose: onClose, + tlsConf: tlsConf, + config: config, + version: config.Versions[0], + handshakeChan: make(chan struct{}), + logger: utils.DefaultLogger.WithPrefix("client"), } return c, nil } func (c *client) dial(ctx context.Context) error { - c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.sconn.LocalAddr(), c.sconn.RemoteAddr(), c.srcConnID, c.destConnID, c.version) + c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.sendConn.LocalAddr(), c.sendConn.RemoteAddr(), c.srcConnID, c.destConnID, c.version) c.conn = newClientConnection( - c.sconn, + c.sendConn, c.packetHandlers, c.destConnID, c.srcConnID, + c.connIDGenerator, c.config, c.tlsConf, c.initialPacketNumber, @@ -291,13 +210,18 @@ func (c *client) dial(ctx context.Context) error { c.packetHandlers.Add(c.srcConnID, c.conn) errorChan := make(chan error, 1) + recreateChan := make(chan errCloseForRecreating) go func() { - err := c.conn.run() // returns as soon as the connection is closed - - if e := (&errCloseForRecreating{}); !errors.As(err, &e) && c.createdPacketConn { - c.packetHandlers.Destroy() + err := c.conn.run() + var recreateErr *errCloseForRecreating + if errors.As(err, &recreateErr) { + recreateChan <- *recreateErr + return } - errorChan <- err + if c.onClose != nil { + c.onClose() + } + errorChan <- err // returns as soon as the connection is closed }() // only set when we're using 0-RTT @@ -312,14 +236,12 @@ func (c *client) dial(ctx context.Context) error { c.conn.shutdown() return ctx.Err() case err := <-errorChan: - var recreateErr *errCloseForRecreating - if errors.As(err, &recreateErr) { - c.initialPacketNumber = recreateErr.nextPacketNumber - c.version = recreateErr.nextVersion - c.hasNegotiatedVersion = true - return c.dial(ctx) - } return err + case recreateErr := <-recreateChan: + c.initialPacketNumber = recreateErr.nextPacketNumber + c.version = recreateErr.nextVersion + c.hasNegotiatedVersion = true + return c.dial(ctx) case <-earlyConnChan: // ready to send 0-RTT data return nil diff --git a/vendor/github.com/quic-go/quic-go/config.go b/vendor/github.com/quic-go/quic-go/config.go index 3ead9b7a..fbd6da17 100644 --- a/vendor/github.com/quic-go/quic-go/config.go +++ b/vendor/github.com/quic-go/quic-go/config.go @@ -2,6 +2,7 @@ package quic import ( "errors" + "fmt" "net" "time" @@ -29,13 +30,19 @@ func validateConfig(config *Config) error { if config.MaxIncomingUniStreams > 1<<60 { return errors.New("invalid value for Config.MaxIncomingUniStreams") } + // check that all QUIC versions are actually supported + for _, v := range config.Versions { + if !protocol.IsValidVersion(v) { + return fmt.Errorf("invalid QUIC version: %s", v) + } + } return nil } // populateServerConfig populates fields in the quic.Config with their default values, if none are set // it may be called with nil func populateServerConfig(config *Config) *Config { - config = populateConfig(config, protocol.DefaultConnectionIDLength) + config = populateConfig(config) if config.MaxTokenAge == 0 { config.MaxTokenAge = protocol.TokenValidity } @@ -48,19 +55,9 @@ func populateServerConfig(config *Config) *Config { return config } -// populateClientConfig populates fields in the quic.Config with their default values, if none are set +// populateConfig populates fields in the quic.Config with their default values, if none are set // it may be called with nil -func populateClientConfig(config *Config, createdPacketConn bool) *Config { - defaultConnIDLen := protocol.DefaultConnectionIDLength - if createdPacketConn { - defaultConnIDLen = 0 - } - - config = populateConfig(config, defaultConnIDLen) - return config -} - -func populateConfig(config *Config, defaultConnIDLen int) *Config { +func populateConfig(config *Config) *Config { if config == nil { config = &Config{} } @@ -68,10 +65,6 @@ func populateConfig(config *Config, defaultConnIDLen int) *Config { if len(versions) == 0 { versions = protocol.SupportedVersions } - conIDLen := config.ConnectionIDLength - if config.ConnectionIDLength == 0 { - conIDLen = defaultConnIDLen - } handshakeIdleTimeout := protocol.DefaultHandshakeIdleTimeout if config.HandshakeIdleTimeout != 0 { handshakeIdleTimeout = config.HandshakeIdleTimeout @@ -108,12 +101,9 @@ func populateConfig(config *Config, defaultConnIDLen int) *Config { } else if maxIncomingUniStreams < 0 { maxIncomingUniStreams = 0 } - connIDGenerator := config.ConnectionIDGenerator - if connIDGenerator == nil { - connIDGenerator = &protocol.DefaultConnectionIDGenerator{ConnLen: conIDLen} - } return &Config{ + GetConfigForClient: config.GetConfigForClient, Versions: versions, HandshakeIdleTimeout: handshakeIdleTimeout, MaxIdleTimeout: idleTimeout, @@ -128,9 +118,6 @@ func populateConfig(config *Config, defaultConnIDLen int) *Config { AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, MaxIncomingStreams: maxIncomingStreams, MaxIncomingUniStreams: maxIncomingUniStreams, - ConnectionIDLength: conIDLen, - ConnectionIDGenerator: connIDGenerator, - StatelessResetKey: config.StatelessResetKey, TokenStore: config.TokenStore, EnableDatagrams: config.EnableDatagrams, DisablePathMTUDiscovery: config.DisablePathMTUDiscovery, diff --git a/vendor/github.com/quic-go/quic-go/connection.go b/vendor/github.com/quic-go/quic-go/connection.go index eb16ece5..c4a91a35 100644 --- a/vendor/github.com/quic-go/quic-go/connection.go +++ b/vendor/github.com/quic-go/quic-go/connection.go @@ -240,6 +240,7 @@ var newConnection = func( clientDestConnID protocol.ConnectionID, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, + connIDGenerator ConnectionIDGenerator, statelessResetToken protocol.StatelessResetToken, conf *Config, tlsConf *tls.Config, @@ -283,7 +284,7 @@ var newConnection = func( runner.Retire, runner.ReplaceWithClosed, s.queueControlFrame, - s.config.ConnectionIDGenerator, + connIDGenerator, ) s.preSetup() s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), ConnectionTracingKey, tracingID)) @@ -311,9 +312,14 @@ var newConnection = func( DisableActiveMigration: true, StatelessResetToken: &statelessResetToken, OriginalDestinationConnectionID: origDestConnID, - ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, - InitialSourceConnectionID: srcConnID, - RetrySourceConnectionID: retrySrcConnID, + // For interoperability with quic-go versions before May 2023, this value must be set to a value + // different from protocol.DefaultActiveConnectionIDLimit. + // If set to the default value, it will be omitted from the transport parameters, which will make + // old quic-go versions interpret it as 0, instead of the default value of 2. + // See https://github.com/quic-go/quic-go/pull/3806. + ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, + InitialSourceConnectionID: srcConnID, + RetrySourceConnectionID: retrySrcConnID, } if s.config.EnableDatagrams { params.MaxDatagramFrameSize = protocol.MaxDatagramFrameSize @@ -323,10 +329,6 @@ var newConnection = func( if s.tracer != nil { s.tracer.SentTransportParameters(params) } - var allow0RTT func() bool - if conf.Allow0RTT != nil { - allow0RTT = func() bool { return conf.Allow0RTT(conn.RemoteAddr()) } - } cs := handshake.NewCryptoSetupServer( initialStream, handshakeStream, @@ -344,7 +346,7 @@ var newConnection = func( }, }, tlsConf, - allow0RTT, + conf.Allow0RTT, s.rttStats, tracer, logger, @@ -363,6 +365,7 @@ var newClientConnection = func( runner connRunner, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, + connIDGenerator ConnectionIDGenerator, conf *Config, tlsConf *tls.Config, initialPacketNumber protocol.PacketNumber, @@ -402,7 +405,7 @@ var newClientConnection = func( runner.Retire, runner.ReplaceWithClosed, s.queueControlFrame, - s.config.ConnectionIDGenerator, + connIDGenerator, ) s.preSetup() s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), ConnectionTracingKey, tracingID)) @@ -428,8 +431,13 @@ var newClientConnection = func( MaxAckDelay: protocol.MaxAckDelayInclGranularity, AckDelayExponent: protocol.AckDelayExponent, DisableActiveMigration: true, - ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, - InitialSourceConnectionID: srcConnID, + // For interoperability with quic-go versions before May 2023, this value must be set to a value + // different from protocol.DefaultActiveConnectionIDLimit. + // If set to the default value, it will be omitted from the transport parameters, which will make + // old quic-go versions interpret it as 0, instead of the default value of 2. + // See https://github.com/quic-go/quic-go/pull/3806. + ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, + InitialSourceConnectionID: srcConnID, } if s.config.EnableDatagrams { params.MaxDatagramFrameSize = protocol.MaxDatagramFrameSize diff --git a/vendor/github.com/quic-go/quic-go/http3/client.go b/vendor/github.com/quic-go/quic-go/http3/client.go index d89f2090..9c94a31e 100644 --- a/vendor/github.com/quic-go/quic-go/http3/client.go +++ b/vendor/github.com/quic-go/quic-go/http3/client.go @@ -33,12 +33,11 @@ const ( var defaultQuicConfig = &quic.Config{ MaxIncomingStreams: -1, // don't allow the server to create bidirectional streams KeepAlivePeriod: 10 * time.Second, - Versions: []protocol.VersionNumber{protocol.Version1}, } type dialFunc func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) -var dialAddr = quic.DialAddrEarlyContext +var dialAddr dialFunc = quic.DialAddrEarly type roundTripperOpts struct { DisableCompression bool @@ -74,9 +73,10 @@ var _ roundTripCloser = &client{} func newClient(hostname string, tlsConf *tls.Config, opts *roundTripperOpts, conf *quic.Config, dialer dialFunc) (roundTripCloser, error) { if conf == nil { conf = defaultQuicConfig.Clone() - } else if len(conf.Versions) == 0 { + } + if len(conf.Versions) == 0 { conf = conf.Clone() - conf.Versions = []quic.VersionNumber{defaultQuicConfig.Versions[0]} + conf.Versions = []quic.VersionNumber{protocol.SupportedVersions[0]} } if len(conf.Versions) != 1 { return nil, errors.New("can only use a single QUIC version for dialing a HTTP/3 connection") diff --git a/vendor/github.com/quic-go/quic-go/http3/mockgen.go b/vendor/github.com/quic-go/quic-go/http3/mockgen.go index cb370373..38939e60 100644 --- a/vendor/github.com/quic-go/quic-go/http3/mockgen.go +++ b/vendor/github.com/quic-go/quic-go/http3/mockgen.go @@ -4,3 +4,5 @@ package http3 //go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package http3 -destination mock_roundtripcloser_test.go github.com/quic-go/quic-go/http3 RoundTripCloser" type RoundTripCloser = roundTripCloser + +//go:generate sh -c "go run github.com/golang/mock/mockgen -package http3 -destination mock_quic_early_listener_test.go github.com/quic-go/quic-go/http3 QUICEarlyListener" diff --git a/vendor/github.com/quic-go/quic-go/http3/response_writer.go b/vendor/github.com/quic-go/quic-go/http3/response_writer.go index 5cc32923..b7c79d50 100644 --- a/vendor/github.com/quic-go/quic-go/http3/response_writer.go +++ b/vendor/github.com/quic-go/quic-go/http3/response_writer.go @@ -6,6 +6,7 @@ import ( "net/http" "strconv" "strings" + "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/utils" @@ -15,6 +16,7 @@ import ( type responseWriter struct { conn quic.Connection + str quic.Stream bufferedStr *bufio.Writer buf []byte @@ -36,6 +38,7 @@ func newResponseWriter(str quic.Stream, conn quic.Connection, logger utils.Logge header: http.Header{}, buf: make([]byte, 16), conn: conn, + str: str, bufferedStr: bufio.NewWriter(str), logger: logger, } @@ -121,6 +124,14 @@ func (w *responseWriter) StreamCreator() StreamCreator { return w.conn } +func (w *responseWriter) SetReadDeadline(deadline time.Time) error { + return w.str.SetReadDeadline(deadline) +} + +func (w *responseWriter) SetWriteDeadline(deadline time.Time) error { + return w.str.SetWriteDeadline(deadline) +} + // copied from http2/http2.go // bodyAllowedForStatus reports whether a given response status code // permits a body. See RFC 2616, section 4.4. diff --git a/vendor/github.com/quic-go/quic-go/http3/roundtrip.go b/vendor/github.com/quic-go/quic-go/http3/roundtrip.go index 95506cac..eef93c28 100644 --- a/vendor/github.com/quic-go/quic-go/http3/roundtrip.go +++ b/vendor/github.com/quic-go/quic-go/http3/roundtrip.go @@ -10,6 +10,7 @@ import ( "net/http" "strings" "sync" + "sync/atomic" "golang.org/x/net/http/httpguts" @@ -17,7 +18,7 @@ import ( ) // declare this as a variable, such that we can it mock it in the tests -var quicDialer = quic.DialEarlyContext +var quicDialer = quic.DialEarly type roundTripCloser interface { RoundTripOpt(*http.Request, RoundTripOpt) (*http.Response, error) @@ -25,6 +26,11 @@ type roundTripCloser interface { io.Closer } +type roundTripCloserWithCount struct { + roundTripCloser + useCount atomic.Int64 +} + // RoundTripper implements the http.RoundTripper interface type RoundTripper struct { mutex sync.Mutex @@ -82,7 +88,7 @@ type RoundTripper struct { MaxResponseHeaderBytes int64 newClient func(hostname string, tlsConf *tls.Config, opts *roundTripperOpts, conf *quic.Config, dialer dialFunc) (roundTripCloser, error) // so we can mock it in tests - clients map[string]roundTripCloser + clients map[string]*roundTripCloserWithCount udpConn *net.UDPConn } @@ -143,6 +149,7 @@ func (r *RoundTripper) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http. if err != nil { return nil, err } + defer cl.useCount.Add(-1) rsp, err := cl.RoundTripOpt(req, opt) if err != nil { r.removeClient(hostname) @@ -160,12 +167,12 @@ func (r *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return r.RoundTripOpt(req, RoundTripOpt{}) } -func (r *RoundTripper) getClient(hostname string, onlyCached bool) (rtc roundTripCloser, isReused bool, err error) { +func (r *RoundTripper) getClient(hostname string, onlyCached bool) (rtc *roundTripCloserWithCount, isReused bool, err error) { r.mutex.Lock() defer r.mutex.Unlock() if r.clients == nil { - r.clients = make(map[string]roundTripCloser) + r.clients = make(map[string]*roundTripCloserWithCount) } client, ok := r.clients[hostname] @@ -188,7 +195,7 @@ func (r *RoundTripper) getClient(hostname string, onlyCached bool) (rtc roundTri } dial = r.makeDialer() } - client, err = newCl( + c, err := newCl( hostname, r.TLSClientConfig, &roundTripperOpts{ @@ -204,10 +211,12 @@ func (r *RoundTripper) getClient(hostname string, onlyCached bool) (rtc roundTri if err != nil { return nil, false, err } + client = &roundTripCloserWithCount{roundTripCloser: c} r.clients[hostname] = client } else if client.HandshakeComplete() { isReused = true } + client.useCount.Add(1) return client, isReused, nil } @@ -273,6 +282,17 @@ func (r *RoundTripper) makeDialer() func(ctx context.Context, addr string, tlsCf if err != nil { return nil, err } - return quicDialer(ctx, r.udpConn, udpAddr, addr, tlsCfg, cfg) + return quicDialer(ctx, r.udpConn, udpAddr, tlsCfg, cfg) + } +} + +func (r *RoundTripper) CloseIdleConnections() { + r.mutex.Lock() + defer r.mutex.Unlock() + for hostname, client := range r.clients { + if client.useCount.Load() == 0 { + client.Close() + delete(r.clients, hostname) + } } } diff --git a/vendor/github.com/quic-go/quic-go/http3/server.go b/vendor/github.com/quic-go/quic-go/http3/server.go index e74247ab..b0d8e4a4 100644 --- a/vendor/github.com/quic-go/quic-go/http3/server.go +++ b/vendor/github.com/quic-go/quic-go/http3/server.go @@ -23,8 +23,12 @@ import ( // allows mocking of quic.Listen and quic.ListenAddr var ( - quicListen = quic.ListenEarly - quicListenAddr = quic.ListenAddrEarly + quicListen = func(conn net.PacketConn, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { + return quic.ListenEarly(conn, tlsConf, config) + } + quicListenAddr = func(addr string, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { + return quic.ListenAddrEarly(addr, tlsConf, config) + } ) const ( @@ -44,6 +48,15 @@ const ( streamTypeQPACKDecoderStream = 3 ) +// A QUICEarlyListener listens for incoming QUIC connections. +type QUICEarlyListener interface { + Accept(context.Context) (quic.EarlyConnection, error) + Addr() net.Addr + io.Closer +} + +var _ QUICEarlyListener = &quic.EarlyListener{} + func versionToALPN(v protocol.VersionNumber) string { //nolint:exhaustive // These are all the versions we care about. switch v { @@ -193,7 +206,7 @@ type Server struct { UniStreamHijacker func(StreamType, quic.Connection, quic.ReceiveStream, error) (hijacked bool) mutex sync.RWMutex - listeners map[*quic.EarlyListener]listenerInfo + listeners map[*QUICEarlyListener]listenerInfo closed bool @@ -249,7 +262,7 @@ func (s *Server) ServeQUICConn(conn quic.Connection) error { // Make sure you use http3.ConfigureTLSConfig to configure a tls.Config // and use it to construct a http3-friendly QUIC listener. // Closing the server does close the listener. -func (s *Server) ServeListener(ln quic.EarlyListener) error { +func (s *Server) ServeListener(ln QUICEarlyListener) error { if err := s.addListener(&ln); err != nil { return err } @@ -275,7 +288,7 @@ func (s *Server) serveConn(tlsConf *tls.Config, conn net.PacketConn) error { baseConf := ConfigureTLSConfig(tlsConf) quicConf := s.QuicConfig if quicConf == nil { - quicConf = &quic.Config{Allow0RTT: func(net.Addr) bool { return true }} + quicConf = &quic.Config{Allow0RTT: true} } else { quicConf = s.QuicConfig.Clone() } @@ -283,7 +296,7 @@ func (s *Server) serveConn(tlsConf *tls.Config, conn net.PacketConn) error { quicConf.EnableDatagrams = true } - var ln quic.EarlyListener + var ln QUICEarlyListener var err error if conn == nil { addr := s.Addr @@ -305,7 +318,7 @@ func (s *Server) serveConn(tlsConf *tls.Config, conn net.PacketConn) error { return err } -func (s *Server) serveListener(ln quic.EarlyListener) error { +func (s *Server) serveListener(ln QUICEarlyListener) error { for { conn, err := ln.Accept(context.Background()) if err != nil { @@ -391,7 +404,7 @@ func (s *Server) generateAltSvcHeader() { // We store a pointer to interface in the map set. This is safe because we only // call trackListener via Serve and can track+defer untrack the same pointer to // local variable there. We never need to compare a Listener from another caller. -func (s *Server) addListener(l *quic.EarlyListener) error { +func (s *Server) addListener(l *QUICEarlyListener) error { s.mutex.Lock() defer s.mutex.Unlock() @@ -402,25 +415,24 @@ func (s *Server) addListener(l *quic.EarlyListener) error { s.logger = utils.DefaultLogger.WithPrefix("server") } if s.listeners == nil { - s.listeners = make(map[*quic.EarlyListener]listenerInfo) + s.listeners = make(map[*QUICEarlyListener]listenerInfo) } if port, err := extractPort((*l).Addr().String()); err == nil { s.listeners[l] = listenerInfo{port} } else { - s.logger.Errorf( - "Unable to extract port from listener %+v, will not be announced using SetQuicHeaders: %s", err) + s.logger.Errorf("Unable to extract port from listener %+v, will not be announced using SetQuicHeaders: %s", err) s.listeners[l] = listenerInfo{} } s.generateAltSvcHeader() return nil } -func (s *Server) removeListener(l *quic.EarlyListener) { +func (s *Server) removeListener(l *QUICEarlyListener) { s.mutex.Lock() + defer s.mutex.Unlock() delete(s.listeners, l) s.generateAltSvcHeader() - s.mutex.Unlock() } func (s *Server) handleConn(conn quic.Connection) error { diff --git a/vendor/github.com/quic-go/quic-go/interface.go b/vendor/github.com/quic-go/quic-go/interface.go index b700e7c1..cdab015b 100644 --- a/vendor/github.com/quic-go/quic-go/interface.go +++ b/vendor/github.com/quic-go/quic-go/interface.go @@ -198,7 +198,7 @@ type EarlyConnection interface { // HandshakeComplete blocks until the handshake completes (or fails). // For the client, data sent before completion of the handshake is encrypted with 0-RTT keys. - // For the serfer, data sent before completion of the handshake is encrypted with 1-RTT keys, + // For the server, data sent before completion of the handshake is encrypted with 1-RTT keys, // however the client's identity is only verified once the handshake completes. HandshakeComplete() <-chan struct{} @@ -239,21 +239,12 @@ type ConnectionIDGenerator interface { // Config contains all configuration data needed for a QUIC server or client. type Config struct { + // GetConfigForClient is called for incoming connections. + // If the error is not nil, the connection attempt is refused. + GetConfigForClient func(info *ClientHelloInfo) (*Config, error) // The QUIC versions that can be negotiated. // If not set, it uses all versions available. Versions []VersionNumber - // The length of the connection ID in bytes. - // It can be 0, or any value between 4 and 18. - // If not set, the interpretation depends on where the Config is used: - // If used for dialing an address, a 0 byte connection ID will be used. - // If used for a server, or dialing on a packet conn, a 4 byte connection ID will be used. - // When dialing on a packet conn, the ConnectionIDLength value must be the same for every Dial call. - ConnectionIDLength int - // An optional ConnectionIDGenerator to be used for ConnectionIDs generated during the lifecycle of a QUIC connection. - // The goal is to give some control on how connection IDs, which can be useful in some scenarios, in particular for servers. - // By default, if not provided, random connection IDs with the length given by ConnectionIDLength is used. - // Otherwise, if one is provided, then ConnectionIDLength is ignored. - ConnectionIDGenerator ConnectionIDGenerator // HandshakeIdleTimeout is the idle timeout before completion of the handshake. // Specifically, if we don't receive any packet from the peer within this time, the connection attempt is aborted. // If this value is zero, the timeout is set to 5 seconds. @@ -314,9 +305,6 @@ type Config struct { // If not set, it will default to 100. // If set to a negative value, it doesn't allow any unidirectional streams. MaxIncomingUniStreams int64 - // The StatelessResetKey is used to generate stateless reset tokens. - // If no key is configured, sending of stateless resets is disabled. - StatelessResetKey *StatelessResetKey // KeepAlivePeriod defines whether this peer will periodically send a packet to keep the connection alive. // If set to 0, then no keep alive is sent. Otherwise, the keep alive is sent on that period (or at most // every half of MaxIdleTimeout, whichever is smaller). @@ -330,13 +318,15 @@ type Config struct { // It has no effect for a client. DisableVersionNegotiationPackets bool // Allow0RTT allows the application to decide if a 0-RTT connection attempt should be accepted. - // When set, 0-RTT is enabled. When not set, 0-RTT is disabled. // Only valid for the server. - // Warning: This API should not be considered stable and might change soon. - Allow0RTT func(net.Addr) bool + Allow0RTT bool // Enable QUIC datagram support (RFC 9221). EnableDatagrams bool - Tracer logging.Tracer + Tracer func(context.Context, logging.Perspective, ConnectionID) logging.ConnectionTracer +} + +type ClientHelloInfo struct { + RemoteAddr net.Addr } // ConnectionState records basic details about a QUIC connection @@ -345,24 +335,3 @@ type ConnectionState struct { SupportsDatagrams bool Version VersionNumber } - -// A Listener for incoming QUIC connections -type Listener interface { - // Close the server. All active connections will be closed. - Close() error - // Addr returns the local network addr that the server is listening on. - Addr() net.Addr - // Accept returns new connections. It should be called in a loop. - Accept(context.Context) (Connection, error) -} - -// An EarlyListener listens for incoming QUIC connections, -// and returns them before the handshake completes. -type EarlyListener interface { - // Close the server. All active connections will be closed. - Close() error - // Addr returns the local network addr that the server is listening on. - Addr() net.Addr - // Accept returns new early connections. It should be called in a loop. - Accept(context.Context) (EarlyConnection, error) -} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go index 394ee40a..849eea69 100644 --- a/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go @@ -9,12 +9,12 @@ import ( // A Packet is a packet type Packet struct { + SendTime time.Time PacketNumber protocol.PacketNumber Frames []*Frame LargestAcked protocol.PacketNumber // InvalidPacketNumber if the packet doesn't contain an ACK Length protocol.ByteCount EncryptionLevel protocol.EncryptionLevel - SendTime time.Time IsPathMTUProbePacket bool // We don't report the loss of Path MTU probe packets to the congestion controller. diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go b/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go index a5861062..09ea2680 100644 --- a/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go +++ b/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go @@ -12,16 +12,16 @@ const maxBurstSizePackets = 10 // The pacer implements a token bucket pacing algorithm. type pacer struct { - budgetAtLastSent protocol.ByteCount - maxDatagramSize protocol.ByteCount - lastSentTime time.Time - getAdjustedBandwidth func() uint64 // in bytes/s + budgetAtLastSent protocol.ByteCount + maxDatagramSize protocol.ByteCount + lastSentTime time.Time + adjustedBandwidth func() uint64 // in bytes/s } func newPacer(getBandwidth func() Bandwidth) *pacer { p := &pacer{ maxDatagramSize: initialMaxDatagramSize, - getAdjustedBandwidth: func() uint64 { + adjustedBandwidth: func() uint64 { // Bandwidth is in bits/s. We need the value in bytes/s. bw := uint64(getBandwidth() / BytesPerSecond) // Use a slightly higher value than the actual measured bandwidth. @@ -49,13 +49,16 @@ func (p *pacer) Budget(now time.Time) protocol.ByteCount { if p.lastSentTime.IsZero() { return p.maxBurstSize() } - budget := p.budgetAtLastSent + (protocol.ByteCount(p.getAdjustedBandwidth())*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 + budget := p.budgetAtLastSent + (protocol.ByteCount(p.adjustedBandwidth())*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 + if budget < 0 { // protect against overflows + budget = protocol.MaxByteCount + } return utils.Min(p.maxBurstSize(), budget) } func (p *pacer) maxBurstSize() protocol.ByteCount { return utils.Max( - protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.getAdjustedBandwidth())/1e9, + protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.adjustedBandwidth())/1e9, maxBurstSizePackets*p.maxDatagramSize, ) } @@ -68,7 +71,7 @@ func (p *pacer) TimeUntilSend() time.Time { } return p.lastSentTime.Add(utils.Max( protocol.MinPacingDelay, - time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond, + time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.adjustedBandwidth())))*time.Nanosecond, )) } diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go b/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go index 0420a5f9..8c9c2a8f 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go @@ -116,7 +116,7 @@ type cryptoSetup struct { clientHelloWritten bool clientHelloWrittenChan chan struct{} // is closed as soon as the ClientHello is written zeroRTTParametersChan chan<- *wire.TransportParameters - allow0RTT func() bool + allow0RTT bool rttStats *utils.RTTStats @@ -197,7 +197,7 @@ func NewCryptoSetupServer( tp *wire.TransportParameters, runner handshakeRunner, tlsConf *tls.Config, - allow0RTT func() bool, + allow0RTT bool, rttStats *utils.RTTStats, tracer logging.ConnectionTracer, logger utils.Logger, @@ -210,14 +210,13 @@ func NewCryptoSetupServer( tp, runner, tlsConf, - allow0RTT != nil, + allow0RTT, rttStats, tracer, logger, protocol.PerspectiveServer, version, ) - cs.allow0RTT = allow0RTT cs.conn = qtls.Server(newConn(localAddr, remoteAddr), cs.tlsConf, cs.extraConf) return cs } @@ -253,6 +252,7 @@ func newCryptoSetup( readEncLevel: protocol.EncryptionInitial, writeEncLevel: protocol.EncryptionInitial, runner: runner, + allow0RTT: enable0RTT, ourParams: tp, paramsChan: extHandler.TransportParameters(), rttStats: rttStats, @@ -503,7 +503,7 @@ func (h *cryptoSetup) accept0RTT(sessionTicketData []byte) bool { h.logger.Debugf("Transport parameters changed. Rejecting 0-RTT.") return false } - if !h.allow0RTT() { + if !h.allow0RTT { h.logger.Debugf("0-RTT not allowed. Rejecting 0-RTT.") return false } diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/params.go b/vendor/github.com/quic-go/quic-go/internal/protocol/params.go index 60c86779..fe3a7562 100644 --- a/vendor/github.com/quic-go/quic-go/internal/protocol/params.go +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/params.go @@ -5,6 +5,9 @@ import "time" // DesiredReceiveBufferSize is the kernel UDP receive buffer size that we'd like to use. const DesiredReceiveBufferSize = (1 << 20) * 2 // 2 MB +// DesiredSendBufferSize is the kernel UDP send buffer size that we'd like to use. +const DesiredSendBufferSize = (1 << 20) * 2 // 2 MB + // InitialPacketSizeIPv4 is the maximum packet size that we use for sending IPv4 packets. const InitialPacketSizeIPv4 = 1252 diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go b/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go index 8241e274..93ad96cc 100644 --- a/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go @@ -77,6 +77,9 @@ const MinConnectionIDLenInitial = 8 // DefaultAckDelayExponent is the default ack delay exponent const DefaultAckDelayExponent = 3 +// DefaultActiveConnectionIDLimit is the default active connection ID limit +const DefaultActiveConnectionIDLimit = 2 + // MaxAckDelayExponent is the maximum ack delay exponent const MaxAckDelayExponent = 20 diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go b/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go index 527539e1..2cd9a191 100644 --- a/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go +++ b/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go @@ -103,8 +103,12 @@ func (r *RTTStats) SetMaxAckDelay(mad time.Duration) { // SetInitialRTT sets the initial RTT. // It is used during the 0-RTT handshake when restoring the RTT stats from the session state. func (r *RTTStats) SetInitialRTT(t time.Duration) { + // On the server side, by the time we get to process the session ticket, + // we might already have obtained an RTT measurement. + // This can happen if we received the ClientHello in multiple pieces, and one of those pieces was lost. + // Discard the restored value. A fresh measurement is always better. if r.hasMeasurement { - panic("initial RTT set after first measurement") + return } r.smoothedRTT = t r.latestRTT = t diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go index f145c8b4..2f816c78 100644 --- a/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go +++ b/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go @@ -90,13 +90,22 @@ func parseAckFrame(r *bytes.Reader, typ uint64, ackDelayExponent uint8, _ protoc return nil, errInvalidAckRanges } - // parse (and skip) the ECN section if ecn { - for i := 0; i < 3; i++ { - if _, err := quicvarint.Read(r); err != nil { - return nil, err - } + ect0, err := quicvarint.Read(r) + if err != nil { + return nil, err } + frame.ECT0 = ect0 + ect1, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + frame.ECT1 = ect1 + ecnce, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + frame.ECNCE = ecnce } return frame, nil diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/header.go b/vendor/github.com/quic-go/quic-go/internal/wire/header.go index 6e8d4f9f..37f48cce 100644 --- a/vendor/github.com/quic-go/quic-go/internal/wire/header.go +++ b/vendor/github.com/quic-go/quic-go/internal/wire/header.go @@ -13,8 +13,6 @@ import ( ) // ParseConnectionID parses the destination connection ID of a packet. -// It uses the data slice for the connection ID. -// That means that the connection ID must not be used after the packet buffer is released. func ParseConnectionID(data []byte, shortHeaderConnIDLen int) (protocol.ConnectionID, error) { if len(data) == 0 { return protocol.ConnectionID{}, io.EOF diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go b/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go index 8eb4cf46..dc1f2b95 100644 --- a/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go +++ b/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go @@ -118,6 +118,7 @@ func (p *TransportParameters) unmarshal(r *bytes.Reader, sentBy protocol.Perspec var ( readOriginalDestinationConnectionID bool readInitialSourceConnectionID bool + readActiveConnectionIDLimit bool ) p.AckDelayExponent = protocol.DefaultAckDelayExponent @@ -139,6 +140,9 @@ func (p *TransportParameters) unmarshal(r *bytes.Reader, sentBy protocol.Perspec } parameterIDs = append(parameterIDs, paramID) switch paramID { + case activeConnectionIDLimitParameterID: + readActiveConnectionIDLimit = true + fallthrough case maxIdleTimeoutParameterID, maxUDPPayloadSizeParameterID, initialMaxDataParameterID, @@ -148,7 +152,6 @@ func (p *TransportParameters) unmarshal(r *bytes.Reader, sentBy protocol.Perspec initialMaxStreamsBidiParameterID, initialMaxStreamsUniParameterID, maxAckDelayParameterID, - activeConnectionIDLimitParameterID, maxDatagramFrameSizeParameterID, ackDelayExponentParameterID: if err := p.readNumericTransportParameter(r, paramID, int(paramLen)); err != nil { @@ -196,6 +199,9 @@ func (p *TransportParameters) unmarshal(r *bytes.Reader, sentBy protocol.Perspec } } + if !readActiveConnectionIDLimit { + p.ActiveConnectionIDLimit = protocol.DefaultActiveConnectionIDLimit + } if !fromSessionTicket { if sentBy == protocol.PerspectiveServer && !readOriginalDestinationConnectionID { return errors.New("missing original_destination_connection_id") @@ -402,7 +408,9 @@ func (p *TransportParameters) Marshal(pers protocol.Perspective) []byte { } } // active_connection_id_limit - b = p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) + if p.ActiveConnectionIDLimit != protocol.DefaultActiveConnectionIDLimit { + b = p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) + } // initial_source_connection_id b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID)) b = quicvarint.Append(b, uint64(p.InitialSourceConnectionID.Len())) diff --git a/vendor/github.com/quic-go/quic-go/logging/interface.go b/vendor/github.com/quic-go/quic-go/logging/interface.go index efcef151..2ce8582e 100644 --- a/vendor/github.com/quic-go/quic-go/logging/interface.go +++ b/vendor/github.com/quic-go/quic-go/logging/interface.go @@ -3,7 +3,6 @@ package logging import ( - "context" "net" "time" @@ -101,12 +100,6 @@ type ShortHeader struct { // A Tracer traces events. type Tracer interface { - // TracerForConnection requests a new tracer for a connection. - // The ODCID is the original destination connection ID: - // The destination connection ID that the client used on the first Initial packet it sent on this connection. - // If nil is returned, tracing will be disabled for this connection. - TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer - SentPacket(net.Addr, *Header, ByteCount, []Frame) SentVersionNegotiationPacket(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []VersionNumber) DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason) diff --git a/vendor/github.com/quic-go/quic-go/logging/multiplex.go b/vendor/github.com/quic-go/quic-go/logging/multiplex.go index 8e85db49..672a5cdb 100644 --- a/vendor/github.com/quic-go/quic-go/logging/multiplex.go +++ b/vendor/github.com/quic-go/quic-go/logging/multiplex.go @@ -1,7 +1,6 @@ package logging import ( - "context" "net" "time" ) @@ -23,16 +22,6 @@ func NewMultiplexedTracer(tracers ...Tracer) Tracer { return &tracerMultiplexer{tracers} } -func (m *tracerMultiplexer) TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer { - var connTracers []ConnectionTracer - for _, t := range m.tracers { - if ct := t.TracerForConnection(ctx, p, odcid); ct != nil { - connTracers = append(connTracers, ct) - } - } - return NewMultiplexedConnectionTracer(connTracers...) -} - func (m *tracerMultiplexer) SentPacket(remote net.Addr, hdr *Header, size ByteCount, frames []Frame) { for _, t := range m.tracers { t.SentPacket(remote, hdr, size, frames) diff --git a/vendor/github.com/quic-go/quic-go/logging/null_tracer.go b/vendor/github.com/quic-go/quic-go/logging/null_tracer.go index 38052ae3..de970385 100644 --- a/vendor/github.com/quic-go/quic-go/logging/null_tracer.go +++ b/vendor/github.com/quic-go/quic-go/logging/null_tracer.go @@ -1,7 +1,6 @@ package logging import ( - "context" "net" "time" ) @@ -12,9 +11,6 @@ type NullTracer struct{} var _ Tracer = &NullTracer{} -func (n NullTracer) TracerForConnection(context.Context, Perspective, ConnectionID) ConnectionTracer { - return NullConnectionTracer{} -} func (n NullTracer) SentPacket(net.Addr, *Header, ByteCount, []Frame) {} func (n NullTracer) SentVersionNegotiationPacket(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []VersionNumber) { } diff --git a/vendor/github.com/quic-go/quic-go/mockgen.go b/vendor/github.com/quic-go/quic-go/mockgen.go index 443e9c10..eb700864 100644 --- a/vendor/github.com/quic-go/quic-go/mockgen.go +++ b/vendor/github.com/quic-go/quic-go/mockgen.go @@ -65,9 +65,6 @@ type UnknownPacketHandler = unknownPacketHandler //go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager" type PacketHandlerManager = packetHandlerManager -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_multiplexer_test.go github.com/quic-go/quic-go Multiplexer" -type Multiplexer = multiplexer - // Need to use source mode for the batchConn, since reflect mode follows type aliases. // See https://github.com/golang/mock/issues/244 for details. // diff --git a/vendor/github.com/quic-go/quic-go/multiplexer.go b/vendor/github.com/quic-go/quic-go/multiplexer.go index 37d4e75c..85f7f403 100644 --- a/vendor/github.com/quic-go/quic-go/multiplexer.go +++ b/vendor/github.com/quic-go/quic-go/multiplexer.go @@ -6,7 +6,6 @@ import ( "sync" "github.com/quic-go/quic-go/internal/utils" - "github.com/quic-go/quic-go/logging" ) var ( @@ -14,30 +13,19 @@ var ( connMuxer multiplexer ) -type indexableConn interface { - LocalAddr() net.Addr -} +type indexableConn interface{ LocalAddr() net.Addr } type multiplexer interface { - AddConn(c net.PacketConn, connIDLen int, statelessResetKey *StatelessResetKey, tracer logging.Tracer) (packetHandlerManager, error) + AddConn(conn indexableConn) RemoveConn(indexableConn) error } -type connManager struct { - connIDLen int - statelessResetKey *StatelessResetKey - tracer logging.Tracer - manager packetHandlerManager -} - // The connMultiplexer listens on multiple net.PacketConns and dispatches // incoming packets to the connection handler. type connMultiplexer struct { mutex sync.Mutex - conns map[string] /* LocalAddr().String() */ connManager - newPacketHandlerManager func(net.PacketConn, int, *StatelessResetKey, logging.Tracer, utils.Logger) (packetHandlerManager, error) // so it can be replaced in the tests - + conns map[string] /* LocalAddr().String() */ indexableConn logger utils.Logger } @@ -46,57 +34,38 @@ var _ multiplexer = &connMultiplexer{} func getMultiplexer() multiplexer { connMuxerOnce.Do(func() { connMuxer = &connMultiplexer{ - conns: make(map[string]connManager), - logger: utils.DefaultLogger.WithPrefix("muxer"), - newPacketHandlerManager: newPacketHandlerMap, + conns: make(map[string]indexableConn), + logger: utils.DefaultLogger.WithPrefix("muxer"), } }) return connMuxer } -func (m *connMultiplexer) AddConn( - c net.PacketConn, - connIDLen int, - statelessResetKey *StatelessResetKey, - tracer logging.Tracer, -) (packetHandlerManager, error) { +func (m *connMultiplexer) index(addr net.Addr) string { + return addr.Network() + " " + addr.String() +} + +func (m *connMultiplexer) AddConn(c indexableConn) { m.mutex.Lock() defer m.mutex.Unlock() - addr := c.LocalAddr() - connIndex := addr.Network() + " " + addr.String() + connIndex := m.index(c.LocalAddr()) p, ok := m.conns[connIndex] - if !ok { - manager, err := m.newPacketHandlerManager(c, connIDLen, statelessResetKey, tracer, m.logger) - if err != nil { - return nil, err - } - p = connManager{ - connIDLen: connIDLen, - statelessResetKey: statelessResetKey, - manager: manager, - tracer: tracer, - } - m.conns[connIndex] = p - } else { - if p.connIDLen != connIDLen { - return nil, fmt.Errorf("cannot use %d byte connection IDs on a connection that is already using %d byte connction IDs", connIDLen, p.connIDLen) - } - if statelessResetKey != nil && p.statelessResetKey != statelessResetKey { - return nil, fmt.Errorf("cannot use different stateless reset keys on the same packet conn") - } - if tracer != p.tracer { - return nil, fmt.Errorf("cannot use different tracers on the same packet conn") - } + if ok { + // Panics if we're already listening on this connection. + // This is a safeguard because we're introducing a breaking API change, see + // https://github.com/quic-go/quic-go/issues/3727 for details. + // We'll remove this at a later time, when most users of the library have made the switch. + panic("connection already exists") // TODO: write a nice message } - return p.manager, nil + m.conns[connIndex] = p } func (m *connMultiplexer) RemoveConn(c indexableConn) error { m.mutex.Lock() defer m.mutex.Unlock() - connIndex := c.LocalAddr().Network() + " " + c.LocalAddr().String() + connIndex := m.index(c.LocalAddr()) if _, ok := m.conns[connIndex]; !ok { return fmt.Errorf("cannote remove connection, connection is unknown") } diff --git a/vendor/github.com/quic-go/quic-go/packet_handler_map.go b/vendor/github.com/quic-go/quic-go/packet_handler_map.go index 1f643412..83caa192 100644 --- a/vendor/github.com/quic-go/quic-go/packet_handler_map.go +++ b/vendor/github.com/quic-go/quic-go/packet_handler_map.go @@ -5,28 +5,22 @@ import ( "crypto/rand" "crypto/sha256" "errors" - "fmt" "hash" "io" - "log" "net" - "os" - "strconv" - "strings" "sync" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" - "github.com/quic-go/quic-go/internal/wire" - "github.com/quic-go/quic-go/logging" ) -// rawConn is a connection that allow reading of a receivedPacket. +// rawConn is a connection that allow reading of a receivedPackeh. type rawConn interface { ReadPacket() (*receivedPacket, error) WritePacket(b []byte, addr net.Addr, oob []byte) (int, error) LocalAddr() net.Addr + SetReadDeadline(time.Time) error io.Closer } @@ -36,116 +30,49 @@ type closePacket struct { info *packetInfo } -// The packetHandlerMap stores packetHandlers, identified by connection ID. -// It is used: -// * by the server to store connections -// * when multiplexing outgoing connections to store clients +type unknownPacketHandler interface { + handlePacket(*receivedPacket) + setCloseError(error) +} + +var errListenerAlreadySet = errors.New("listener already set") + type packetHandlerMap struct { - mutex sync.Mutex + mutex sync.Mutex + handlers map[protocol.ConnectionID]packetHandler + resetTokens map[protocol.StatelessResetToken] /* stateless reset token */ packetHandler - conn rawConn - connIDLen int - - closeQueue chan closePacket - - handlers map[protocol.ConnectionID]packetHandler - resetTokens map[protocol.StatelessResetToken] /* stateless reset token */ packetHandler - server unknownPacketHandler - numZeroRTTEntries int - - listening chan struct{} // is closed when listen returns closed bool + closeChan chan struct{} + + enqueueClosePacket func(closePacket) deleteRetiredConnsAfter time.Duration - zeroRTTQueueDuration time.Duration - statelessResetEnabled bool - statelessResetMutex sync.Mutex - statelessResetHasher hash.Hash + statelessResetMutex sync.Mutex + statelessResetHasher hash.Hash - tracer logging.Tracer logger utils.Logger } var _ packetHandlerManager = &packetHandlerMap{} -func setReceiveBuffer(c net.PacketConn, logger utils.Logger) error { - conn, ok := c.(interface{ SetReadBuffer(int) error }) - if !ok { - return errors.New("connection doesn't allow setting of receive buffer size. Not a *net.UDPConn?") - } - size, err := inspectReadBuffer(c) - if err != nil { - return fmt.Errorf("failed to determine receive buffer size: %w", err) - } - if size >= protocol.DesiredReceiveBufferSize { - logger.Debugf("Conn has receive buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024) - return nil - } - if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil { - return fmt.Errorf("failed to increase receive buffer size: %w", err) - } - newSize, err := inspectReadBuffer(c) - if err != nil { - return fmt.Errorf("failed to determine receive buffer size: %w", err) - } - if newSize == size { - return fmt.Errorf("failed to increase receive buffer size (wanted: %d kiB, got %d kiB)", protocol.DesiredReceiveBufferSize/1024, newSize/1024) - } - if newSize < protocol.DesiredReceiveBufferSize { - return fmt.Errorf("failed to sufficiently increase receive buffer size (was: %d kiB, wanted: %d kiB, got: %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024, newSize/1024) - } - logger.Debugf("Increased receive buffer size to %d kiB", newSize/1024) - return nil -} - -// only print warnings about the UDP receive buffer size once -var receiveBufferWarningOnce sync.Once - -func newPacketHandlerMap( - c net.PacketConn, - connIDLen int, - statelessResetKey *StatelessResetKey, - tracer logging.Tracer, - logger utils.Logger, -) (packetHandlerManager, error) { - if err := setReceiveBuffer(c, logger); err != nil { - if !strings.Contains(err.Error(), "use of closed network connection") { - receiveBufferWarningOnce.Do(func() { - if disable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING")); disable { - return - } - log.Printf("%s. See https://github.com/quic-go/quic-go/wiki/UDP-Receive-Buffer-Size for details.", err) - }) - } - } - conn, err := wrapConn(c) - if err != nil { - return nil, err - } - m := &packetHandlerMap{ - conn: conn, - connIDLen: connIDLen, - listening: make(chan struct{}), +func newPacketHandlerMap(key *StatelessResetKey, enqueueClosePacket func(closePacket), logger utils.Logger) *packetHandlerMap { + h := &packetHandlerMap{ + closeChan: make(chan struct{}), handlers: make(map[protocol.ConnectionID]packetHandler), resetTokens: make(map[protocol.StatelessResetToken]packetHandler), deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout, - zeroRTTQueueDuration: protocol.Max0RTTQueueingDuration, - closeQueue: make(chan closePacket, 4), - statelessResetEnabled: statelessResetKey != nil, - tracer: tracer, + enqueueClosePacket: enqueueClosePacket, logger: logger, } - if m.statelessResetEnabled { - m.statelessResetHasher = hmac.New(sha256.New, statelessResetKey[:]) + if key != nil { + h.statelessResetHasher = hmac.New(sha256.New, key[:]) } - go m.listen() - go m.runCloseQueue() - - if logger.Debug() { - go m.logUsage() + if h.logger.Debug() { + go h.logUsage() } - return m, nil + return h } func (h *packetHandlerMap) logUsage() { @@ -153,7 +80,7 @@ func (h *packetHandlerMap) logUsage() { var printedZero bool for { select { - case <-h.listening: + case <-h.closeChan: return case <-ticker.C: } @@ -174,6 +101,14 @@ func (h *packetHandlerMap) logUsage() { } } +func (h *packetHandlerMap) Get(id protocol.ConnectionID) (packetHandler, bool) { + h.mutex.Lock() + defer h.mutex.Unlock() + + handler, ok := h.handlers[id] + return handler, ok +} + func (h *packetHandlerMap) Add(id protocol.ConnectionID, handler packetHandler) bool /* was added */ { h.mutex.Lock() defer h.mutex.Unlock() @@ -187,26 +122,17 @@ func (h *packetHandlerMap) Add(id protocol.ConnectionID, handler packetHandler) return true } -func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.ConnectionID, fn func() packetHandler) bool { +func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.ConnectionID, fn func() (packetHandler, bool)) bool { h.mutex.Lock() defer h.mutex.Unlock() - var q *zeroRTTQueue - if handler, ok := h.handlers[clientDestConnID]; ok { - q, ok = handler.(*zeroRTTQueue) - if !ok { - h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID) - return false - } - q.retireTimer.Stop() - h.numZeroRTTEntries-- - if h.numZeroRTTEntries < 0 { - panic("number of 0-RTT queues < 0") - } + if _, ok := h.handlers[clientDestConnID]; ok { + h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID) + return false } - conn := fn() - if q != nil { - q.EnqueueAll(conn) + conn, ok := fn() + if !ok { + return false } h.handlers[clientDestConnID] = conn h.handlers[newConnID] = conn @@ -240,12 +166,7 @@ func (h *packetHandlerMap) ReplaceWithClosed(ids []protocol.ConnectionID, pers p if connClosePacket != nil { handler = newClosedLocalConn( func(addr net.Addr, info *packetInfo) { - select { - case h.closeQueue <- closePacket{payload: connClosePacket, addr: addr, info: info}: - default: - // Oops, we're backlogged. - // Just drop the packet, sending CONNECTION_CLOSE copies is best effort anyway. - } + h.enqueueClosePacket(closePacket{payload: connClosePacket, addr: addr, info: info}) }, pers, h.logger, @@ -272,17 +193,6 @@ func (h *packetHandlerMap) ReplaceWithClosed(ids []protocol.ConnectionID, pers p }) } -func (h *packetHandlerMap) runCloseQueue() { - for { - select { - case <-h.listening: - return - case p := <-h.closeQueue: - h.conn.WritePacket(p.payload, p.addr, p.info.OOB()) - } - } -} - func (h *packetHandlerMap) AddResetToken(token protocol.StatelessResetToken, handler packetHandler) { h.mutex.Lock() h.resetTokens[token] = handler @@ -295,19 +205,16 @@ func (h *packetHandlerMap) RemoveResetToken(token protocol.StatelessResetToken) h.mutex.Unlock() } -func (h *packetHandlerMap) SetServer(s unknownPacketHandler) { +func (h *packetHandlerMap) GetByResetToken(token protocol.StatelessResetToken) (packetHandler, bool) { h.mutex.Lock() - h.server = s - h.mutex.Unlock() + defer h.mutex.Unlock() + + handler, ok := h.resetTokens[token] + return handler, ok } func (h *packetHandlerMap) CloseServer() { h.mutex.Lock() - if h.server == nil { - h.mutex.Unlock() - return - } - h.server = nil var wg sync.WaitGroup for _, handler := range h.handlers { if handler.getPerspective() == protocol.PerspectiveServer { @@ -323,23 +230,16 @@ func (h *packetHandlerMap) CloseServer() { wg.Wait() } -// Destroy closes the underlying connection and waits until listen() has returned. -// It does not close active connections. -func (h *packetHandlerMap) Destroy() error { - if err := h.conn.Close(); err != nil { - return err - } - <-h.listening // wait until listening returns - return nil -} - -func (h *packetHandlerMap) close(e error) error { +func (h *packetHandlerMap) Close(e error) { h.mutex.Lock() + if h.closed { h.mutex.Unlock() - return nil + return } + close(h.closeChan) + var wg sync.WaitGroup for _, handler := range h.handlers { wg.Add(1) @@ -348,129 +248,14 @@ func (h *packetHandlerMap) close(e error) error { wg.Done() }(handler) } - - if h.server != nil { - h.server.setCloseError(e) - } h.closed = true h.mutex.Unlock() wg.Wait() - return getMultiplexer().RemoveConn(h.conn) -} - -func (h *packetHandlerMap) listen() { - defer close(h.listening) - for { - p, err := h.conn.ReadPacket() - //nolint:staticcheck // SA1019 ignore this! - // TODO: This code is used to ignore wsa errors on Windows. - // Since net.Error.Temporary is deprecated as of Go 1.18, we should find a better solution. - // See https://github.com/quic-go/quic-go/issues/1737 for details. - if nerr, ok := err.(net.Error); ok && nerr.Temporary() { - h.logger.Debugf("Temporary error reading from conn: %w", err) - continue - } - if err != nil { - h.close(err) - return - } - h.handlePacket(p) - } -} - -func (h *packetHandlerMap) handlePacket(p *receivedPacket) { - connID, err := wire.ParseConnectionID(p.data, h.connIDLen) - if err != nil { - h.logger.Debugf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err) - if h.tracer != nil { - h.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) - } - p.buffer.MaybeRelease() - return - } - - h.mutex.Lock() - defer h.mutex.Unlock() - - if isStatelessReset := h.maybeHandleStatelessReset(p.data); isStatelessReset { - return - } - - if handler, ok := h.handlers[connID]; ok { - if ha, ok := handler.(*zeroRTTQueue); ok { // only enqueue 0-RTT packets in the 0-RTT queue - if wire.Is0RTTPacket(p.data) { - ha.handlePacket(p) - return - } - } else { // existing connection - handler.handlePacket(p) - return - } - } - if !wire.IsLongHeaderPacket(p.data[0]) { - go h.maybeSendStatelessReset(p, connID) - return - } - if h.server == nil { // no server set - h.logger.Debugf("received a packet with an unexpected connection ID %s", connID) - return - } - if wire.Is0RTTPacket(p.data) { - if h.numZeroRTTEntries >= protocol.Max0RTTQueues { - if h.tracer != nil { - h.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropDOSPrevention) - } - return - } - h.numZeroRTTEntries++ - queue := &zeroRTTQueue{queue: make([]*receivedPacket, 0, 8)} - h.handlers[connID] = queue - queue.retireTimer = time.AfterFunc(h.zeroRTTQueueDuration, func() { - h.mutex.Lock() - defer h.mutex.Unlock() - // The entry might have been replaced by an actual connection. - // Only delete it if it's still a 0-RTT queue. - if handler, ok := h.handlers[connID]; ok { - if q, ok := handler.(*zeroRTTQueue); ok { - delete(h.handlers, connID) - h.numZeroRTTEntries-- - if h.numZeroRTTEntries < 0 { - panic("number of 0-RTT queues < 0") - } - q.Clear() - if h.logger.Debug() { - h.logger.Debugf("Removing 0-RTT queue for %s.", connID) - } - } - } - }) - queue.handlePacket(p) - return - } - h.server.handlePacket(p) -} - -func (h *packetHandlerMap) maybeHandleStatelessReset(data []byte) bool { - // stateless resets are always short header packets - if wire.IsLongHeaderPacket(data[0]) { - return false - } - if len(data) < 17 /* type byte + 16 bytes for the reset token */ { - return false - } - - token := *(*protocol.StatelessResetToken)(data[len(data)-16:]) - if sess, ok := h.resetTokens[token]; ok { - h.logger.Debugf("Received a stateless reset with token %#x. Closing connection.", token) - go sess.destroy(&StatelessResetError{Token: token}) - return true - } - return false } func (h *packetHandlerMap) GetStatelessResetToken(connID protocol.ConnectionID) protocol.StatelessResetToken { var token protocol.StatelessResetToken - if !h.statelessResetEnabled { + if h.statelessResetHasher == nil { // Return a random stateless reset token. // This token will be sent in the server's transport parameters. // By using a random token, an off-path attacker won't be able to disrupt the connection. @@ -484,24 +269,3 @@ func (h *packetHandlerMap) GetStatelessResetToken(connID protocol.ConnectionID) h.statelessResetMutex.Unlock() return token } - -func (h *packetHandlerMap) maybeSendStatelessReset(p *receivedPacket, connID protocol.ConnectionID) { - defer p.buffer.Release() - if !h.statelessResetEnabled { - return - } - // Don't send a stateless reset in response to very small packets. - // This includes packets that could be stateless resets. - if len(p.data) <= protocol.MinStatelessResetSize { - return - } - token := h.GetStatelessResetToken(connID) - h.logger.Debugf("Sending stateless reset to %s (connection ID: %s). Token: %#x", p.remoteAddr, connID, token) - data := make([]byte, protocol.MinStatelessResetSize-16, protocol.MinStatelessResetSize) - rand.Read(data) - data[0] = (data[0] & 0x7f) | 0x40 - data = append(data, token[:]...) - if _, err := h.conn.WritePacket(data, p.remoteAddr, p.info.OOB()); err != nil { - h.logger.Debugf("Error sending Stateless Reset: %s", err) - } -} diff --git a/vendor/github.com/quic-go/quic-go/packet_packer.go b/vendor/github.com/quic-go/quic-go/packet_packer.go index 14befd46..180a03e1 100644 --- a/vendor/github.com/quic-go/quic-go/packet_packer.go +++ b/vendor/github.com/quic-go/quic-go/packet_packer.go @@ -417,7 +417,7 @@ func (p *packetPacker) PackCoalescedPacket(onlyAck bool, v protocol.VersionNumbe if oneRTTPayload.length > 0 { size += p.shortHeaderPacketLength(connID, oneRTTPacketNumberLen, oneRTTPayload) + protocol.ByteCount(oneRTTSealer.Overhead()) } - } else if p.perspective == protocol.PerspectiveClient { // 0-RTT + } else if p.perspective == protocol.PerspectiveClient && !onlyAck { // 0-RTT packets can't contain ACK frames var err error zeroRTTSealer, err = p.cryptoSetup.Get0RTTSealer() if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable { diff --git a/vendor/github.com/quic-go/quic-go/quicvarint/varint.go b/vendor/github.com/quic-go/quic-go/quicvarint/varint.go index cbebfe61..3f12c076 100644 --- a/vendor/github.com/quic-go/quic-go/quicvarint/varint.go +++ b/vendor/github.com/quic-go/quic-go/quicvarint/varint.go @@ -70,25 +70,6 @@ func Read(r io.ByteReader) (uint64, error) { return uint64(b8) + uint64(b7)<<8 + uint64(b6)<<16 + uint64(b5)<<24 + uint64(b4)<<32 + uint64(b3)<<40 + uint64(b2)<<48 + uint64(b1)<<56, nil } -// Write writes i in the QUIC varint format to w. -// Deprecated: use Append instead. -func Write(w Writer, i uint64) { - if i <= maxVarInt1 { - w.WriteByte(uint8(i)) - } else if i <= maxVarInt2 { - w.Write([]byte{uint8(i>>8) | 0x40, uint8(i)}) - } else if i <= maxVarInt4 { - w.Write([]byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}) - } else if i <= maxVarInt8 { - w.Write([]byte{ - uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32), - uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i), - }) - } else { - panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i)) - } -} - // Append appends i in the QUIC varint format. func Append(b []byte, i uint64) []byte { if i <= maxVarInt1 { diff --git a/vendor/github.com/quic-go/quic-go/send_conn.go b/vendor/github.com/quic-go/quic-go/send_conn.go index c53ebdfa..0ac27037 100644 --- a/vendor/github.com/quic-go/quic-go/send_conn.go +++ b/vendor/github.com/quic-go/quic-go/send_conn.go @@ -22,7 +22,7 @@ type sconn struct { var _ sendConn = &sconn{} -func newSendConn(c rawConn, remote net.Addr, info *packetInfo) sendConn { +func newSendConn(c rawConn, remote net.Addr, info *packetInfo) *sconn { return &sconn{ rawConn: c, remoteAddr: remote, @@ -51,24 +51,3 @@ func (c *sconn) LocalAddr() net.Addr { } return addr } - -type spconn struct { - net.PacketConn - - remoteAddr net.Addr -} - -var _ sendConn = &spconn{} - -func newSendPconn(c net.PacketConn, remote net.Addr) sendConn { - return &spconn{PacketConn: c, remoteAddr: remote} -} - -func (c *spconn) Write(p []byte) error { - _, err := c.WriteTo(p, c.remoteAddr) - return err -} - -func (c *spconn) RemoteAddr() net.Addr { - return c.remoteAddr -} diff --git a/vendor/github.com/quic-go/quic-go/server.go b/vendor/github.com/quic-go/quic-go/server.go index edacdd85..4043c58c 100644 --- a/vendor/github.com/quic-go/quic-go/server.go +++ b/vendor/github.com/quic-go/quic-go/server.go @@ -20,7 +20,7 @@ import ( ) // ErrServerClosed is returned by the Listener or EarlyListener's Accept method after a call to Close. -var ErrServerClosed = errors.New("quic: Server closed") +var ErrServerClosed = errors.New("quic: server closed") // packetHandler handles packets type packetHandler interface { @@ -30,17 +30,13 @@ type packetHandler interface { getPerspective() protocol.Perspective } -type unknownPacketHandler interface { - handlePacket(*receivedPacket) - setCloseError(error) -} - type packetHandlerManager interface { - AddWithConnID(protocol.ConnectionID, protocol.ConnectionID, func() packetHandler) bool - Destroy() error - connRunner - SetServer(unknownPacketHandler) + Get(protocol.ConnectionID) (packetHandler, bool) + GetByResetToken(protocol.StatelessResetToken) (packetHandler, bool) + AddWithConnID(protocol.ConnectionID, protocol.ConnectionID, func() (packetHandler, bool)) bool + Close(error) CloseServer() + connRunner } type quicConn interface { @@ -54,6 +50,11 @@ type quicConn interface { shutdown() } +type zeroRTTQueue struct { + packets []*receivedPacket + expiration time.Time +} + // A Listener of QUIC type baseServer struct { mutex sync.Mutex @@ -64,16 +65,18 @@ type baseServer struct { config *Config conn rawConn - // If the server is started with ListenAddr, we create a packet conn. - // If it is started with Listen, we take a packet conn as a parameter. - createdPacketConn bool tokenGenerator *handshake.TokenGenerator - connHandler packetHandlerManager + connIDGenerator ConnectionIDGenerator + connHandler packetHandlerManager + onClose func() receivedPackets chan *receivedPacket + nextZeroRTTCleanup time.Time + zeroRTTQueues map[protocol.ConnectionID]*zeroRTTQueue // only initialized if acceptEarlyConns == true + // set as a member, so they can be set in the tests newConn func( sendConn, @@ -83,6 +86,7 @@ type baseServer struct { protocol.ConnectionID, /* client dest connection ID */ protocol.ConnectionID, /* destination connection ID */ protocol.ConnectionID, /* source connection ID */ + ConnectionIDGenerator, protocol.StatelessResetToken, *Config, *tls.Config, @@ -94,61 +98,101 @@ type baseServer struct { protocol.VersionNumber, ) quicConn - serverError error - errorChan chan struct{} - closed bool - running chan struct{} // closed as soon as run() returns + serverError error + errorChan chan struct{} + closed bool + running chan struct{} // closed as soon as run() returns + versionNegotiationQueue chan *receivedPacket + invalidTokenQueue chan *receivedPacket connQueue chan quicConn connQueueLen int32 // to be used as an atomic + tracer logging.Tracer + logger utils.Logger } -var ( - _ Listener = &baseServer{} - _ unknownPacketHandler = &baseServer{} -) +// A Listener listens for incoming QUIC connections. +// It returns connections once the handshake has completed. +type Listener struct { + baseServer *baseServer +} -type earlyServer struct{ *baseServer } +// Accept returns new connections. It should be called in a loop. +func (l *Listener) Accept(ctx context.Context) (Connection, error) { + return l.baseServer.Accept(ctx) +} -var _ EarlyListener = &earlyServer{} +// Close the server. All active connections will be closed. +func (l *Listener) Close() error { + return l.baseServer.Close() +} -func (s *earlyServer) Accept(ctx context.Context) (EarlyConnection, error) { - return s.baseServer.accept(ctx) +// Addr returns the local network address that the server is listening on. +func (l *Listener) Addr() net.Addr { + return l.baseServer.Addr() +} + +// An EarlyListener listens for incoming QUIC connections, and returns them before the handshake completes. +// For connections that don't use 0-RTT, this allows the server to send 0.5-RTT data. +// This data is encrypted with forward-secure keys, however, the client's identity has not yet been verified. +// For connection using 0-RTT, this allows the server to accept and respond to streams that the client opened in the +// 0-RTT data it sent. Note that at this point during the handshake, the live-ness of the +// client has not yet been confirmed, and the 0-RTT data could have been replayed by an attacker. +type EarlyListener struct { + baseServer *baseServer +} + +// Accept returns a new connections. It should be called in a loop. +func (l *EarlyListener) Accept(ctx context.Context) (EarlyConnection, error) { + return l.baseServer.accept(ctx) +} + +// Close the server. All active connections will be closed. +func (l *EarlyListener) Close() error { + return l.baseServer.Close() +} + +// Addr returns the local network addr that the server is listening on. +func (l *EarlyListener) Addr() net.Addr { + return l.baseServer.Addr() } // ListenAddr creates a QUIC server listening on a given address. // The tls.Config must not be nil and must contain a certificate configuration. // The quic.Config may be nil, in that case the default values will be used. -func ListenAddr(addr string, tlsConf *tls.Config, config *Config) (Listener, error) { - return listenAddr(addr, tlsConf, config, false) -} - -// ListenAddrEarly works like ListenAddr, but it returns connections before the handshake completes. -func ListenAddrEarly(addr string, tlsConf *tls.Config, config *Config) (EarlyListener, error) { - s, err := listenAddr(addr, tlsConf, config, true) +func ListenAddr(addr string, tlsConf *tls.Config, config *Config) (*Listener, error) { + conn, err := listenUDP(addr) if err != nil { return nil, err } - return &earlyServer{s}, nil + return (&Transport{ + Conn: conn, + createdConn: true, + isSingleUse: true, + }).Listen(tlsConf, config) } -func listenAddr(addr string, tlsConf *tls.Config, config *Config, acceptEarly bool) (*baseServer, error) { +// ListenAddrEarly works like ListenAddr, but it returns connections before the handshake completes. +func ListenAddrEarly(addr string, tlsConf *tls.Config, config *Config) (*EarlyListener, error) { + conn, err := listenUDP(addr) + if err != nil { + return nil, err + } + return (&Transport{ + Conn: conn, + createdConn: true, + isSingleUse: true, + }).ListenEarly(tlsConf, config) +} + +func listenUDP(addr string) (*net.UDPConn, error) { udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { return nil, err } - conn, err := net.ListenUDP("udp", udpAddr) - if err != nil { - return nil, err - } - serv, err := listen(conn, tlsConf, config, acceptEarly) - if err != nil { - return nil, err - } - serv.createdPacketConn = true - return serv, nil + return net.ListenUDP("udp", udpAddr) } // Listen listens for QUIC connections on a given net.PacketConn. If the @@ -161,61 +205,55 @@ func listenAddr(addr string, tlsConf *tls.Config, config *Config, acceptEarly bo // The tls.Config must not be nil and must contain a certificate configuration. // Furthermore, it must define an application control (using NextProtos). // The quic.Config may be nil, in that case the default values will be used. -func Listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (Listener, error) { - return listen(conn, tlsConf, config, false) +func Listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (*Listener, error) { + tr := &Transport{Conn: conn, isSingleUse: true} + return tr.Listen(tlsConf, config) } // ListenEarly works like Listen, but it returns connections before the handshake completes. -func ListenEarly(conn net.PacketConn, tlsConf *tls.Config, config *Config) (EarlyListener, error) { - s, err := listen(conn, tlsConf, config, true) - if err != nil { - return nil, err - } - return &earlyServer{s}, nil +func ListenEarly(conn net.PacketConn, tlsConf *tls.Config, config *Config) (*EarlyListener, error) { + tr := &Transport{Conn: conn, isSingleUse: true} + return tr.ListenEarly(tlsConf, config) } -func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config, acceptEarly bool) (*baseServer, error) { - if tlsConf == nil { - return nil, errors.New("quic: tls.Config not set") - } - if err := validateConfig(config); err != nil { - return nil, err - } - config = populateServerConfig(config) - for _, v := range config.Versions { - if !protocol.IsValidVersion(v) { - return nil, fmt.Errorf("%s is not a valid QUIC version", v) - } - } - - connHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDGenerator.ConnectionIDLen(), config.StatelessResetKey, config.Tracer) - if err != nil { - return nil, err - } +func newServer( + conn rawConn, + connHandler packetHandlerManager, + connIDGenerator ConnectionIDGenerator, + tlsConf *tls.Config, + config *Config, + tracer logging.Tracer, + onClose func(), + acceptEarly bool, +) (*baseServer, error) { tokenGenerator, err := handshake.NewTokenGenerator(rand.Reader) if err != nil { return nil, err } - c, err := wrapConn(conn) - if err != nil { - return nil, err - } s := &baseServer{ - conn: c, - tlsConf: tlsConf, - config: config, - tokenGenerator: tokenGenerator, - connHandler: connHandler, - connQueue: make(chan quicConn), - errorChan: make(chan struct{}), - running: make(chan struct{}), - receivedPackets: make(chan *receivedPacket, protocol.MaxServerUnprocessedPackets), - newConn: newConnection, - logger: utils.DefaultLogger.WithPrefix("server"), - acceptEarlyConns: acceptEarly, + conn: conn, + tlsConf: tlsConf, + config: config, + tokenGenerator: tokenGenerator, + connIDGenerator: connIDGenerator, + connHandler: connHandler, + connQueue: make(chan quicConn), + errorChan: make(chan struct{}), + running: make(chan struct{}), + receivedPackets: make(chan *receivedPacket, protocol.MaxServerUnprocessedPackets), + versionNegotiationQueue: make(chan *receivedPacket, 4), + invalidTokenQueue: make(chan *receivedPacket, 4), + newConn: newConnection, + tracer: tracer, + logger: utils.DefaultLogger.WithPrefix("server"), + acceptEarlyConns: acceptEarly, + onClose: onClose, + } + if acceptEarly { + s.zeroRTTQueues = map[protocol.ConnectionID]*zeroRTTQueue{} } go s.run() - connHandler.SetServer(s) + go s.runSendQueue() s.logger.Debugf("Listening for %s connections on %s", conn.LocalAddr().Network(), conn.LocalAddr().String()) return s, nil } @@ -239,6 +277,19 @@ func (s *baseServer) run() { } } +func (s *baseServer) runSendQueue() { + for { + select { + case <-s.running: + return + case p := <-s.versionNegotiationQueue: + s.maybeSendVersionNegotiationPacket(p) + case p := <-s.invalidTokenQueue: + s.maybeSendInvalidToken(p) + } + } +} + // Accept returns connections that already completed the handshake. // It is only valid if acceptEarlyConns is false. func (s *baseServer) Accept(ctx context.Context) (Connection, error) { @@ -267,18 +318,12 @@ func (s *baseServer) Close() error { if s.serverError == nil { s.serverError = ErrServerClosed } - // If the server was started with ListenAddr, we created the packet conn. - // We need to close it in order to make the go routine reading from that conn return. - createdPacketConn := s.createdPacketConn s.closed = true close(s.errorChan) s.mutex.Unlock() <-s.running - s.connHandler.CloseServer() - if createdPacketConn { - return s.connHandler.Destroy() - } + s.onClose() return nil } @@ -303,17 +348,21 @@ func (s *baseServer) handlePacket(p *receivedPacket) { case s.receivedPackets <- p: default: s.logger.Debugf("Dropping packet from %s (%d bytes). Server receive queue full.", p.remoteAddr, p.Size()) - if s.config.Tracer != nil { - s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention) + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention) } } } func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer still in use? */ { + if !s.nextZeroRTTCleanup.IsZero() && p.rcvTime.After(s.nextZeroRTTCleanup) { + defer s.cleanupZeroRTTQueues(p.rcvTime) + } + if wire.IsVersionNegotiationPacket(p.data) { s.logger.Debugf("Dropping Version Negotiation packet.") - if s.config.Tracer != nil { - s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedPacket) + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedPacket) } return false } @@ -322,42 +371,54 @@ func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer s panic(fmt.Sprintf("misrouted packet: %#v", p.data)) } v, err := wire.ParseVersion(p.data) - // send a Version Negotiation Packet if the client is speaking a different protocol version - if err != nil || !protocol.IsSupportedVersion(s.config.Versions, v) { - if err != nil || p.Size() < protocol.MinUnknownVersionPacketSize { - s.logger.Debugf("Dropping a packet with an unknown version that is too small (%d bytes)", p.Size()) - if s.config.Tracer != nil { - s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) - } - return false - } - _, src, dest, err := wire.ParseArbitraryLenConnectionIDs(p.data) - if err != nil { // should never happen - s.logger.Debugf("Dropping a packet with an unknown version for which we failed to parse connection IDs") - if s.config.Tracer != nil { - s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) - } - return false - } - if !s.config.DisableVersionNegotiationPackets { - go s.sendVersionNegotiationPacket(p.remoteAddr, src, dest, p.info.OOB(), v) + // drop the packet if we failed to parse the protocol version + if err != nil { + s.logger.Debugf("Dropping a packet with an unknown version") + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) } return false } + // send a Version Negotiation Packet if the client is speaking a different protocol version + if !protocol.IsSupportedVersion(s.config.Versions, v) { + if s.config.DisableVersionNegotiationPackets { + return false + } + + if p.Size() < protocol.MinUnknownVersionPacketSize { + s.logger.Debugf("Dropping a packet with an unsupported version number %d that is too small (%d bytes)", v, p.Size()) + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) + } + return false + } + return s.enqueueVersionNegotiationPacket(p) + } + + if wire.Is0RTTPacket(p.data) { + if !s.acceptEarlyConns { + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropUnexpectedPacket) + } + return false + } + return s.handle0RTTPacket(p) + } + // If we're creating a new connection, the packet will be passed to the connection. // The header will then be parsed again. hdr, _, _, err := wire.ParsePacket(p.data) if err != nil { - if s.config.Tracer != nil { - s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) } s.logger.Debugf("Error parsing packet: %s", err) return false } if hdr.Type == protocol.PacketTypeInitial && p.Size() < protocol.MinInitialPacketSize { s.logger.Debugf("Dropping a packet that is too small to be a valid Initial (%d bytes)", p.Size()) - if s.config.Tracer != nil { - s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) } return false } @@ -367,8 +428,8 @@ func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer s // There's little point in sending a Stateless Reset, since the client // might not have received the token yet. s.logger.Debugf("Dropping long header packet of type %s (%d bytes)", hdr.Type, len(p.data)) - if s.config.Tracer != nil { - s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropUnexpectedPacket) + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropUnexpectedPacket) } return false } @@ -383,6 +444,74 @@ func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer s return true } +func (s *baseServer) handle0RTTPacket(p *receivedPacket) bool { + connID, err := wire.ParseConnectionID(p.data, 0) + if err != nil { + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropHeaderParseError) + } + return false + } + + // check again if we might have a connection now + if handler, ok := s.connHandler.Get(connID); ok { + handler.handlePacket(p) + return true + } + + if q, ok := s.zeroRTTQueues[connID]; ok { + if len(q.packets) >= protocol.Max0RTTQueueLen { + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropDOSPrevention) + } + return false + } + q.packets = append(q.packets, p) + return true + } + + if len(s.zeroRTTQueues) >= protocol.Max0RTTQueues { + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropDOSPrevention) + } + return false + } + queue := &zeroRTTQueue{packets: make([]*receivedPacket, 1, 8)} + queue.packets[0] = p + expiration := p.rcvTime.Add(protocol.Max0RTTQueueingDuration) + queue.expiration = expiration + if s.nextZeroRTTCleanup.IsZero() || s.nextZeroRTTCleanup.After(expiration) { + s.nextZeroRTTCleanup = expiration + } + s.zeroRTTQueues[connID] = queue + return true +} + +func (s *baseServer) cleanupZeroRTTQueues(now time.Time) { + // Iterate over all queues to find those that are expired. + // This is ok since we're placing a pretty low limit on the number of queues. + var nextCleanup time.Time + for connID, q := range s.zeroRTTQueues { + if q.expiration.After(now) { + if nextCleanup.IsZero() || nextCleanup.After(q.expiration) { + nextCleanup = q.expiration + } + continue + } + for _, p := range q.packets { + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropDOSPrevention) + } + p.buffer.Release() + } + delete(s.zeroRTTQueues, connID) + if s.logger.Debug() { + s.logger.Debugf("Removing 0-RTT queue for %s.", connID) + } + } + s.nextZeroRTTCleanup = nextCleanup +} + // validateToken returns false if: // - address is invalid // - token is expired @@ -406,12 +535,20 @@ func (s *baseServer) validateToken(token *handshake.Token, addr net.Addr) bool { func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) error { if len(hdr.Token) == 0 && hdr.DestConnectionID.Len() < protocol.MinConnectionIDLenInitial { p.buffer.Release() - if s.config.Tracer != nil { - s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) } return errors.New("too short connection ID") } + // The server queues packets for a while, and we might already have established a connection by now. + // This results in a second check in the connection map. + // That's ok since it's not the hot path (it's only taken by some Initial and 0-RTT packets). + if handler, ok := s.connHandler.Get(hdr.DestConnectionID); ok { + handler.handlePacket(p) + return nil + } + var ( token *handshake.Token retrySrcConnID *protocol.ConnectionID @@ -429,7 +566,6 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro } clientAddrIsValid := s.validateToken(token, p.remoteAddr) - if token != nil && !clientAddrIsValid { // For invalid and expired non-retry tokens, we don't send an INVALID_TOKEN error. // We just ignore them, and act as if there was no token on this packet at all. @@ -440,16 +576,13 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro // For Retry tokens, we send an INVALID_ERROR if // * the token is too old, or // * the token is invalid, in case of a retry token. - go func() { - defer p.buffer.Release() - if err := s.maybeSendInvalidToken(p, hdr); err != nil { - s.logger.Debugf("Error sending INVALID_TOKEN error: %s", err) - } - }() + s.enqueueInvalidToken(p) return nil } } if token == nil && s.config.RequireAddressValidation(p.remoteAddr) { + // Retry invalidates all 0-RTT packets sent. + delete(s.zeroRTTQueues, hdr.DestConnectionID) go func() { defer p.buffer.Release() if err := s.sendRetry(p.remoteAddr, hdr, p.info); err != nil { @@ -470,26 +603,31 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro return nil } - connID, err := s.config.ConnectionIDGenerator.GenerateConnectionID() + connID, err := s.connIDGenerator.GenerateConnectionID() if err != nil { return err } s.logger.Debugf("Changing connection ID to %s.", connID) var conn quicConn tracingID := nextConnTracingID() - if added := s.connHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler { + if added := s.connHandler.AddWithConnID(hdr.DestConnectionID, connID, func() (packetHandler, bool) { + config := s.config + if s.config.GetConfigForClient != nil { + conf, err := s.config.GetConfigForClient(&ClientHelloInfo{RemoteAddr: p.remoteAddr}) + if err != nil { + s.logger.Debugf("Rejecting new connection due to GetConfigForClient callback") + return nil, false + } + config = populateConfig(conf) + } var tracer logging.ConnectionTracer - if s.config.Tracer != nil { + if config.Tracer != nil { // Use the same connection ID that is passed to the client's GetLogWriter callback. connID := hdr.DestConnectionID if origDestConnID.Len() > 0 { connID = origDestConnID } - tracer = s.config.Tracer.TracerForConnection( - context.WithValue(context.Background(), ConnectionTracingKey, tracingID), - protocol.PerspectiveServer, - connID, - ) + tracer = config.Tracer(context.WithValue(context.Background(), ConnectionTracingKey, tracingID), protocol.PerspectiveServer, connID) } conn = s.newConn( newSendConn(s.conn, p.remoteAddr, p.info), @@ -499,8 +637,9 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro hdr.DestConnectionID, hdr.SrcConnectionID, connID, + s.connIDGenerator, s.connHandler.GetStatelessResetToken(connID), - s.config, + config, s.tlsConf, s.tokenGenerator, clientAddrIsValid, @@ -510,8 +649,22 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro hdr.Version, ) conn.handlePacket(p) - return conn + + if q, ok := s.zeroRTTQueues[hdr.DestConnectionID]; ok { + for _, p := range q.packets { + conn.handlePacket(p) + } + delete(s.zeroRTTQueues, hdr.DestConnectionID) + } + + return conn, true }); !added { + go func() { + defer p.buffer.Release() + if err := s.sendConnectionRefused(p.remoteAddr, hdr, p.info); err != nil { + s.logger.Debugf("Error rejecting connection: %s", err) + } + }() return nil } go conn.run() @@ -555,7 +708,7 @@ func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *pack // Log the Initial packet now. // If no Retry is sent, the packet will be logged by the connection. (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) - srcConnID, err := s.config.ConnectionIDGenerator.GenerateConnectionID() + srcConnID, err := s.connIDGenerator.GenerateConnectionID() if err != nil { return err } @@ -584,38 +737,58 @@ func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *pack // append the Retry integrity tag tag := handshake.GetRetryIntegrityTag(buf.Data, hdr.DestConnectionID, hdr.Version) buf.Data = append(buf.Data, tag[:]...) - if s.config.Tracer != nil { - s.config.Tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(buf.Data)), nil) + if s.tracer != nil { + s.tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(buf.Data)), nil) } _, err = s.conn.WritePacket(buf.Data, remoteAddr, info.OOB()) return err } -func (s *baseServer) maybeSendInvalidToken(p *receivedPacket, hdr *wire.Header) error { +func (s *baseServer) enqueueInvalidToken(p *receivedPacket) { + select { + case s.invalidTokenQueue <- p: + default: + // it's fine to drop INVALID_TOKEN packets when we are busy + p.buffer.Release() + } +} + +func (s *baseServer) maybeSendInvalidToken(p *receivedPacket) { + defer p.buffer.Release() + + hdr, _, _, err := wire.ParsePacket(p.data) + if err != nil { + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) + } + s.logger.Debugf("Error parsing packet: %s", err) + return + } + // Only send INVALID_TOKEN if we can unprotect the packet. // This makes sure that we won't send it for packets that were corrupted. sealer, opener := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveServer, hdr.Version) data := p.data[:hdr.ParsedLen()+hdr.Length] extHdr, err := unpackLongHeader(opener, hdr, data, hdr.Version) if err != nil { - if s.config.Tracer != nil { - s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropHeaderParseError) + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropHeaderParseError) } - // don't return the error here. Just drop the packet. - return nil + return } hdrLen := extHdr.ParsedLen() if _, err := opener.Open(data[hdrLen:hdrLen], data[hdrLen:], extHdr.PacketNumber, data[:hdrLen]); err != nil { - // don't return the error here. Just drop the packet. - if s.config.Tracer != nil { - s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropPayloadDecryptError) + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropPayloadDecryptError) } - return nil + return } if s.logger.Debug() { s.logger.Debugf("Client sent an invalid retry token. Sending INVALID_TOKEN to %s.", p.remoteAddr) } - return s.sendError(p.remoteAddr, hdr, sealer, qerr.InvalidToken, p.info) + if err := s.sendError(p.remoteAddr, hdr, sealer, qerr.InvalidToken, p.info); err != nil { + s.logger.Debugf("Error sending INVALID_TOKEN error: %s", err) + } } func (s *baseServer) sendConnectionRefused(remoteAddr net.Addr, hdr *wire.Header, info *packetInfo) error { @@ -661,21 +834,48 @@ func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer han replyHdr.Log(s.logger) wire.LogFrame(s.logger, ccf, true) - if s.config.Tracer != nil { - s.config.Tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(b.Data)), []logging.Frame{ccf}) + if s.tracer != nil { + s.tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(b.Data)), []logging.Frame{ccf}) } _, err = s.conn.WritePacket(b.Data, remoteAddr, info.OOB()) return err } -func (s *baseServer) sendVersionNegotiationPacket(remote net.Addr, src, dest protocol.ArbitraryLenConnectionID, oob []byte, v protocol.VersionNumber) { +func (s *baseServer) enqueueVersionNegotiationPacket(p *receivedPacket) (bufferInUse bool) { + select { + case s.versionNegotiationQueue <- p: + return true + default: + // it's fine to not send version negotiation packets when we are busy + } + return false +} + +func (s *baseServer) maybeSendVersionNegotiationPacket(p *receivedPacket) { + defer p.buffer.Release() + + v, err := wire.ParseVersion(p.data) + if err != nil { + s.logger.Debugf("failed to parse version for sending version negotiation packet: %s", err) + return + } + + _, src, dest, err := wire.ParseArbitraryLenConnectionIDs(p.data) + if err != nil { // should never happen + s.logger.Debugf("Dropping a packet with an unknown version for which we failed to parse connection IDs") + if s.tracer != nil { + s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) + } + return + } + s.logger.Debugf("Client offered version %s, sending Version Negotiation", v) data := wire.ComposeVersionNegotiation(dest, src, s.config.Versions) - if s.config.Tracer != nil { - s.config.Tracer.SentVersionNegotiationPacket(remote, src, dest, s.config.Versions) + if s.tracer != nil { + s.tracer.SentVersionNegotiationPacket(p.remoteAddr, src, dest, s.config.Versions) } - if _, err := s.conn.WritePacket(data, remote, oob); err != nil { + if _, err := s.conn.WritePacket(data, p.remoteAddr, p.info.OOB()); err != nil { s.logger.Debugf("Error sending Version Negotiation: %s", err) } } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_buffers.go b/vendor/github.com/quic-go/quic-go/sys_conn_buffers.go new file mode 100644 index 00000000..0c4e024a --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_buffers.go @@ -0,0 +1,68 @@ +package quic + +import ( + "errors" + "fmt" + "net" + "syscall" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +//go:generate sh -c "echo '// Code generated by go generate. DO NOT EDIT.\n// Source: sys_conn_buffers.go\n' > sys_conn_buffers_write.go && sed -e 's/SetReadBuffer/SetWriteBuffer/g' -e 's/setReceiveBuffer/setSendBuffer/g' -e 's/inspectReadBuffer/inspectWriteBuffer/g' -e 's/protocol\\.DesiredReceiveBufferSize/protocol\\.DesiredSendBufferSize/g' -e 's/forceSetReceiveBuffer/forceSetSendBuffer/g' -e 's/receive buffer/send buffer/g' sys_conn_buffers.go | sed '/^\\/\\/go:generate/d' >> sys_conn_buffers_write.go" +func setReceiveBuffer(c net.PacketConn, logger utils.Logger) error { + conn, ok := c.(interface{ SetReadBuffer(int) error }) + if !ok { + return errors.New("connection doesn't allow setting of receive buffer size. Not a *net.UDPConn?") + } + + var syscallConn syscall.RawConn + if sc, ok := c.(interface { + SyscallConn() (syscall.RawConn, error) + }); ok { + var err error + syscallConn, err = sc.SyscallConn() + if err != nil { + syscallConn = nil + } + } + // The connection has a SetReadBuffer method, but we couldn't obtain a syscall.RawConn. + // This shouldn't happen for a net.UDPConn, but is possible if the connection just implements the + // net.PacketConn interface and the SetReadBuffer method. + // We have no way of checking if increasing the buffer size actually worked. + if syscallConn == nil { + return conn.SetReadBuffer(protocol.DesiredReceiveBufferSize) + } + + size, err := inspectReadBuffer(syscallConn) + if err != nil { + return fmt.Errorf("failed to determine receive buffer size: %w", err) + } + if size >= protocol.DesiredReceiveBufferSize { + logger.Debugf("Conn has receive buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024) + return nil + } + // Ignore the error. We check if we succeeded by querying the buffer size afterward. + _ = conn.SetReadBuffer(protocol.DesiredReceiveBufferSize) + newSize, err := inspectReadBuffer(syscallConn) + if newSize < protocol.DesiredReceiveBufferSize { + // Try again with RCVBUFFORCE on Linux + _ = forceSetReceiveBuffer(syscallConn, protocol.DesiredReceiveBufferSize) + newSize, err = inspectReadBuffer(syscallConn) + if err != nil { + return fmt.Errorf("failed to determine receive buffer size: %w", err) + } + } + if err != nil { + return fmt.Errorf("failed to determine receive buffer size: %w", err) + } + if newSize == size { + return fmt.Errorf("failed to increase receive buffer size (wanted: %d kiB, got %d kiB)", protocol.DesiredReceiveBufferSize/1024, newSize/1024) + } + if newSize < protocol.DesiredReceiveBufferSize { + return fmt.Errorf("failed to sufficiently increase receive buffer size (was: %d kiB, wanted: %d kiB, got: %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024, newSize/1024) + } + logger.Debugf("Increased receive buffer size to %d kiB", newSize/1024) + return nil +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_buffers_write.go b/vendor/github.com/quic-go/quic-go/sys_conn_buffers_write.go new file mode 100644 index 00000000..86fead96 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_buffers_write.go @@ -0,0 +1,70 @@ +// Code generated by go generate. DO NOT EDIT. +// Source: sys_conn_buffers.go + +package quic + +import ( + "errors" + "fmt" + "net" + "syscall" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" +) + +func setSendBuffer(c net.PacketConn, logger utils.Logger) error { + conn, ok := c.(interface{ SetWriteBuffer(int) error }) + if !ok { + return errors.New("connection doesn't allow setting of send buffer size. Not a *net.UDPConn?") + } + + var syscallConn syscall.RawConn + if sc, ok := c.(interface { + SyscallConn() (syscall.RawConn, error) + }); ok { + var err error + syscallConn, err = sc.SyscallConn() + if err != nil { + syscallConn = nil + } + } + // The connection has a SetWriteBuffer method, but we couldn't obtain a syscall.RawConn. + // This shouldn't happen for a net.UDPConn, but is possible if the connection just implements the + // net.PacketConn interface and the SetWriteBuffer method. + // We have no way of checking if increasing the buffer size actually worked. + if syscallConn == nil { + return conn.SetWriteBuffer(protocol.DesiredSendBufferSize) + } + + size, err := inspectWriteBuffer(syscallConn) + if err != nil { + return fmt.Errorf("failed to determine send buffer size: %w", err) + } + if size >= protocol.DesiredSendBufferSize { + logger.Debugf("Conn has send buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredSendBufferSize/1024) + return nil + } + // Ignore the error. We check if we succeeded by querying the buffer size afterward. + _ = conn.SetWriteBuffer(protocol.DesiredSendBufferSize) + newSize, err := inspectWriteBuffer(syscallConn) + if newSize < protocol.DesiredSendBufferSize { + // Try again with RCVBUFFORCE on Linux + _ = forceSetSendBuffer(syscallConn, protocol.DesiredSendBufferSize) + newSize, err = inspectWriteBuffer(syscallConn) + if err != nil { + return fmt.Errorf("failed to determine send buffer size: %w", err) + } + } + if err != nil { + return fmt.Errorf("failed to determine send buffer size: %w", err) + } + if newSize == size { + return fmt.Errorf("failed to increase send buffer size (wanted: %d kiB, got %d kiB)", protocol.DesiredSendBufferSize/1024, newSize/1024) + } + if newSize < protocol.DesiredSendBufferSize { + return fmt.Errorf("failed to sufficiently increase send buffer size (was: %d kiB, wanted: %d kiB, got: %d kiB)", size/1024, protocol.DesiredSendBufferSize/1024, newSize/1024) + } + logger.Debugf("Increased send buffer size to %d kiB", newSize/1024) + return nil +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go index 61c3f54b..2c12233c 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go @@ -2,7 +2,11 @@ package quic -import "golang.org/x/sys/unix" +import ( + "syscall" + + "golang.org/x/sys/unix" +) const msgTypeIPTOS = unix.IP_TOS @@ -17,3 +21,23 @@ const ( ) const batchSize = 8 // needs to smaller than MaxUint8 (otherwise the type of oobConn.readPos has to be changed) + +func forceSetReceiveBuffer(c syscall.RawConn, bytes int) error { + var serr error + if err := c.Control(func(fd uintptr) { + serr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, bytes) + }); err != nil { + return err + } + return serr +} + +func forceSetSendBuffer(c syscall.RawConn, bytes int) error { + var serr error + if err := c.Control(func(fd uintptr) { + serr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, bytes) + }); err != nil { + return err + } + return serr +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go new file mode 100644 index 00000000..80b795c3 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go @@ -0,0 +1,6 @@ +//go:build !linux + +package quic + +func forceSetReceiveBuffer(c any, bytes int) error { return nil } +func forceSetSendBuffer(c any, bytes int) error { return nil } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go b/vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go index 7ab5040a..c5c7f486 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go @@ -8,8 +8,7 @@ func newConn(c net.PacketConn) (rawConn, error) { return &basicConn{PacketConn: c}, nil } -func inspectReadBuffer(interface{}) (int, error) { - return 0, nil -} +func inspectReadBuffer(any) (int, error) { return 0, nil } +func inspectWriteBuffer(any) (int, error) { return 0, nil } func (i *packetInfo) OOB() []byte { return nil } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_oob.go b/vendor/github.com/quic-go/quic-go/sys_conn_oob.go index 806dfb81..42e5f53b 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_oob.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_oob.go @@ -5,7 +5,6 @@ package quic import ( "encoding/binary" "errors" - "fmt" "net" "syscall" "time" @@ -32,20 +31,10 @@ type batchConn interface { ReadBatch(ms []ipv4.Message, flags int) (int, error) } -func inspectReadBuffer(c interface{}) (int, error) { - conn, ok := c.(interface { - SyscallConn() (syscall.RawConn, error) - }) - if !ok { - return 0, errors.New("doesn't have a SyscallConn") - } - rawConn, err := conn.SyscallConn() - if err != nil { - return 0, fmt.Errorf("couldn't get syscall.RawConn: %w", err) - } +func inspectReadBuffer(c syscall.RawConn) (int, error) { var size int var serr error - if err := rawConn.Control(func(fd uintptr) { + if err := c.Control(func(fd uintptr) { size, serr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF) }); err != nil { return 0, err @@ -53,6 +42,17 @@ func inspectReadBuffer(c interface{}) (int, error) { return size, serr } +func inspectWriteBuffer(c syscall.RawConn) (int, error) { + var size int + var serr error + if err := c.Control(func(fd uintptr) { + size, serr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF) + }); err != nil { + return 0, err + } + return size, serr +} + type oobConn struct { OOBCapablePacketConn batchConn batchConn diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_windows.go b/vendor/github.com/quic-go/quic-go/sys_conn_windows.go index b003fe94..580d18a5 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_windows.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_windows.go @@ -3,9 +3,6 @@ package quic import ( - "errors" - "fmt" - "net" "syscall" "golang.org/x/sys/windows" @@ -15,20 +12,10 @@ func newConn(c OOBCapablePacketConn) (rawConn, error) { return &basicConn{PacketConn: c}, nil } -func inspectReadBuffer(c net.PacketConn) (int, error) { - conn, ok := c.(interface { - SyscallConn() (syscall.RawConn, error) - }) - if !ok { - return 0, errors.New("doesn't have a SyscallConn") - } - rawConn, err := conn.SyscallConn() - if err != nil { - return 0, fmt.Errorf("couldn't get syscall.RawConn: %w", err) - } +func inspectReadBuffer(c syscall.RawConn) (int, error) { var size int var serr error - if err := rawConn.Control(func(fd uintptr) { + if err := c.Control(func(fd uintptr) { size, serr = windows.GetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_RCVBUF) }); err != nil { return 0, err @@ -36,4 +23,15 @@ func inspectReadBuffer(c net.PacketConn) (int, error) { return size, serr } +func inspectWriteBuffer(c syscall.RawConn) (int, error) { + var size int + var serr error + if err := c.Control(func(fd uintptr) { + size, serr = windows.GetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_SNDBUF) + }); err != nil { + return 0, err + } + return size, serr +} + func (i *packetInfo) OOB() []byte { return nil } diff --git a/vendor/github.com/quic-go/quic-go/transport.go b/vendor/github.com/quic-go/quic-go/transport.go new file mode 100644 index 00000000..c62ee388 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/transport.go @@ -0,0 +1,416 @@ +package quic + +import ( + "context" + "crypto/rand" + "crypto/tls" + "errors" + "log" + "net" + "os" + "strconv" + "strings" + "sync" + "time" + + "github.com/quic-go/quic-go/internal/wire" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/logging" +) + +type Transport struct { + // A single net.PacketConn can only be handled by one Transport. + // Bad things will happen if passed to multiple Transports. + // + // If the connection satisfies the OOBCapablePacketConn interface + // (as a net.UDPConn does), ECN and packet info support will be enabled. + // In this case, optimized syscalls might be used, skipping the + // ReadFrom and WriteTo calls to read / write packets. + Conn net.PacketConn + + // The length of the connection ID in bytes. + // It can be 0, or any value between 4 and 18. + // If unset, a 4 byte connection ID will be used. + ConnectionIDLength int + + // Use for generating new connection IDs. + // This allows the application to control of the connection IDs used, + // which allows routing / load balancing based on connection IDs. + // All Connection IDs returned by the ConnectionIDGenerator MUST + // have the same length. + ConnectionIDGenerator ConnectionIDGenerator + + // The StatelessResetKey is used to generate stateless reset tokens. + // If no key is configured, sending of stateless resets is disabled. + StatelessResetKey *StatelessResetKey + + // A Tracer traces events that don't belong to a single QUIC connection. + Tracer logging.Tracer + + handlerMap packetHandlerManager + + mutex sync.Mutex + initOnce sync.Once + initErr error + + // Set in init. + // If no ConnectionIDGenerator is set, this is the ConnectionIDLength. + connIDLen int + // Set in init. + // If no ConnectionIDGenerator is set, this is set to a default. + connIDGenerator ConnectionIDGenerator + + server unknownPacketHandler + + conn rawConn + + closeQueue chan closePacket + statelessResetQueue chan *receivedPacket + + listening chan struct{} // is closed when listen returns + closed bool + createdConn bool + isSingleUse bool // was created for a single server or client, i.e. by calling quic.Listen or quic.Dial + + logger utils.Logger +} + +// Listen starts listening for incoming QUIC connections. +// There can only be a single listener on any net.PacketConn. +// Listen may only be called again after the current Listener was closed. +func (t *Transport) Listen(tlsConf *tls.Config, conf *Config) (*Listener, error) { + if tlsConf == nil { + return nil, errors.New("quic: tls.Config not set") + } + if err := validateConfig(conf); err != nil { + return nil, err + } + + t.mutex.Lock() + defer t.mutex.Unlock() + + if t.server != nil { + return nil, errListenerAlreadySet + } + conf = populateServerConfig(conf) + if err := t.init(true); err != nil { + return nil, err + } + s, err := newServer(t.conn, t.handlerMap, t.connIDGenerator, tlsConf, conf, t.Tracer, t.closeServer, false) + if err != nil { + return nil, err + } + t.server = s + return &Listener{baseServer: s}, nil +} + +// ListenEarly starts listening for incoming QUIC connections. +// There can only be a single listener on any net.PacketConn. +// Listen may only be called again after the current Listener was closed. +func (t *Transport) ListenEarly(tlsConf *tls.Config, conf *Config) (*EarlyListener, error) { + if tlsConf == nil { + return nil, errors.New("quic: tls.Config not set") + } + if err := validateConfig(conf); err != nil { + return nil, err + } + + t.mutex.Lock() + defer t.mutex.Unlock() + + if t.server != nil { + return nil, errListenerAlreadySet + } + conf = populateServerConfig(conf) + if err := t.init(true); err != nil { + return nil, err + } + s, err := newServer(t.conn, t.handlerMap, t.connIDGenerator, tlsConf, conf, t.Tracer, t.closeServer, true) + if err != nil { + return nil, err + } + t.server = s + return &EarlyListener{baseServer: s}, nil +} + +// Dial dials a new connection to a remote host (not using 0-RTT). +func (t *Transport) Dial(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *Config) (Connection, error) { + if err := validateConfig(conf); err != nil { + return nil, err + } + conf = populateConfig(conf) + if err := t.init(false); err != nil { + return nil, err + } + var onClose func() + if t.isSingleUse { + onClose = func() { t.Close() } + } + return dial(ctx, newSendConn(t.conn, addr, nil), t.connIDGenerator, t.handlerMap, tlsConf, conf, onClose, false) +} + +// DialEarly dials a new connection, attempting to use 0-RTT if possible. +func (t *Transport) DialEarly(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) { + if err := validateConfig(conf); err != nil { + return nil, err + } + conf = populateConfig(conf) + if err := t.init(false); err != nil { + return nil, err + } + var onClose func() + if t.isSingleUse { + onClose = func() { t.Close() } + } + return dial(ctx, newSendConn(t.conn, addr, nil), t.connIDGenerator, t.handlerMap, tlsConf, conf, onClose, true) +} + +func (t *Transport) init(isServer bool) error { + t.initOnce.Do(func() { + getMultiplexer().AddConn(t.Conn) + + conn, err := wrapConn(t.Conn) + if err != nil { + t.initErr = err + return + } + + t.logger = utils.DefaultLogger // TODO: make this configurable + t.conn = conn + t.handlerMap = newPacketHandlerMap(t.StatelessResetKey, t.enqueueClosePacket, t.logger) + t.listening = make(chan struct{}) + + t.closeQueue = make(chan closePacket, 4) + t.statelessResetQueue = make(chan *receivedPacket, 4) + + if t.ConnectionIDGenerator != nil { + t.connIDGenerator = t.ConnectionIDGenerator + t.connIDLen = t.ConnectionIDGenerator.ConnectionIDLen() + } else { + connIDLen := t.ConnectionIDLength + if t.ConnectionIDLength == 0 && (!t.isSingleUse || isServer) { + connIDLen = protocol.DefaultConnectionIDLength + } + t.connIDLen = connIDLen + t.connIDGenerator = &protocol.DefaultConnectionIDGenerator{ConnLen: t.connIDLen} + } + + go t.listen(conn) + go t.runSendQueue() + }) + return t.initErr +} + +func (t *Transport) enqueueClosePacket(p closePacket) { + select { + case t.closeQueue <- p: + default: + // Oops, we're backlogged. + // Just drop the packet, sending CONNECTION_CLOSE copies is best effort anyway. + } +} + +func (t *Transport) runSendQueue() { + for { + select { + case <-t.listening: + return + case p := <-t.closeQueue: + t.conn.WritePacket(p.payload, p.addr, p.info.OOB()) + case p := <-t.statelessResetQueue: + t.sendStatelessReset(p) + } + } +} + +// Close closes the underlying connection and waits until listen has returned. +// It is invalid to start new listeners or connections after that. +func (t *Transport) Close() error { + t.close(errors.New("closing")) + if t.createdConn { + if err := t.conn.Close(); err != nil { + return err + } + } else { + t.conn.SetReadDeadline(time.Now()) + defer func() { t.conn.SetReadDeadline(time.Time{}) }() + } + <-t.listening // wait until listening returns + return nil +} + +func (t *Transport) closeServer() { + t.handlerMap.CloseServer() + t.mutex.Lock() + t.server = nil + if t.isSingleUse { + t.closed = true + } + t.mutex.Unlock() + if t.createdConn { + t.Conn.Close() + } + if t.isSingleUse { + t.conn.SetReadDeadline(time.Now()) + defer func() { t.conn.SetReadDeadline(time.Time{}) }() + <-t.listening // wait until listening returns + } +} + +func (t *Transport) close(e error) { + t.mutex.Lock() + defer t.mutex.Unlock() + if t.closed { + return + } + + t.handlerMap.Close(e) + if t.server != nil { + t.server.setCloseError(e) + } + t.closed = true +} + +// only print warnings about the UDP receive buffer size once +var setBufferWarningOnce sync.Once + +func (t *Transport) listen(conn rawConn) { + defer close(t.listening) + defer getMultiplexer().RemoveConn(t.Conn) + + if err := setReceiveBuffer(t.Conn, t.logger); err != nil { + if !strings.Contains(err.Error(), "use of closed network connection") { + setBufferWarningOnce.Do(func() { + if disable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING")); disable { + return + } + log.Printf("%s. See https://github.com/quic-go/quic-go/wiki/UDP-Receive-Buffer-Size for details.", err) + }) + } + } + if err := setSendBuffer(t.Conn, t.logger); err != nil { + if !strings.Contains(err.Error(), "use of closed network connection") { + setBufferWarningOnce.Do(func() { + if disable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING")); disable { + return + } + log.Printf("%s. See https://github.com/quic-go/quic-go/wiki/UDP-Receive-Buffer-Size for details.", err) + }) + } + } + + for { + p, err := conn.ReadPacket() + //nolint:staticcheck // SA1019 ignore this! + // TODO: This code is used to ignore wsa errors on Windows. + // Since net.Error.Temporary is deprecated as of Go 1.18, we should find a better solution. + // See https://github.com/quic-go/quic-go/issues/1737 for details. + if nerr, ok := err.(net.Error); ok && nerr.Temporary() { + t.mutex.Lock() + closed := t.closed + t.mutex.Unlock() + if closed { + return + } + t.logger.Debugf("Temporary error reading from conn: %w", err) + continue + } + if err != nil { + t.close(err) + return + } + t.handlePacket(p) + } +} + +func (t *Transport) handlePacket(p *receivedPacket) { + connID, err := wire.ParseConnectionID(p.data, t.connIDLen) + if err != nil { + t.logger.Debugf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err) + if t.Tracer != nil { + t.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) + } + p.buffer.MaybeRelease() + return + } + + if isStatelessReset := t.maybeHandleStatelessReset(p.data); isStatelessReset { + return + } + if handler, ok := t.handlerMap.Get(connID); ok { + handler.handlePacket(p) + return + } + if !wire.IsLongHeaderPacket(p.data[0]) { + t.maybeSendStatelessReset(p) + return + } + + t.mutex.Lock() + defer t.mutex.Unlock() + if t.server == nil { // no server set + t.logger.Debugf("received a packet with an unexpected connection ID %s", connID) + return + } + t.server.handlePacket(p) +} + +func (t *Transport) maybeSendStatelessReset(p *receivedPacket) { + if t.StatelessResetKey == nil { + p.buffer.Release() + return + } + + // Don't send a stateless reset in response to very small packets. + // This includes packets that could be stateless resets. + if len(p.data) <= protocol.MinStatelessResetSize { + p.buffer.Release() + return + } + + select { + case t.statelessResetQueue <- p: + default: + // it's fine to not send a stateless reset when we're busy + p.buffer.Release() + } +} + +func (t *Transport) sendStatelessReset(p *receivedPacket) { + defer p.buffer.Release() + + connID, err := wire.ParseConnectionID(p.data, t.connIDLen) + if err != nil { + t.logger.Errorf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err) + return + } + token := t.handlerMap.GetStatelessResetToken(connID) + t.logger.Debugf("Sending stateless reset to %s (connection ID: %s). Token: %#x", p.remoteAddr, connID, token) + data := make([]byte, protocol.MinStatelessResetSize-16, protocol.MinStatelessResetSize) + rand.Read(data) + data[0] = (data[0] & 0x7f) | 0x40 + data = append(data, token[:]...) + if _, err := t.conn.WritePacket(data, p.remoteAddr, p.info.OOB()); err != nil { + t.logger.Debugf("Error sending Stateless Reset to %s: %s", p.remoteAddr, err) + } +} + +func (t *Transport) maybeHandleStatelessReset(data []byte) bool { + // stateless resets are always short header packets + if wire.IsLongHeaderPacket(data[0]) { + return false + } + if len(data) < 17 /* type byte + 16 bytes for the reset token */ { + return false + } + + token := *(*protocol.StatelessResetToken)(data[len(data)-16:]) + if conn, ok := t.handlerMap.GetByResetToken(token); ok { + t.logger.Debugf("Received a stateless reset with token %#x. Closing connection.", token) + go conn.destroy(&StatelessResetError{Token: token}) + return true + } + return false +} diff --git a/vendor/github.com/quic-go/quic-go/zero_rtt_queue.go b/vendor/github.com/quic-go/quic-go/zero_rtt_queue.go deleted file mode 100644 index b81a936e..00000000 --- a/vendor/github.com/quic-go/quic-go/zero_rtt_queue.go +++ /dev/null @@ -1,34 +0,0 @@ -package quic - -import ( - "time" - - "github.com/quic-go/quic-go/internal/protocol" -) - -type zeroRTTQueue struct { - queue []*receivedPacket - retireTimer *time.Timer -} - -var _ packetHandler = &zeroRTTQueue{} - -func (h *zeroRTTQueue) handlePacket(p *receivedPacket) { - if len(h.queue) < protocol.Max0RTTQueueLen { - h.queue = append(h.queue, p) - } -} -func (h *zeroRTTQueue) shutdown() {} -func (h *zeroRTTQueue) destroy(error) {} -func (h *zeroRTTQueue) getPerspective() protocol.Perspective { return protocol.PerspectiveClient } -func (h *zeroRTTQueue) EnqueueAll(sess packetHandler) { - for _, p := range h.queue { - sess.handlePacket(p) - } -} - -func (h *zeroRTTQueue) Clear() { - for _, p := range h.queue { - p.buffer.Release() - } -} diff --git a/vendor/modules.txt b/vendor/modules.txt index df2e00ab..6e969a1b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -46,8 +46,8 @@ github.com/hashicorp/golang-lru/simplelru ## explicit; go 1.12 github.com/hectane/go-acl github.com/hectane/go-acl/api -# github.com/jedisct1/dlog v0.0.0-20230211133026-0338add8743f -## explicit; go 1.17 +# github.com/jedisct1/dlog v0.0.0-20230513092435-93bc2a55ad59 +## explicit; go 1.20 github.com/jedisct1/dlog # github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e ## explicit @@ -55,14 +55,14 @@ github.com/jedisct1/go-clocksmith # github.com/jedisct1/go-dnsstamps v0.0.0-20230211133001-124a632de565 ## explicit; go 1.18 github.com/jedisct1/go-dnsstamps -# github.com/jedisct1/go-hpke-compact v0.0.0-20230211184420-51d4440017ce -## explicit; go 1.19 +# github.com/jedisct1/go-hpke-compact v0.0.0-20230513092519-91c912752223 +## explicit; go 1.20 github.com/jedisct1/go-hpke-compact -# github.com/jedisct1/go-minisign v0.0.0-20230410063418-3d885d71f62c +# github.com/jedisct1/go-minisign v0.0.0-20230513092556-d96eb068239a ## explicit; go 1.20 github.com/jedisct1/go-minisign -# github.com/jedisct1/xsecretbox v0.0.0-20230211185120-2025cdbb9f0f -## explicit; go 1.19 +# github.com/jedisct1/xsecretbox v0.0.0-20230513092623-8c0b2dff5e24 +## explicit; go 1.20 github.com/jedisct1/xsecretbox # github.com/k-sone/critbitgo v1.4.0 ## explicit @@ -112,7 +112,7 @@ github.com/quic-go/qtls-go1-19 # github.com/quic-go/qtls-go1-20 v0.2.2 ## explicit; go 1.20 github.com/quic-go/qtls-go1-20 -# github.com/quic-go/quic-go v0.34.0 +# github.com/quic-go/quic-go v0.35.0 ## explicit; go 1.19 github.com/quic-go/quic-go github.com/quic-go/quic-go/http3