---
title: DNS プロトコルと解決
url: https://doc.liz6.com/ja/networking/04-DNS/01-dns-protocol-and-resolution
locale: ja
area: networking
tags:
- networking
- DNS
date: 2026-06-30
modified: 2026-07-16
description: インターネットの「電話帳」——ルートサーバーから再帰解決器、権威サーバーまで、1回のDNSクエリで数十回の委譲追跡が発生する可能性があります。edns0、any cast、ネガティブキャッシュなどのメカニズムにより、DNSは1日あたり数兆回のクエリでも利用可能となっています。
---

# DNS プロトコルと解決

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

## 概要

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

## DNS クエリフロー

<svg viewBox="0 0 720 420" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="DNS 再帰クエリの完全なチェーン: stub resolver から authoritative サーバーまで">
  <defs><marker id="dnsq" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker></defs>
  <rect width="720" height="420" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">DNS クエリフロー: stub resolver から authoritative へ</text>

  <rect x="60" y="46" width="600" height="44" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="76" y="64" font-size="12" font-weight="700" fill="#3730a3">① stub resolver (libc / systemd-resolved)</text>
  <text x="76" y="82" font-size="11" fill="#4f46e5">/etc/resolv.conf を読み取る → nameserver (例: 127.0.0.1:53, AdGuard)</text>
  <line x1="360" y1="90" x2="360" y2="100" stroke="#475569" stroke-width="1.7" marker-end="url(#dnsq)"/>

  <rect x="60" y="100" width="600" height="44" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="76" y="118" font-size="12" font-weight="700" fill="#115e59">② recursive resolver (例: AdGuard → upstream DoH/DoT)</text>
  <text x="76" y="136" font-size="11" fill="#0f766e">ローカルキャッシュを確認 → ミス → Root hints から開始</text>
  <line x1="360" y1="144" x2="360" y2="154" stroke="#475569" stroke-width="1.7" marker-end="url(#dnsq)"/>

  <rect x="60" y="154" width="600" height="44" rx="8" fill="#e2e8f0" stroke="#cbd5e1"/>
  <text x="76" y="172" font-size="12" font-weight="700" fill="#334155">③ Root (a–m.root-servers.net)</text>
  <text x="76" y="190" font-size="11" fill="#475569">"com. NS?" を問い合わせ → + グルー: a.gtld-servers.net A 192.5.6.30</text>
  <line x1="360" y1="198" x2="360" y2="208" stroke="#475569" stroke-width="1.7" marker-end="url(#dnsq)"/>

  <rect x="60" y="208" width="600" height="44" rx="8" fill="#e2e8f0" stroke="#cbd5e1"/>
  <text x="76" y="226" font-size="12" font-weight="700" fill="#334155">④ TLD (com)</text>
  <text x="76" y="244" font-size="11" fill="#475569">"example.com NS?" を問い合わせ → + グルー: ns1.example.com A ...</text>
  <line x1="360" y1="252" x2="360" y2="262" stroke="#475569" stroke-width="1.7" marker-end="url(#dnsq)"/>

  <rect x="60" y="262" width="600" height="44" rx="8" fill="#dcfce7" stroke="#4ade80"/>
  <text x="76" y="280" font-size="12" font-weight="700" fill="#166534">⑤ Authoritative (ns1.example.com)</text>
  <text x="76" y="298" font-size="11" fill="#15803d">"www.example.com A?" を問い合わせ → 回答: 93.184.216.34 (+ 任意の RRSIG/NSEC)</text>
  <line x1="360" y1="306" x2="360" y2="316" stroke="#475569" stroke-width="1.7" marker-end="url(#dnsq)"/>

  <rect x="60" y="316" width="600" height="40" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="360" y="340" text-anchor="middle" font-size="12" font-weight="700" fill="#3730a3">⑥ stub resolver → クライアントへ返却</text>

  <rect x="60" y="368" width="600" height="44" rx="7" fill="#eef2ff"/>
  <text x="360" y="386" text-anchor="middle" font-size="12" font-weight="600" fill="#3730a3">再帰解決器はスタブに対して一度の代行 (再帰的): 自ら逐次イテレーティブクエリを発行</text>
  <text x="360" y="402" text-anchor="middle" font-size="12" fill="#3730a3">(ルート → TLD → 権威)、最終的な回答を組み立てて返却。</text>
</svg>

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, ラベル圧縮, ゾーン転送*
