DNS検索コマンドdugの紹介

2024年08月21日 水曜日


【この記事を書いた人】
山本 和彦

Haskellコミュニティでは、ネットワーク関連を担当。 4児の父であり、家庭では子供たちと、ジョギング、サッカー、スキー、釣り、クワガタ採集をして過ごす。

「DNS検索コマンドdugの紹介」のイメージ

技術研究所開発室の山本(和)です。私は、同僚の日比野と一緒に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 TCP
  • dot – 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に登録してください。日本語で構いません。

山本 和彦

2024年08月21日 水曜日

Haskellコミュニティでは、ネットワーク関連を担当。 4児の父であり、家庭では子供たちと、ジョギング、サッカー、スキー、釣り、クワガタ採集をして過ごす。

Related
関連記事