Skip to content

Commit 9337954

Browse files
committed
DoQ
1 parent 48fdea2 commit 9337954

File tree

7 files changed

+603
-3
lines changed

7 files changed

+603
-3
lines changed

README.md

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
[![Rust](https://img.shields.io/badge/rust-%23000000.svg?style=flat&logo=rust&logoColor=white)](https://www.rust-lang.org/)
55
[![Crates.io](https://img.shields.io/crates/v/doh-proxy.svg)](https://crates.io/crates/doh-proxy)
66

7-
A fast and secure DoH (DNS-over-HTTPS) and ODoH (Oblivious DoH) server.
7+
A fast and secure DoH (DNS-over-HTTPS), DoQ (DNS-over-QUIC), and ODoH (Oblivious DoH) server.
88

9-
`doh-proxy` is written in Rust, and has been battle-tested in production since February 2018. It doesn't do DNS resolution on its own, but can sit in front of any DNS resolver in order to augment it with DoH support.
9+
`doh-proxy` is written in Rust, and has been battle-tested in production since February 2018. It doesn't do DNS resolution on its own, but can sit in front of any DNS resolver in order to augment it with DoH, DoQ, and ODoH support.
1010

1111
## Table of Contents
1212

@@ -56,6 +56,7 @@ A fast and secure DoH (DNS-over-HTTPS) and ODoH (Oblivious DoH) server.
5656
## Features
5757

5858
- **DNS-over-HTTPS (DoH)** - Encrypts DNS queries using HTTPS
59+
- **DNS-over-QUIC (DoQ)** - Encrypts DNS queries using QUIC protocol (RFC 9250)
5960
- **JSON API Support** - Compatible with Google DNS-over-HTTPS JSON API format
6061
- **Oblivious DoH (ODoH)** - Provides additional privacy by hiding client IP addresses
6162
- **EDNS Client Subnet** - Forward client IP information to upstream resolvers for geo-optimized responses
@@ -139,6 +140,9 @@ OPTIONS:
139140
--enable-ecs Enable EDNS Client Subnet
140141
--ecs-prefix-v4 <ecs_prefix_v4> IPv4 prefix length for EDNS Client Subnet [default: 24]
141142
--ecs-prefix-v6 <ecs_prefix_v6> IPv6 prefix length for EDNS Client Subnet [default: 56]
143+
--enable-doq Enable DNS-over-QUIC (DoQ) server on UDP port 853
144+
--doq-port <doq_port> UDP port for DNS-over-QUIC server [default: 853]
145+
--doq-idle-timeout <doq_idle_timeout> Idle timeout for DoQ connections in seconds [default: 30]
142146
```
143147

144148
### Example Configurations
@@ -170,6 +174,17 @@ doh-proxy -H 'doh.example.com' \
170174
doh-proxy -H 'doh.example.com' -u 127.0.0.1:53 -l 127.0.0.1:3000
171175
```
172176

177+
**With DNS-over-QUIC (DoQ) support:**
178+
```sh
179+
doh-proxy -H 'doh.example.com' \
180+
-u 127.0.0.1:53 \
181+
-i /path/to/cert.pem \
182+
-I /path/to/key.pem \
183+
--enable-doq \
184+
--doq-port 853
185+
```
186+
This enables both DoH on HTTPS port and DoQ on UDP port 853.
187+
173188
## Deployment Architectures
174189

175190
### Behind a Reverse Proxy (Recommended)
@@ -409,6 +424,71 @@ curl -H "X-Forwarded-For: 1.2.3.4" \
409424

410425
For maximum privacy, avoid enabling ECS or use larger prefix values (smaller subnets) like /8 for IPv4 or /32 for IPv6.
411426

427+
## DNS-over-QUIC (DoQ)
428+
429+
### Overview
430+
431+
DNS-over-QUIC (DoQ) is specified in RFC 9250 and provides DNS transport over the QUIC protocol. It offers similar privacy properties to DoH but with improved performance characteristics:
432+
433+
- **Zero Round-Trip Time (0-RTT)**: Faster connection establishment for returning clients
434+
- **No Head-of-Line Blocking**: Independent stream delivery prevents one slow query from blocking others
435+
- **Connection Migration**: Maintains connections even when client IP addresses change
436+
- **Lower Latency**: QUIC's efficient packet loss recovery and congestion control
437+
438+
### Configuration
439+
440+
Enable DoQ support with the following command-line options:
441+
442+
- `--enable-doq` - Enable DNS-over-QUIC server
443+
- `--doq-port <port>` - UDP port for DoQ (default: 853)
444+
- `--doq-idle-timeout <seconds>` - Connection idle timeout (default: 30)
445+
446+
### Examples
447+
448+
**Basic DoQ setup:**
449+
```sh
450+
doh-proxy -H 'dns.example.com' \
451+
-u 127.0.0.1:53 \
452+
-i /path/to/cert.pem \
453+
-I /path/to/key.pem \
454+
--enable-doq
455+
```
456+
457+
**DoQ with custom port and timeout:**
458+
```sh
459+
doh-proxy -H 'dns.example.com' \
460+
-u 127.0.0.1:53 \
461+
-i /path/to/cert.pem \
462+
-I /path/to/key.pem \
463+
--enable-doq \
464+
--doq-port 8853 \
465+
--doq-idle-timeout 60
466+
```
467+
468+
**Combined DoH and DoQ:**
469+
```sh
470+
doh-proxy -H 'dns.example.com' \
471+
-u 127.0.0.1:53 \
472+
-l 0.0.0.0:443 \
473+
-i /path/to/cert.pem \
474+
-I /path/to/key.pem \
475+
--enable-doq
476+
```
477+
This configuration serves DoH on TCP port 443 and DoQ on UDP port 853.
478+
479+
### Client Configuration
480+
481+
DoQ clients can connect using:
482+
- Protocol: DNS-over-QUIC (RFC 9250)
483+
- Port: UDP 853 (default)
484+
- ALPN: "doq"
485+
486+
### Requirements
487+
488+
- TLS certificates are required (same certificates used for DoH)
489+
- UDP port 853 must be accessible (or custom port if configured)
490+
- QUIC uses UDP, ensure firewalls allow UDP traffic
491+
412492
## Oblivious DoH (ODoH)
413493

414494
Oblivious DoH is similar to Anonymized DNSCrypt, but for DoH. It requires relays, but also upstream DoH servers that support the protocol.

src/config.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,26 @@ pub fn parse_opts(globals: &mut Globals) {
169169
.num_args(1)
170170
.default_value("56")
171171
.help("EDNS Client Subnet prefix length for IPv6 addresses"),
172+
)
173+
.arg(
174+
Arg::new("enable_doq")
175+
.long("enable-doq")
176+
.action(SetTrue)
177+
.help("Enable DNS-over-QUIC (DoQ) server on UDP port 853"),
178+
)
179+
.arg(
180+
Arg::new("doq_port")
181+
.long("doq-port")
182+
.num_args(1)
183+
.default_value("853")
184+
.help("UDP port for DNS-over-QUIC server"),
185+
)
186+
.arg(
187+
Arg::new("doq_idle_timeout")
188+
.long("doq-idle-timeout")
189+
.num_args(1)
190+
.default_value("30")
191+
.help("Idle timeout for DoQ connections in seconds"),
172192
);
173193

174194
#[cfg(feature = "tls")]
@@ -299,6 +319,7 @@ pub fn parse_opts(globals: &mut Globals) {
299319
globals.disable_post = matches.get_flag("disable_post");
300320
globals.allow_odoh_post = matches.get_flag("allow_odoh_post");
301321
globals.enable_ecs = matches.get_flag("enable_ecs");
322+
globals.enable_doq = matches.get_flag("enable_doq");
302323

303324
// Parse ECS prefix lengths
304325
let ecs_prefix_v4_str = matches
@@ -321,6 +342,27 @@ pub fn parse_opts(globals: &mut Globals) {
321342
))
322343
});
323344

345+
// Parse DoQ configuration
346+
let doq_port_str = matches
347+
.get_one::<String>("doq_port")
348+
.expect("doq_port has a default value");
349+
globals.doq_port = doq_port_str.parse().unwrap_or_else(|e| {
350+
exit_with_error(&format!(
351+
"Invalid DoQ port '{}': {}",
352+
doq_port_str, e
353+
))
354+
});
355+
356+
let doq_idle_timeout_str = matches
357+
.get_one::<String>("doq_idle_timeout")
358+
.expect("doq_idle_timeout has a default value");
359+
globals.doq_idle_timeout = doq_idle_timeout_str.parse().unwrap_or_else(|e| {
360+
exit_with_error(&format!(
361+
"Invalid DoQ idle timeout '{}': {}",
362+
doq_idle_timeout_str, e
363+
))
364+
});
365+
324366
#[cfg(feature = "tls")]
325367
{
326368
globals.tls_cert_path = matches

src/libdoh/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ tokio-rustls = { version = "^0.24.1", features = [
4343
"early-data",
4444
], optional = true }
4545
rustls-pemfile = "^1.0.4"
46+
quiche = { version = "0.21", features = ["boringssl-vendored"] }
47+
ring = "0.17"
48+
mio = { version = "1.0", features = ["net", "os-poll"] }
4649

4750
[profile.release]
4851
codegen-units = 1

0 commit comments

Comments
 (0)