DNSの問い合わせ・委任・キャッシュ
DNSの各欄から、次の問い合わせ先と回答の再利用条件を確認します。
説明
DNSの名前解決では、端末から問い合わせを受けたリゾルバーが、必要に応じて委任先をたどり回答を取得します。応答には最終的な答えだけでなく、担当するサーバーやそのIPが入る場合もあります。
www.example.testの問い合わせをバイト列で確認し、委任のNSとglue、最終回答のAをPCAPで追います。名前圧縮とTTLを解析して、回答の位置とキャッシュを再利用できる時間を確認します。
1問い合わせのバイト列
www.example.testのAレコードを問い合わせるDNSメッセージを表示します。UDP/IPヘッダーは含みません。
xxd -g 1 -c 16問い合わせの構造 query.binDNSメッセージ先頭12バイトがDNSヘッダーです。ID0x1234、flags0x0100、質問数1、その後に長さ付きのラベルが並びます。03 www、07 example、04 test、00が名前の終端で、最後の0001・0001がQTYPE AとQCLASS INです。
2ヘッダーのIDと再帰要求
最初の12バイトを六つの16ビット値として復号します。
python3 -cDNSヘッダーを復号 'from pathlib import Path; import struct; q=Path("query.bin").read_bytes(); values=struct.unpack("!6H",q[:12]); print(values); print(hex(values[0]),bool(values[1]&0x100),values[2:])'ID・RD・各欄の個数ID0x1234、RDが1、質問1、各応答欄0です。RDは再帰処理を望むフラグであり、この問い合わせが権威サーバーへ直接送られたことを表すものではありません。端末が再帰リゾルバーへ結果の取得を依頼する場面で使います。
3委任をたどる経路
delegation.csvは教材用のDNS階層です。再帰リゾルバーが必要な委任をまだ保持していない条件の流れを確認します。
cat delegation.csv問い合わせ先と次の委任先ルートからtest、testからexample.testへ進み、最後に担当する権威からwwwのAを得ます。毎回必ずルートから問い合わせるわけではなく、使える委任や回答がキャッシュにあれば途中を省けます。
4委任応答のNSとglue
referral.pcapはexample.testへの委任応答です。
tcpdump -n -tt -vvv応答の三つの欄 -r referral.pcapNSとglueの記録Answerは0、Authorityにexample.testのNS、Additionalにns.example.testのAがあります。glueは、委任先サーバー名を引くために同じ委任先へ行かなければならない循環を避ける手掛かりです。wwwのIPを答えている欄ではありません。
5再帰リゾルバーからの最終回答
answer.pcapには端末へ返すAレコードの回答があります。
tcpdump -n -tt -vvvID・フラグ・TTL・A -r answer.pcap端末への回答ID4660は0x1234と同じで、www.example.testのAは192.0.2.80、TTL60秒です。QRは応答、RAは再帰機能の提供を示し、この回答ではAAは0です。リゾルバーが返す回答と、その名前の権威が直接返す回答を区別して読みます。
6名前圧縮の参照先
回答の名前はc00cという2バイトのポインターで、メッセージ先頭から12バイト目の質問名を参照します。
python3 -c圧縮ポインターを復号 'from pathlib import Path; q=Path("query.bin").read_bytes(); a=Path("answer.bin").read_bytes(); pointer=int.from_bytes(a[len(q):len(q)+2],"big"); print(hex(pointer),pointer>>14,pointer&0x3fff,a[pointer&0x3fff])'参照の種別と12バイト目先頭2ビットが3、残り14ビットが12です。12バイト目はwwwの長さ3で、同じ名前の文字列を繰り返さず参照しています。参照先はIPアドレスの位置ではなく、DNSメッセージ内の名前の位置です。
7TTLの残りと再問い合わせ
取得時刻を0秒、TTL60秒とし、経過20秒と61秒で再利用できるかを計算します。プリフェッチや期限切れ回答の利用は扱わないモデルです。
python3 -c有効期限と再利用の条件 'expires=60; print([(now,max(0,expires-now),"cache" if now<expires else "query authority") for now in (20,61)])'残り秒数と問い合わせ先20秒後は残り40秒でキャッシュを使い、61秒後にはAの回答を更新する必要があります。その時点でも委任のキャッシュが有効なら、ルートから全部やり直す必要はありません。TTLはIPパケットのホップ制限とは別の値です。
まとめ
Question・Answer・Authority・Additionalには異なる役割の情報が入ります。
委任と回答のキャッシュには期限があり、使える情報によって問い合わせの開始点が変わります。
操作履歴と解説を振り返れます。
シェル / PCAPと通信の記録
xxd -g 1 -c 16 query.bin00000000: 12 34 01 00 00 01 00 00 00 00 00 00 03 77 77 77 .4...........www 00000010: 07 65 78 61 6d 70 6c 65 04 74 65 73 74 00 00 01 .example.test... 00000020: 00 01 ..
python3 -c 'from pathlib import Path; import struct; q=Path("query.bin").read_bytes(); values=struct.unpack("!6H",q[:12]); print(values); print(hex(values[0]),bool(values[1]&0x100),values[2:])'(4660, 256, 1, 0, 0, 0) 0x1234 True (1, 0, 0, 0)
cat delegation.csvSTEP,ASKED,ANSWER,NEXT 1,root-for-lab,test NS,192.0.2.52 2,test authority,example.test NS and glue,192.0.2.54 3,example.test authority,www.example.test A,192.0.2.80
tcpdump -n -tt -vvv -r referral.pcapreading from file referral.pcap, link-type EN10MB (Ethernet)
0.000000 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 134)
192.0.2.52.53 > 192.0.2.53.53000: [udp sum ok] 4660- q: A? www.example.test. 0/1/1 ns: example.test. [5m] NS ns.example.test. ar: ns.example.test. [5m] A 192.0.2.54 (106)tcpdump -n -tt -vvv -r answer.pcapreading from file answer.pcap, link-type EN10MB (Ethernet)
0.000000 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 78)
192.0.2.53.53 > 192.0.2.10.53000: [udp sum ok] 4660 q: A? www.example.test. 1/0/0 www.example.test. [1m] A 192.0.2.80 (50)python3 -c 'from pathlib import Path; q=Path("query.bin").read_bytes(); a=Path("answer.bin").read_bytes(); pointer=int.from_bytes(a[len(q):len(q)+2],"big"); print(hex(pointer),pointer>>14,pointer&0x3fff,a[pointer&0x3fff])'0xc00c 3 12 3
python3 -c 'expires=60; print([(now,max(0,expires-now),"cache" if now<expires else "query authority") for now in (20,61)])'[(20, 40, 'cache'), (61, 0, 'query authority')]
演習完了