5 分で読了 #networking #DNS
このページの目次

DNS プロトコルと解決

インターネットの「電話帳」——ルートサーバーから再帰解決器、権威サーバーまで、1回のDNSクエリで数十回の委譲追跡が発生する可能性があります。edns0、any cast、ネガティブキャッシュなどのメカニズムにより、DNSは1日あたり数兆回のクエリでも利用可能となっています。

概要

DNSはインターネットの「電話帳」——人間が読みやすいドメイン名をIPアドレスに変換します。1983年に設計され(RFC 882/883)、分散型かつ階層化されたアーキテクチャによりインターネット規模へのスケーラビリティを実現しています:ルートからTLD、権威サーバーまで、各層は自らの名前空間のみを担当します。再帰解決器は「重い作業」——スタブクライアントに代わって、ルートから回答までの一連のプロセスを完了します。DNSは元々セキュリティ設計のないプロトコル(UDP平文)でしたが、後のDNSSECや暗号化転送(DoT/DoH/DoQ)はこの欠点を補完するために導入されました。

DNS クエリフロー

DNS クエリフロー: stub resolver から authoritative へ ① stub resolver (libc / systemd-resolved) /etc/resolv.conf を読み取る → nameserver (例: 127.0.0.1:53, AdGuard) ② recursive resolver (例: AdGuard → upstream DoH/DoT) ローカルキャッシュを確認 → ミス → Root hints から開始 ③ Root (a–m.root-servers.net) "com. NS?" を問い合わせ → + グルー: a.gtld-servers.net A 192.5.6.30 ④ TLD (com) "example.com NS?" を問い合わせ → + グルー: ns1.example.com A ... ⑤ Authoritative (ns1.example.com) "www.example.com A?" を問い合わせ → 回答: 93.184.216.34 (+ 任意の RRSIG/NSEC) ⑥ stub resolver → クライアントへ返却 再帰解決器はスタブに対して一度の代行 (再帰的): 自ら逐次イテレーティブクエリを発行 (ルート → TLD → 権威)、最終的な回答を組み立てて返却。

3つのクエリタイプ:

  • Recursive (RD=1): 解決器がクライアントに代わって全程再帰 (stub → recursive)
  • Iterative: 1回で referral または answer を返す (recursive → authoritative)
  • Inverse: PTR クエリ (IP → name、ほとんど使用されない)

DNS ワイヤーフォーマット

DNS メッセージ (TCP または UDP):

ヘッダー (12 バイト):
  トランザクションID (2B): ランダム、リクエスト ⇔ 応答のマッチング用
  フラグ (2B):
    QR (bit 0): 0=Query, 1=Response
    OPCODE (bits 1-4): 0=Standard, 1=Inverse, 2=Status, 4=Notify, 5=Update
    AA (bit 5): 権威回答 (Authoritative Answer)
    TC (bit 6): 切り捨て (TCP を使用)
    RD (bit 7): 再帰要求 (Recursion Desired)
    RA (bit 8): 再帰利用可能 (Recursion Available)
    Z (bits 9-11): 0 (予約済み)
    RCODE (bits 12-15): 0=NoError, 1=FormErr, 2=SrvFail, 3=NXDomain, 5=Refused
  QDCOUNT (2B): クエリ数
  ANCOUNT (2B): 回答数
  NSCOUNT (2B): 権威レコード数
  ARCOUNT (2B): 追加レコード数

Question Section (可変長):
  QNAME: ラベル形式 (以下参照)
  QTYPE (2B): 1=A, 28=AAAA, 5=CNAME, 15=MX, 2=NS, 6=SOA, 33=SRV, ...
  QCLASS (2B): 1=IN (Internet)

Answer/Authority/Additional Sections (可変長):
  各 RR:
    NAME: ラベル形式 (通常圧縮済み — 以前の名称へのポインタ)
    TYPE (2B)
    CLASS (2B): 1=IN
    TTL (4B): キャッシュ有効期間 (秒)
    RDLENGTH (2B): RDATA 長
    RDATA: 型固有

ラベル形式と圧縮

