はじめに
ブラウザにおいてクライアントレベルでDoHが使用可能であり、FireFoxにおいてはデフォルトでDoHを使用(いまのところ米国ユーザのみ)する設定だったりします。DNSリクエストに対する抗トラッキングというかプライバシー保護の意識が高まっているのではないでしょうか。
そのような中、QUICが2021年5月にRFC-9000として標準化されました。
これでQUICをトランスポート層プロトコルに用いるHTTP/3も近いうちに標準化されるのではないかと思われます。
そこで現在のDoHのトランスポートをQUICに置き換えたDoH3(DNS-over-HTTP/3)をやっていきます。
調べたところAdguardTeamのdnsproxyではDoH、DoQに対応しておりDoH3への改修もしやすい実装になっていましたのでDoH3化していきます。
dnsproxyのDoH3、IPv6パッチ
dnsproxyですが現時点最新のv0.39.0ではDoH3(DNS-over-HTTP/3)は対応していないので、DoH部分のHTTP/3対応化を行い、またIPv6推進のためにDNSサーバへはIPv6のみで接続します。
・DNSリゾルバーへの接続はDoH3(DNS-over-HTTP/3)のみで行うようになります
・DNSレスポンスの順はIPv6が(あれば)先になります
パッチのライセンス
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Copyright 2021 logicalmixed Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. |
dnsproxy-0.39.0-v6doh3.patch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
diff -u -r dnsproxy-0.39.0/proxy/lookup.go dnsproxy-0.39.0-v6doh3/proxy/lookup.go --- dnsproxy-0.39.0/proxy/lookup.go 2021-07-28 10:03:06.000000000 +0900 +++ dnsproxy-0.39.0-v6doh3/proxy/lookup.go 2021-07-28 11:50:00.000000000 +0900 @@ -39,8 +39,8 @@ } ch := make(chan *lookupResult) - go p.lookupIPAddr(host, dns.TypeA, ch) go p.lookupIPAddr(host, dns.TypeAAAA, ch) + go p.lookupIPAddr(host, dns.TypeA, ch) var ipAddrs []net.IPAddr var errs []error @@ -68,5 +68,6 @@ return []net.IPAddr{}, errs[0] } - return proxyutil.SortIPAddrs(ipAddrs), nil + return ipAddrs, nil + //return proxyutil.SortIPAddrs(ipAddrs), nil } diff -u -r dnsproxy-0.39.0/upstream/bootstrap.go dnsproxy-0.39.0-v6doh3/upstream/bootstrap.go --- dnsproxy-0.39.0/upstream/bootstrap.go 2021-07-28 10:03:06.000000000 +0900 +++ dnsproxy-0.39.0-v6doh3/upstream/bootstrap.go 2021-07-28 11:48:33.000000000 +0900 @@ -211,6 +211,7 @@ func (n *bootstrapper) createDialContext(addresses []string) (dialContext dialHandler) { dialer := &net.Dialer{ Timeout: n.options.Timeout, + DualStack: true, } dialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { diff -u -r dnsproxy-0.39.0/upstream/bootstrap_resolver.go dnsproxy-0.39.0-v6doh3/upstream/bootstrap_resolver.go --- dnsproxy-0.39.0/upstream/bootstrap_resolver.go 2021-07-28 10:03:06.000000000 +0900 +++ dnsproxy-0.39.0-v6doh3/upstream/bootstrap_resolver.go 2021-07-28 11:49:30.000000000 +0900 @@ -156,8 +156,8 @@ } ch := make(chan *resultError) - go r.resolve(host, dns.TypeA, ch) go r.resolve(host, dns.TypeAAAA, ch) + go r.resolve(host, dns.TypeA, ch) var ipAddrs []net.IPAddr var errs []error @@ -183,5 +183,6 @@ return []net.IPAddr{}, errs[0] } - return proxyutil.SortIPAddrs(ipAddrs), nil + return ipAddrs, nil + //return proxyutil.SortIPAddrs(ipAddrs), nil } diff -u -r dnsproxy-0.39.0/upstream/upstream_doh.go dnsproxy-0.39.0-v6doh3/upstream/upstream_doh.go --- dnsproxy-0.39.0/upstream/upstream_doh.go 2021-07-28 10:03:06.000000000 +0900 +++ dnsproxy-0.39.0-v6doh3/upstream/upstream_doh.go 2021-07-28 11:47:30.000000000 +0900 @@ -11,7 +11,10 @@ "github.com/joomcode/errorx" "github.com/miekg/dns" - "golang.org/x/net/http2" + //"golang.org/x/net/http2" + + "github.com/lucas-clemente/quic-go" + "github.com/lucas-clemente/quic-go/http3" ) // dnsOverHTTPS represents DNS-over-HTTPS upstream. @@ -137,23 +140,24 @@ // createTransport initializes an HTTP transport that will be used specifically // for this DOH resolver. This HTTP transport ensures that the HTTP requests // will be sent exactly to the IP address got from the bootstrap resolver. -func (p *dnsOverHTTPS) createTransport() (*http.Transport, error) { +func (p *dnsOverHTTPS) createTransport() (*http3.RoundTripper, error) { tlsConfig, dialContext, err := p.boot.get() if err != nil { return nil, errorx.Decorate(err, "couldn't bootstrap %s", p.boot.URL) } - transport := &http.Transport{ - TLSClientConfig: tlsConfig, + if dialContext!=nil {} + + var qconf quic.Config + qconf.KeepAlive = true + qconf.MaxIncomingStreams = -1 + transport := &http3.RoundTripper{ + TLSClientConfig: tlsConfig, + QuicConfig: &qconf, DisableCompression: true, - DialContext: dialContext, } // It appears that this is important to explicitly configure transport to use HTTP2 // Relevant issue: https://github.com/AdguardTeam/dnsproxy/issues/11 - _, err = http2.ConfigureTransports(transport) - if err != nil { - return nil, err - } return transport, nil } diff -u -r dnsproxy-0.39.0/vendor/github.com/lucas-clemente/quic-go/client.go dnsproxy-0.39.0-v6doh3/vendor/github.com/lucas-clemente/quic-go/client.go --- dnsproxy-0.39.0/vendor/github.com/lucas-clemente/quic-go/client.go 2021-07-28 10:03:07.000000000 +0900 +++ dnsproxy-0.39.0-v6doh3/vendor/github.com/lucas-clemente/quic-go/client.go 2021-07-28 11:42:19.000000000 +0900 @@ -106,11 +106,11 @@ config *Config, use0RTT bool, ) (quicSession, error) { - udpAddr, err := net.ResolveUDPAddr("udp", addr) + udpAddr, err := net.ResolveUDPAddr("udp6", addr) if err != nil { return nil, err } - udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) + udpConn, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0}) if err != nil { return nil, err } |
dnsproxyのビルド
任意のディレクトリで下記を実行しビルドします。
1 2 3 4 5 6 7 8 9 10 |
git clone -b v0.39.0 --depth 1 https://github.com/AdguardTeam/dnsproxy.git ./dnsproxy-0.39.0-v6doh3 cd dnsproxy-0.39.0-v6doh3 rm -r vendor/github.com/lucas-clemente/quic-go git clone -b v0.21.2 --depth 1 https://github.com/lucas-clemente/quic-go.git vendor/github.com/lucas-clemente/quic-go/ git clone -b v0.2.1 --depth 1 https://github.com/marten-seemann/qpack.git vendor/github.com/marten-seemann/qpack/ patch -p1 -d ./ < ../dnsproxy-0.39.0-v6doh3.patch go build -mod=vendor |
dnsproxy動作確認
(1) dnsproxyを立ち上げ
DoH3での接続先として確認できたDNSリゾルバーを載せます。接続先に応じたオプションでdnsproxyを立ち上げます。
- cloudflare
1./dnsproxy -u https://cloudflare-dns.com/dns-query -l ::1 -p 5053 -b 2606:4700:4700::1111 -v - google
1./dnsproxy -u https://dns.google/dns-query -l ::1 -p 5053 -b 2606:4700:4700::1111 -v
*ここではIPv6のループバックアドレスでlistenしていますが、IPv4アドレスも指定可能です
*接続先へはIPv6でのみ接続します
(2) ドメインを引いて確認
1 |
dig yahoo.com @::1 -p 5053 |
ここではdigコマンドでIPが引けるかで確認しましたが、tcpdumpやwiresharkなどでQUICでの接続も確認しておくと良いかもしれません。
あとがき
HTTP/3のメリットというよりQUICの恩恵ですがQUICでの接続は接続元のIP変動(含むポート変動)に影響されないので、IPv6でのテンポラリアドレス(時間などで変動)や動的IP(固定IP契約でないPPPoE、DS-LITE、MAP-Eなど)の環境下でIPが変わったタイミングでの切断を気にしないで済みそうです。
またキャッシュされていない任意のドメインについてDoH3、DoHでそれぞれQuery timeをdigで確認してました。数回確認しましたがQuery timeはDoH3(約380msec)、DoH(約520msec)となり、DoH3の方が140msec±5msecほど高速になりました。DoHをDoH3に置き換えることで25%以上の高速化が望めるようです。