Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enabling 0-RTT for QUIC/H3 clients #387

Merged
merged 5 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cmd/routedns/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ type resolver struct {
Socks5Username string `toml:"socks5-username"`
Socks5Password string `toml:"socks5-password"`
Socks5ResolveLocal bool `toml:"socks5-resolve-local"` // Resolve DNS server address locally (i.e. bootstrap-resolver), not on the SOCK5 proxy

//QUIC and DoH/3 configuration
Use0RTT bool `toml:"enable-0rtt"`
}

// DoH-specific resolver options
Expand Down
3 changes: 3 additions & 0 deletions cmd/routedns/example-config/doh-quic-client.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# DNS-over-HTTPS using the QUIC protocol.
# New connections get initiated with 0-RTT if possible.
# Use0RTT will overwrite the method to GET.

[resolvers.cloudflare-doh-quic]
address = "https://cloudflare-dns.com/dns-query"
protocol = "doh"
transport = "quic"
enable-0rtt = true

[listeners.local-udp]
address = "127.0.0.1:53"
Expand Down
2 changes: 2 additions & 0 deletions cmd/routedns/example-config/doq-client.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# This config starts a UDP and a TCP resolver on the loopback interface for plain DNS.
# All queries are forwarded to a local DNS-over-QUIC server.
# New connections get initiated with 0-RTT if possible.

[resolvers.local-doq]
address = "server.acme.test:8853"
protocol = "doq"
ca = "example-config/server.crt"
bootstrap-address = "127.0.0.1"
enable-0rtt = true

[listeners.local-udp]
address = "127.0.0.1:53"
Expand Down
2 changes: 2 additions & 0 deletions cmd/routedns/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func instantiateResolver(id string, r resolver, resolvers map[string]rdns.Resolv
LocalAddr: net.ParseIP(r.LocalAddr),
TLSConfig: tlsConfig,
QueryTimeout: time.Duration(r.QueryTimeout) * time.Second,
Use0RTT: r.Use0RTT,
}
resolvers[id], err = rdns.NewDoQClient(id, r.Address, opt)
if err != nil {
Expand Down Expand Up @@ -81,6 +82,7 @@ func instantiateResolver(id string, r resolver, resolvers map[string]rdns.Resolv
LocalAddr: net.ParseIP(r.LocalAddr),
QueryTimeout: time.Duration(r.QueryTimeout) * time.Second,
Dialer: socks5DialerFromConfig(r),
Use0RTT: r.Use0RTT,
}
resolvers[id], err = rdns.NewDoHClient(id, r.Address, opt)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions doc/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,7 @@ Example config files: [well-known.toml](../cmd/routedns/example-config/well-know
### DNS-over-HTTPS Resolver

DNS resolvers using the HTTPS protocol are configured with `protocol = "doh"`. By default, DoH uses TCP as transport, but it can also be run over QUIC (UDP) by providing the option `transport = "quic"`. DoH supports two HTTP methods, GET and POST. By default RouteDNS uses the POST method, but can be configured to use GET as well using the option `doh = { method = "GET" }`.
DoH with QUIC supports 0-RTT. The DoH resolver will try to use 0-RTT connection establishment if `transport = "quic"` and `enable-0rtt = true` are configured. When 0-RTT is enabled, the resolver will disregard the configured method and always use GET instead.

Examples:

Expand Down Expand Up @@ -1535,6 +1536,7 @@ DoH resolver using QUIC transport.
address = "https://cloudflare-dns.com/dns-query"
protocol = "doh"
transport = "quic"
enable-0rtt = true
```

Example config files: [well-known.toml](../cmd/routedns/example-config/well-known.toml), [simple-doh.toml](../cmd/routedns/example-config/simple-doh.toml), [mutual-tls-doh-client.toml](../cmd/routedns/example-config/mutual-tls-doh-client.toml)
Expand All @@ -1560,6 +1562,7 @@ Example config files: [dtls-client.toml](../cmd/routedns/example-config/dtls-cli
### DNS-over-QUIC Resolver

Similar to DoT, but uses a QUIC connection as transport as per [RFC9250](https://datatracker.ietf.org/doc/rfc9250/). Configured with `protocol = "doq"`. Note that this is different from DoH over QUIC. See [DNS-over-HTTPS](#DNS-over-HTTPS-Resolver) for how to configure this.
The DoQ resolver will try to use 0-RTT connection establishment if `enable-0rtt = true` is configured.

Examples:

Expand All @@ -1569,6 +1572,7 @@ address = "server.acme.test:8853"
protocol = "doq"
ca = "example-config/server.crt"
bootstrap-address = "127.0.0.1"
enable-0rtt = true
```

Example config files: [doq-client.toml](../cmd/routedns/example-config/doq-client.toml)
Expand Down
15 changes: 14 additions & 1 deletion dohclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type DoHClientOptions struct {

// Optional dialer, e.g. proxy
Dialer Dialer

Use0RTT bool
}

// DoHClient is a DNS-over-HTTP resolver with support fot HTTP/2.
Expand Down Expand Up @@ -85,6 +87,9 @@ func NewDoHClient(id, endpoint string, opt DoHClientOptions) (*DoHClient, error)
if opt.Method == "" {
opt.Method = "POST"
}
if opt.Use0RTT && opt.Transport == "quic" {
opt.Method = "GET"
}
if opt.Method != "POST" && opt.Method != "GET" {
return nil, fmt.Errorf("unsupported method '%s'", opt.Method)
}
Expand Down Expand Up @@ -181,7 +186,12 @@ func (d *DoHClient) ResolveGET(q *dns.Msg) (*dns.Msg, error) {
ctx, cancel := context.WithTimeout(context.Background(), d.opt.QueryTimeout)
defer cancel()

req, err := http.NewRequestWithContext(ctx, "GET", u, nil)
method := http.MethodGet
if d.opt.Use0RTT {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if d.opt.Use0RTT {
if d.opt.Use0RTT && opt.Transport == "quic" {

Should we add this? To handle a mis-configuration where Use0RTT is used but it's not actually quic, but http2

method = http3.MethodGet0RTT
}

req, err := http.NewRequestWithContext(ctx, method, u, nil)
if err != nil {
d.metrics.err.Add("http", 1)
return nil, err
Expand Down Expand Up @@ -268,6 +278,9 @@ func dohQuicTransport(endpoint string, opt DoHClientOptions) (http.RoundTripper,
if err != nil {
return nil, err
}

// enable TLS session caching for session resumption and 0-RTT
tlsConfig.ClientSessionCache = tls.NewLRUClientSessionCache(100)
tlsConfig.ServerName = u.Hostname()
lAddr := net.IPv4zero
if opt.LocalAddr != nil {
Expand Down
7 changes: 7 additions & 0 deletions doqclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type DoQClientOptions struct {
TLSConfig *tls.Config

QueryTimeout time.Duration

Use0RTT bool
}

var _ Resolver = &DoQClient{}
Expand Down Expand Up @@ -77,6 +79,11 @@ func NewDoQClient(id, endpoint string, opt DoQClientOptions) (*DoQClient, error)
// quic-go requires the ServerName be set explicitly
tlsConfig.ServerName = host

// enable TLS session caching for session resumption and 0-RTT
if opt.Use0RTT {
tlsConfig.ClientSessionCache = tls.NewLRUClientSessionCache(100)
}

if opt.QueryTimeout == 0 {
opt.QueryTimeout = defaultQueryTimeout
}
Expand Down
Loading