ラベルエンコーディング:
  [長さ 1B][ラベルデータ ...] → 長さ + データ、各ラベルは独立
  最後のラベル: length=0 (ルート、空ラベル)

  例: www.example.com → 0x03 'w' 'w' 'w' 0x07 'e' 'x' 'a' 'm' 'p' 'l' 'e' 0x03 'c' 'o' 'm' 0x00

DNS 圧縮 (スペース節約):
  長さバイトの上位 2 bits = 11 → これはポインタ (ラベルではない!)
  ポインタ = 下位 14 bits のオフセット (DNS メッセージ先頭からの相対位置)
  例: 0xC0 0x0C → オフセット 12 を指す (最初の question の QNAME)

  これにより、同じメッセージ内で以前出現したドメイン名を繰り返し参照可能
  → "example.com" が QNAME、A レコードの NAME、NS レコードの RDATA に出現 → 3回の参照 → 約 12+12+12 = 36 バイト節約

主要なレコードタイプ

A (type 1):  RDATA = 4バイト IPv4 アドレス
AAAA (type 28): RDATA = 16バイト IPv6 アドレス
CNAME (type 5): RDATA = 正式名称 (ラベル形式) — エイリアス解決
  → 制限: CNAME は他のタイプと共存できない (RRSIG/NSEC を除く)
    つまり: www.example.com が CNAME の場合、A レコードを同時に持つことはできない
MX (type 15): RDATA = 優先度 (2B) + 交換先 (ラベル形式)
  → 優先度: 低い優先度値 = 高い優先度
SOA (type 6): RDATA = mname + rname + serial + refresh + retry + expire + minimum TTL
  → ゾーンの権威情報: serial はゾーン転送のバージョン比較に使用
NS (type 2): RDATA = ネームサーバー (ラベル形式) — このゾーンを権威するネームサーバー
SRV (type 33): RDATA = 優先度 + 重み + ポート + ターゲット
  → _service._proto.name → 対象ホスト:ポート
  例: _sip._tcp.example.com SRV 10 60 5060 sipserver.example.com
PTR (type 12): RDATA = 名前 (ラベル形式) — 逆引き (IP → 名前)
  → IPv4 は in-addr.arpa、IPv6 は ip6.arpa
TXT (type 16): RDATA = 1つ以上のテキスト文字列 (長さプレフィックス付き)
  → SPF, DKIM, DMARC、認証トークン
CAA (type 257): RDATA = フラグ + タグ + 値
  → どの CA がこのドメインの証明書を発行できるかを制限
  → 例: 0 issue "letsencrypt.org" → Let's Encrypt のみが署名可能

EDNS0 (RFC 6891)

従来の DNS の制限: UDP ペイロード最大 512B → 大きな応答 (DNSSEC RRSIG) には TCP フォールバックが必要

EDNS0: Additional section に OPT擬似RRを追加:
  NAME=0 (ルート), TYPE=41 (OPT), CLASS=UDP ペイロードサイズ, TTL=フラグ+RCODE

  UDP ペイロードサイズ: 受信側が処理できる最大 UDP 応答サイズを通知 (通常 1232 または 4096)
  → TCP フォールバックの回数を削減
  DNSSEC OK (DO bit): クライアントが DNSSEC をサポート → サーバーは RRSIG を付与すべき

  フラグ: DNSSEC OK (DO bit 15)

AFXR (ゾーン転送)

AXFR (完全ゾーン転送):
  主に使用される: primary → secondary ネームサーバー間の同期

  TCP 接続 (ゾーン転送は 512B を超える可能性がある):
    secondary: AXFR リクエスト (type 252, class IN) → primary
    primary: ゾーン内の全 RR を返す (SOA ... SOA、先頭と末尾に SOA)

IXFR (増分ゾーン転送、RFC 1995):
  変更分のみ転送 (SOA serial に基づく)

参考

  • RFC: 1034, 1035, 6891, 3596 (AAAA), 2782 (SRV), 6844 (CAA)
  • ツール⁠: dig +trace, drill, kdig, delv, tcpdump -nn port 53
  • ソースコード⁠: glibc resolv/, systemd-resolved

キーワード: DNS, A/AAAA/MX/NS/SOA/SRV/CNAME/TXT/CAA, 再帰解決器, EDNS0, ラベル圧縮, ゾーン転送