dig の全てのコマンドラインオプションを一覧にしたシートを作成しました

## 概要
筆者は [DNS Summer Day 2023](https://dnsops.jp/event20230623.html) で「あたらしい dig」というテーマで発表を行いました(資料は[こちら](https://dnsops.jp/event/20230623/20230623-akamai-v2.pdf))。

DNS のテストツールである dig コマンドは、ネットワークエンジニアのみなさんが日常的に利用していると思います。

一方で、dig を用いているとたまに想定とは異なる結果が得られ、戸惑うことがあります。
原因としては、dig の送信するリクエストメッセージに関するデフォルト値が一般的な感覚と異なるために起きることが多いようです。

発表ではこれらの具体的な例を挙げつつ、もし dig のいくつかのコマンドラインオプションの存在やそのデフォルト値の知識があったならば、それらはすぐに解決したであろうことを示しました。

dig には非常に多くのコマンドラインオプションがあります。しかし、man ページや -h オプションで表示される簡易ヘルプではコマンドラインオプションが機能で分類されておらずアルファベット順で記載されており、全体を見渡すことが困難です。そこで、コマンドラインオプションを機能別に分類してみた、というお話もしました。

この記事では発表内容をふり返りながら、改めてコマンドラインオプションを整理した[シート](https://github.com/y01m/dig-options/blob/main/dig-options-a4.pdf)も紹介します。

## 「あたらしい dig 」というタイトル

dig コマンドはオープンソースの DNS ソフトウェアである BIND に付属する DNS のテストツールであり、広く使われています。特に、DNS に関連した問題が発生した時などに、それを dig の実行結果で表現することがよくあります。

たとえば、「server.example.com に host.example.com という CNAME が設定されている」と日本語で表現された場合、server と host のどちらが CNAME のオーナーで、どちらが CNAME の値でしょうか。どちらにもとることができそうです。

一方、下記のように dig の実行結果を示すことで、server の方が CNAME のオーナーであり、host の方がその値であることが明確になります。

```plaintext:dig の例
$ dig @ns.example.com +norec server.example.com
(略)
;; ANSWER SECTION:
server.example.com.  3600  IN CNAME host.example.com.
(略)
```

また、実行されたコマンドラインが示されているので、これが ns.example.com に問い合わせて得られた結果であるということも表現されています。

私は dig が「DNS のふるまいを示す標準言語」としての役割を担っていると考えました。

私が小学生の時の国語の教科書のタイトルは「あたらしい国語」でした。dig についてお互いがよく知ることによって、より正確なコミュニケーションをとるためのリテラシーを向上できるのでは、という気持ちを込めて「あたらしい dig」と題して発表ではお話しました。

## dig を利用していて戸惑う場面

冒頭に書いたように、dig が期待と異なる結果を返すことで戸惑う場面がしばしばあります。

### バージョンによって動作が異なる例 (DNS Cookie)

ある同僚から、「dig でエラーが返ってくるとお客様から報告を受けたのに、同じコマンドを実行してみたところ問題なくアンサーが返ってくる」という相談を受けました。そこで、よく調べてみるとお客様の使っている dig と、彼の使っている dig ではバージョンが異なっていました。DNS のプロトコルには EDNS と呼ばれる拡張があり、クエリーに追加の情報を載せることができるようになっていますが、新しいバージョンの dig ではデフォルトで旧バージョンにはなかった DNS Cookie と呼ばれる EDNS オプションのついたクエリーを行っていることが原因で結果が異なったということがわかりました。
対象のサーバーのソフトウェアが非常に古く、未知の EDNS オプションを受け取るとエラーを返していたのです。

dig コマンドは応答メッセージの情報は詳細に表示しますが、自らの送信したリクエストに関する情報は省略されており、上記の例のようにデフォルトで EDNS オプションがついていること等には気づきにくいと言えます。

実は dig に +qr というオプションをつけて実行することにより、リクエストの情報もレスポンスの情報と同様に詳細に表示することができます。

```;plaintext:+qr オプションの例。「Got answer」の前の「Sending:」以降が追加で表示されたリクエスト。DNS Cookie がついていることがわかる。
% dig +qr example.com @8.8.8.8

; <<>> DiG 9.18.18 <<>> +qr example.com
;; global options: +cmd
;; Sending:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16043
;; flags: rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: 074e622b02f61792
;; QUESTION SECTION:
;example.com.			IN	A

;; QUERY SIZE: 52

;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16043
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
(略)
```

さて、DNS Cookie の有無が結果の違いの原因であったことが分かりましたので、動作を合わせてみることにします。新しいバージョンの dig でも +nocookie オプションをつけることで DNS Cookie をつけずにリクエストできますし、+noedns オプションをつけることにより、EDNS そのものを使わないようにもできます。

相談を受けたケースでは、+[no]qr、+[no]cookie、+[no]edns という 3 つのオプションを知っていれば、はじめから違いの原因に気づいて自力で解決できたかもしれません。

### デフォルトの動作が期待と異なる例 (ANY クエリーにおける TCP)

ANY タイプのクエリーの応答は大きくなることが多いため、DNS を使った UDP リフレクション DDoS 攻撃でしばしば悪用されます。ただし、サーバー側は応答が 512 バイト(もしくは EDNS で指定された UDP バッファサイズ) を超えるような場合には　TC (truncate) フラグを立てた応答を返すことによりリクエスト元に TCP での再試行を促します。

それを知ったうえで、このような TC 応答の動作を確認する目的で UDP では返せないような大きな応答が生成されるはずの ANY タイプクエリーを dig を実行してみると、想定に反してあたかも巨大な応答が UDP で返ってきたかのように表示されて戸惑うことになります。
実は dig は ANY タイプのクエリーに関してはデフォルトで UDP でなく最初から TCP でクエリーを行ないます。
そのことが分かった時点で、次に ANY タイプであっても明示的に UDP でクエリーを行うようにと dig に +notcp オプションをつけて実行してみた場合はどうでしょうか。それでも同様に UDP で応答が返ってきたかのように巨大な応答が表示されてしまいます。
dig は UDP で TC フラグの立った応答が返ると、その応答に関しては表示せずに TCP で再問い合わせを行い、その結果だけを表示します。+notcp に加え、+ignore オプションをつけることにより、初めて UDP で返された TC フラグの立った応答を表示することができます。

これらについては dig の man ページをよく読むと書いてあるのですが、コマンドの返した表示から気づくことは難しいでしょう。

## 全てのコマンドラインオプションをマスターしたい

何度かこういう経験をすると、より多くのコマンドラインオプションを知って dig を使いこなしたい、いっそのことならば全てマスターしたい、と思うようになります。
ところが、dig にはおよそ 100 くらいのコマンドラインオプションがあるにもかかわらず、man ページではアルファベット順に説明されているだけで、体系的に整理されているわけではありません。順番に読んでいこうとしてもどうにも頭に入ってきません。

### コマンドラインオプションを整理

そこで、まずは種類別に分けてみることにしました。当初はPCの画面でテキストエディタ上で並べ替えていこうとしましたが、闇雲な作業で何種類に分けたらいいのかもわからず、なかなか進みませんでした。

そこで、アナログにコマンドラインオプションを一つ一つカードにしてみたら全体像が見えるのではないか、ということでカード状にして机の上に広げて作業してみたところ、何とかそれらしく分類することができました。
![dig-option-cards.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3664893/80d55189-6d3f-08a4-8bbe-a610aa30bec8.jpeg)

この作業で得られた結果をもとに、DNS Summer Day の発表では以下のように分類し、数ページのスライドにまとめました。

- トランスポート ( L3 / L4 / DoH / DoT )
- 再送、タイムアウト、フォールバック
- クエリの内容の指定 ( 一般 / TSIG / EDNS / 特殊な指定の仕方 )
- 表示
- その他

本当は 1 枚にまとめたかったのですが、イベントで投影するスライドでは約100もあるコマンドラインオプションを一枚に詰め込むのは厳しいと判断して断念しました。

この発表に関しては、想像以上に反響があり「助かる」という声までいただきました。そこで、あらためて一枚のシートにまとめ、各オプションの説明とデフォルト値を加えたものを作成しました(分類も見直して一部変更しています)。
2024 年 1 月に福岡で開催された JANOG Meeting 53 では、このシートをクリアファイルに印刷したものを弊社のブースで配布しましたが、とくに告知をしていたわけでもないのにどこで知ったのかこのクリアファイルを求めてブースを訪れてくれる方もいらっしゃいました。そこで、今回こちらで PDF 形式でダウンロードできるようにしました。手元に置いてぜひご活用ください。

[<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3664893/2adf77a9-fe23-59a5-ec0a-db654bc69bbb.png"><br>dig のコマンドラインオプションのシート(A4)](https://raw.githubusercontent.com/y01m/dig-options/main/dig-options-a4.pdf)

## 知っておくと便利なオプションの例

このように整理することで、全てのコマンドラインオプションを学び、実際に試してみると、多くの気づきがありました。そこで、先に説明した +qr オプションに加え、知っておくと便利なオプションを以下にいくつかピックアップしてみます。

### 部分の選択

dig の表示は端末のコンソールでは数十行になってしまいます。
アンサーの中の値だけを表示する +short というオプションがありますが、CNAME を辿る場合に CNAME の値とその先の最終的な値が並列されて区別なく表示される等、これはこれで情報が不足しすぎていると感じることもあります。
デフォルトでは全ての部分が表示されますので、+noall で一旦全ての表示をオフにし、さらに +answer をつけることにより、answer に該当する情報だけが表示されます。

```plaintext:+short と +noall +answer の比較
% dig +short www.example.com
host.example.com.
192.0.2.1
% dig +noall +answer www.example.com
www.example.com.		3600	IN	CNAME	host.example.com.
host.example.com. 300	IN	A	192.0.2.1
% 
```
+answer の他にも特定の部分の表示に対応したオプションがあるため、必要な部分だけを表示することができます。

### +yaml オプション

DNS Summer Day での発表後に反響が大きかったものの一つがこちらです。dig の出力は独特であり、スクリプトで読み込んでパースするのに苦労します。+yaml オプションをつけることで、YAML 形式で表示することができます。スクリプト処理に適するのはもちろんですが、インデントで構造化され、また key-value 形式で示されるためそれぞれ表示されているものが何の値なのか分かるようになっており、人の目でもこちらの方が見やすくわかりやすいように思います。

```;plaintext:+yaml オプションの例
% dig +noedns +yaml example.com
-
  type: MESSAGE
  message:
    type: RECURSIVE_RESPONSE
    query_time: !!timestamp 2024-06-07T03:00:00.000Z
    response_time: !!timestamp 2024-06-07T03:00:00.006Z
    message_size: 45b
    socket_family: INET
    socket_protocol: UDP
    response_address: "203.0.113.1"
    response_port: 53
    query_address: "0.0.0.0"
    query_port: 0
    response_message_data:
      opcode: QUERY
      status: NOERROR
      id: 27491
      flags: qr rd ra ad
      QUESTION: 1
      ANSWER: 1
      AUTHORITY: 0
      ADDITIONAL: 0
      QUESTION_SECTION:
        - example.com. IN A
      ANSWER_SECTION:
        - example.com. 2316 IN A 192.0.2.1
%
```

JSON 形式のデータを処理するために [jq](https://jqlang.github.io/jq/) というツールを使い慣れている方も多いと思いますが、[yq](https://github.com/mikefarah/yq) というツールは jq のラッパーとして jq と同様に YAML を扱うことができます。これを使うと自在に dig +yaml の出力を操作できます。yq は YAML だけでなく XML などの入力にも対応しているので、インストールしておくと便利です。

### +nssearch オプション

+nssearch は他のほとんどのオプションと異なり、複数のクエリーを発生させます。
まず、与えられたドメイン名に関する NS タイプのクエリーを行い、その結果得られた各ホストの各アドレスに向けて一斉に SOA タイプのクエリーを行います。その応答が早く帰ってきたものから順に、結果を所要時間(RTT)とともに表示します。
ここで表示されたシリアル番号が複数のホスト間で一致しなければゾーン転送による同期ができていないことが考えられます。また、応答がない等の異常も表示されます。
ゾーン名を指定して実行することにより簡易的に問題の発見につなげることができます。

```;plaintext:+nssearch の例
% dig +nssearch example.com
SOA ns.example.com. admin.example.com. 2024060110 7200 3600 1209600 3600 from server 192.0.2.53 in 11 ms.
SOA ns.example.com. admin.example.com. 2024060110 7200 3600 1209600 3600 from server 203.0.113.53 in 14 ms.
SOA ns.example.com. admin.example.com. 2024060110 7200 3600 1209600 3600 from server 2001:db8:8d::53 in 19 ms.
SOA ns.example.com. admin.example.com. 2024060110 7200 3600 1209600 3600 from server 2001:db8:8f::53 in 20 ms.
%
```


## 最後に
今年の [DNS Summer Day 2024](https://dnsops.jp/event20240621.html) では、DNSViz というツールについてお話しすることになりました。みなさんとお会いできるのを楽しみにしています。