DNS検索コマンドdugの紹介
2024年08月21日 水曜日
CONTENTS
技術研究所開発室の山本(和)です。私は、同僚の日比野と一緒にDNSのフルリゾルバ(キャッシュサーバ)の実装を進めています。このフルリゾルバの名前は bowline
と言います。結びの王様である「もやい結び」から名付けました。
サーバの bowline
に加えて、検索コマンドである dug
も実装しています。DNSに詳しい方なら分かると思いますが、 dig
コマンドに似せた名前を選びました。(もちろん、DIGDUGという名作を意識しています。)
今回は、 DNS検索コマンド dug
の使い方について説明します。dugコマンドをインストールする一番簡単な方法は、githubからバイナリをダウンロードすることです。Intel Linux 用と Arm macOS 用のバイナリを配布しています。
また、プログラミング言語Haskellで書かれたbowlineとdugは、dnsextという名前のリポジトリでソースコードが公開されています。
2つのモード
dug
には、2つのモードがあります。
1つは、 dig
コマンドのようにスタブリゾルバとして動作するモードです。ドメイン名とリソースレコード(以下 RR)名を与えると、(再帰検索要求付きで)フルリゾルバから対応する値を得ようと試みます。
% dug www.iij.ad.jp a
...
www.iij.ad.jp. 300(5 mins) IN A 202.232.2.180
...
この記事での ...
は、読みやすさのために、余分な文字列を削除したことを表します。
このモードの特徴として、以下が挙げられます。
- パイプラインを使って複数の要求を一括で送信できる
- DNS over QUIC や DNS over HTTP/3 など、さまざまなトランスポートに対応している
- SVCB(Service Binding) RR から自動的にトランスポートを選択できる
- TLS や QUIC のコネクションの再開や 0-RTT に対応している
もう1つのモードは、 bowline
の反復検索アルゴリズムを使って、ルートドメインから対象のドメインまでの解決過程を可視化します。キャッシュサーバではないので、答えが得られたら終わりで、キャッシュする訳ではありません。
基本的な検索
前述のように、ドメイン名と RR名を与えるのが、dug
の基本的な使い方です。現時点で、RR名には以下が使えます。
a | ns | cname | soa | null | ptr | mx | txt | rp | aaaa | srv | dname | opt | ds | rrsig | nsec | dnskey | nsec3 | nsec3param | tlsa | cds | cdnskey | csync | svcb | https | axfr | any | caa
RR名を省略すると、a
を指定したことになります。
また、RR名に対応する値を知っているなら、type
という文字列に続けて数字を指定する方法もあります。以下は、AAAA RRの値である 28 を指定する例です。
% dug www.iij.ad.jp type28
;; 10.131.16.20#53/UDP, Tx:42bytes, Rx:70bytes
...
www.iij.ad.jp. 166(2 mins) IN AAAA 2001:240:bb81::10:180
...
このドメイン名とRR名に加えて、要求のフラグも制御できますが、今回は説明を割愛します。
ドメイン名のところにIPアドレスを指定すると、IPアドレスだと自動判別して、逆引きをします。
% dug 2001:300::5
...
5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.3.0.1.0.0.2.ip6.arpa. 86400(1 day) IN PTR "public00.dns.iij.jp."
...
サーバの指定
@
を付けると、問い合わせるフルリゾルバを指定できます。以下は、フルリゾルバに 1.1.1.1 (Cloudflare) を指定する例です。
% dug @1.1.1.1 www.iij.ad.jp aaaa
;; 1.1.1.1#53/UDP, Tx:42bytes, Rx:70bytes
...
www.iij.ad.jp. 300(5 mins) IN AAAA 2001:240:bb81::10:180
...
;; 38usec
サーバを指定しない場合は、 /etc/resolv.conf
などから得られたシステムが指定するフルリゾルバを使います。
サーバは複数指定できます。その場合、早く帰ってきた答えが表示されます。
% dug @8.8.8.8 @1.1.1.1 www.iij.ad.jp aaaa
;; 1.1.1.1#53/UDP, Tx:42bytes, Rx:70bytes
...
www.iij.ad.jp. 204(3 mins) IN AAAA 2001:240:bb81::10:180
...
;; 25usec
この例では、フルリゾルバとして 8.8.8.8 (Google)と 1.1.1.1 を指定した結果、1.1.1.1 の方が早く答えを返しています。
パイプライン
ドメイン名と RR名を複数繰り返すと、パイプラインを使って答えを得ようとします。パイプラインとは、クライアントが複数の要求を一度に送信し、サーバは答えが得られたものから応答を返す仕組みです。
% dug www.iij.ad.jp aaaa www.mew.org aaaa
;; 10.131.16.20#53/UDP, Tx:40bytes, Rx:68bytes
...
www.mew.org. 270(4 mins) IN AAAA 2001:240:bb82:2706::1:59
...
;; 10.131.16.20#53/UDP, Tx:42bytes, Rx:70bytes
...
www.iij.ad.jp. 109(1 min) IN AAAA 2001:240:bb81::10:180
...
;; 70usec
パイプラインの場合も複数のフルリゾルバを指定することもできます。結果は、常に早い者勝ちのルールで表示されます。
% dug @1.1.1.1 @8.8.8.8 www.mew.org aaaa www.iij.ad.jp aaaa
;; 1.1.1.1#53/UDP, Tx:40bytes, Rx:68bytes
...
www.mew.org. 3600(1 hour) IN AAAA 2001:240:bb82:2706::1:59
...
;; 8.8.8.8#53/UDP, Tx:42bytes, Rx:70bytes
...
www.iij.ad.jp. 74(1 min) IN AAAA 2001:240:bb81::10:180
...
;; 65usec
ちょっと、すごいと思ってきましたか?
トランスポートの選択
トランスポートを指定するには、 -d
オプションに以下のキーワードを指定します。
- 無指定 – DNS over UDP
tcp
– DNS over TCPdot
– DNS over TLS (over TCP)doq
– DNS over QUIC (over UDP)h2
– DNS over HTTP/2 over TLS (over TCP)h2c
– DNS over HTTP/2 (over TCP)h3
– DNS over HTTP/3 (over QUIC over UDP)
せっかくなので、DNS over HTTP/3 を試してみましょう。僕が知る限り HTTP/3 をサポートしているパブリックDNSサーバは、94.140.14.140(AdGuard)のみです。
% dug @94.140.14.140 -d h3 www.iij.ad.jp
;; 94.140.14.140#443/H3, Tx:42bytes, Rx:58bytes
...
www.iij.ad.jp. 300(5 mins) IN A 202.232.2.180
...
;; 508usec
94.140.14.140#443/H3: v1(FullHandshake)
応答の一行目と最後に、H3
の文字列が表示されているのが分かると思います。v1は、QUICのバージョンです。FullHandshakeは、(セッションの再開でなく)新規にセッションが作られたという意味です。
SVCB
フルリゾルバがどのトランスポートを使って欲しいかは、SVCB RR を使って宣言します。その SVCB RR を検索するには、スタブリゾルバは “_dns.resolver.arpa” という特殊なドメイン名を指定します。
% dug @1.1.1.1 _dns.resolver.arpa svcb -f multi
;; 1.1.1.1#53/UDP, Tx:47bytes, Rx:358bytes
...
_dns.resolver.arpa. 300(5 mins) IN SVCB RD_SVCB {
svcb_priority = 1
, svcb_target = "one.one.one.one."
, svcb_params = {
alpn=["h2","h3"]
, port=443
, ipv4hint=[1.1.1.1,1.0.0.1]
, ipv6hint=[2606:4700:4700::1111,2606:4700:4700::1001]
, dohpath="/dns-query{?dns}"}}
...
この例では、出力の書式を複数行にするオプション -f multi
を使って、結果を見やすくしています。SVCB RR の詳細は説明しませんが、HTTP/2 と HTTP/3 が指定されているのが分かるでしょう。
SVCB RRを検索し、値を解析して適切なトランスポートを自動的に選択するのには、 -d auto
オプションを使用します。dugは、最も優先順位の高いサーバすべてに、同時に要求を出します。
dug @1.1.1.1 -d auto www.iij.ad.jp
;; 1.1.1.1#443/H2, Tx:42bytes, Rx:58bytes
...
www.iij.ad.jp. 21(secs) IN A 202.232.2.180
...
1.1.1.1に対して HTTP/2が選ばれているのが分かります。
なお、IPv6 での Cloudflare の DNS サービスは、 DNS over TLS に対応しているものの、DNS over HTTP/2 や DNS over HTTP/3 には対応していないようです。それにも関わらず、SVCB RR で対応していると宣言しています。
この例では、dugは 2つのIPv4サーバ、および、2つのIPv6サーバに同時に要求を出しています。IPv4サーバからの応答が早ければ、それが表示されます。IPv6からは HTTP の 200 以外のコードが返ってきて、それを受け取った時点で OperationRefused
と表示し終了します。
2024/08/29修正:CloudflareのIPv6サービスに対して検索が失敗するのはdugのバグのせいでした。CloudflareのIPv6サービスには問題はありません。訂正してお詫びします。バグが修正されたdugのバイナリをリリースしておきました
コネクションの再開
下層に TLS か QUIC を利用するトランスポートでは、セッションの再開が利用できます。セッション情報を格納するファイルは -R
オプションで指定します。以下は IPv4 アドレスと IPv6 アドレスをそれぞれ2つずつ持つ public.dns.iij.jp に対して、TLS でアクセスし、セッション情報を保存する例です。
% dug @public.dns.iij.jp -d tls -R session www.mew.org
...
103.2.57.5#853/TLS: v1.3(FullHandshake), Next(Resumption:OK, 0-RTT:NG)
103.2.57.6#853/TLS: v1.3(FullHandshake), Next(Resumption:OK, 0-RTT:NG)
2001:300::5#853/TLS: v1.3(FullHandshake), Next(Resumption:OK, 0-RTT:NG)
2001:300::6#853/TLS: v1.3(FullHandshake), Next(Resumption:OK, 0-RTT:NG)
-R
オプションは、空のファイルが指定されると、単にセッション情報を書き込みます。中にセッション情報がある場合は、それを使ってセッションを再開し、新しいセッション情報でファイルを上書きします。
セッションを再開するために、もう一度 -R
オプション付きで、 dug
を使ってみましょう。
% dug @public.dns.iij.jp -d tls -R session www.mew.org
...
103.2.57.5#853/TLS: v1.3(Resumption)
103.2.57.6#853/TLS: v1.3(Resumption)
2001:300::5#853/TLS: v1.3(Resumption)
2001:300::6#853/TLS: v1.3(Resumption)
セッションの再開(Resumption)を利用していることが分かります。なお、セッションの再開の目的は、主にサーバ認証の省略であって、応答時間の短縮ではないことにご注意ください。
0-RTT
応答時間の短縮が目的である 0-RTT を使ってみましょう。まず、-R
でセッション情報を保存保存するように指示しながら、8.8.8.8に対して検索します。
% dug @8.8.8.8 -d h2 www.iij.ad.jp -R session
...
;; 123usec
8.8.8.8#443/H2: v1.3(FullHandshake), Next(Resumption:OK, 0-RTT:OK)
0-RTT:OK
と表示されているので、次のセッションの再開時に 0-RTT を利用できます。0-RTT を利用するには、-R
オプションに加えて、-Z
オプションを指定します。
% dug @8.8.8.8 -d h2 www.iij.ad.jp -R session -Z
...
;; 88usec
8.8.8.8#443/H2: v1.3(0-RTT), Next(Resumption:OK, 0-RTT:OK)
前回は、新規セッションを意味するFullHandshakeと表示されていた部分が、0-RTT(セッションの再開時に0-RTTを利用)に変わっていることが分かります。
反復検索
反復検索のモードを使うには、-i
オプションを指定します。以下の例では、表示される情報の量を調整するために -v
オプションも付けています。0から3までの数字が指定でき、数字が大きくなるにつれて、表示量が増えます。
また、DNSの検証を省略するために、+cdflag
(Check Disabled) オプションも指定しています。問い合わせを制御するオプションは、+
から始まり、ドメイン名とRR名の後に書く必要があります。
% dug -i -v2 www.iij.ad.jp +cdflag
...
root-priming: verification success - RRSIG of NS: "."
"a.root-servers.net." 198.41.0.4#53 ...
...
"m.root-servers.net." 202.12.27.33#53 ...
iterative: query "jp." A
query "jp." A to 198.41.0.4#53/UDP
query "jp." A to 202.12.27.33#53/UDP
query "jp." A to 202.12.27.33#53/UDP: win
delegation - no DS, check disabled: "." -> "jp."
zone: "jp.":
...
"c.dns.jp." 156.154.100.5#53 ...
"d.dns.jp." ... 2001:240::53#53
...
"f.dns.jp." ... 2001:2f8:0:100::153#53
"g.dns.jp." 203.119.40.1#53
...
iterative: query "ad.jp." A
query "ad.jp." A to 156.154.100.5#53/UDP
query "ad.jp." A to 2001:240::53#53/UDP
query "ad.jp." A to 2001:240::53#53/UDP: win
no delegation: "jp." -> "ad.jp."
iterative: query "iij.ad.jp." A
query "iij.ad.jp." A to 203.119.40.1#53/UDP
query "iij.ad.jp." A to 2001:2f8:0:100::153#53/UDP
query "iij.ad.jp." A to 203.119.40.1#53/UDP: win
delegation - no DS, check disabled: "jp." -> "iij.ad.jp."
zone: "iij.ad.jp.":
"dns0.iij.ad.jp." 210.130.0.5#53 ...
"dns1.iij.ad.jp." ... 2001:240::115#53
iterative: query "www.iij.ad.jp." A
query "www.iij.ad.jp." A to 210.130.0.5#53/UDP
query "www.iij.ad.jp." A to 2001:240::115#53/UDP
query "www.iij.ad.jp." A to 210.130.0.5#53/UDP: win
...
www.iij.ad.jp. 300(5 mins) IN A 202.232.2.180
...
デフォルトでは、問い合わせにIPv6も用います。IPv4に限定したいなら、-4
オプションを付けてください。
おわりに
なにか要望がありましたら、githubのissuesに登録してください。日本語で構いません。