「DNSって、UDPの53番だけ開けておけばいいんですよね?」
実はこれ、よくある誤解です。そして「普段は問題ないのに、一部のドメインだけ引けない」「DNSSECを有効にしたら、ときどき名前解決に失敗する」といった、原因の分かりにくい障害の入口にもなっています。
第3回までは、キャッシュDNSがどう答えを探すかという「流れ」を見てきました。第4回では視点を変えて、DNSのメッセージの中身と、それがUDP・TCPでどう運ばれるかを見ていきます。digの出力が読めるようになるための土台でもあります。
DNSメッセージの構造
DNSの問い合わせと応答は、どちらも同じ形式のDNSメッセージでやり取りされます。

| 部分 | 入るもの |
|---|---|
| ヘッダー(12バイト) | 問い合わせと応答を対応づけるID、フラグ、応答コード(RCODE)、各セクションの件数 |
| 質問 | 問い合わせた名前・タイプ・クラス。応答にもそのまま写される |
| 回答 | 答えのレコード |
| 権威 | 委任先のNSレコード、「存在しない」ときのSOAレコード |
| 追加 | NSのホスト名に対応するグルーのA/AAAAレコード、EDNSのOPT疑似レコード |
digの出力の「QUESTION SECTION」「ANSWER SECTION」などは、この5つの部分をそのまま表示したものです(第8回)。問い合わせでは回答・権威は空で、追加にOPTだけが入ることが多いです。問い合わせも応答も同じ形で、違うのはフラグと各部分の中身です。
ポイント
・メッセージはヘッダー・質問・回答・権威・追加の5部分
・権威には委任先のNSや「無い」ときのSOAが入る
・追加にはグルーやEDNSのOPT疑似レコードが入る
ヘッダーのフラグ
ヘッダーのフラグは、障害の切り分けでとてもよく見る情報です。
| フラグ | 名前 | 意味 | 見るべき場面 |
|---|---|---|---|
| QR | Query/Response | 0=問い合わせ、1=応答 | 応答には必ず付く |
| AA | Authoritative Answer | 権威DNSサーバー自身の答え | キャッシュからの答えか、権威の答えか |
| TC | TrunCation | 応答が入りきらず切り詰めた | TCPでやり直す合図 |
| RD | Recursion Desired | 再帰問い合わせを希望する | スタブリゾルバーは通常1 |
| RA | Recursion Available | 再帰問い合わせを受け付ける | 権威専用サーバーは0 |
| AD | Authentic Data | DNSSECの検証に成功した | 検証するリゾルバーの応答(第9回) |
| CD | Checking Disabled | DNSSECの検証をしないでほしい | 検証失敗の切り分け(dig +cd、Resolve-DnsName -DnssecCd) |
実際のdigの出力で見ると、こうなります。
$ dig www.example.jp # キャッシュDNSに聞く
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
$ dig @ns1.example.jp www.example.jp +norec # 権威DNSに直接聞く
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
キャッシュDNSの答えにはaaが付かず、権威DNSに直接聞くとaaが付きます。「権威の答えと、キャッシュの答えが違う」という切り分けで、最初に見る情報です。flags行の後ろの数字は、各セクションの件数です。
なお、PowerShellのResolve-DnsNameはフラグを表示しないため、フラグを見るときはLinuxなどのdigを使います。
UDPとTCP
DNSは53番ポートを使い、UDPとTCPの両方で通信します。

通常の問い合わせはUDPで行います。1往復で済むため、軽く速いのが利点です。
応答がUDPに入りきらないとき、サーバーはヘッダーのTCビットを立てた応答を返し、クライアントは同じ問い合わせをTCPでやり直します。これをTCPフォールバックと呼びます。ゾーン転送(第7回)もTCPです。
TCPは「たまに使う予備」ではない
ここが冒頭の誤解のポイントです。
- RFC 7766(2016年):すべての汎用的な実装に、UDPとTCPの両方への対応を求める
- RFC 9210(2022年):運用者に、TCPを遮断しないことを求める
ファイアウォールでは、クライアントとキャッシュDNSの間、キャッシュDNSと権威DNSの間の両方で、UDP/53とTCP/53を許可します。片方でもTCP/53を閉じると、大きな応答だけが失敗するという、原因の見えにくい障害になります。
ポイント
・通常はUDP、入りきらなければTCビットを見てTCPでやり直す
・TCP対応は必須(RFC 7766)、遮断しない(RFC 9210)
・両区間でUDP/53とTCP/53を許可する
TCPフォールバックの誕生経緯と「512の壁」
なぜこんな仕組みになっているのか、歴史を少しさかのぼってみます。

- 1983年(RFC 883)、1987年(RFC 1034/1035):DNSはUDPとTCPのどちらかで通信し、UDPメッセージは最大512バイトまでと定めました。512の根拠はRFC 791の「すべてのホストが受け取れるデータグラム576バイト(データ512+ヘッダー64)」で、必ず1パケットで届くサイズを上限にしたのです。
- 1989年(RFC 1123):問い合わせは必ず最初にUDPで送り、応答が切り詰められていたらTCPでやり直す、と定めました。小さなデータを頻繁にやり取りするDNSで、処理の負担を抑えるためです。これがTCPフォールバックです。
- 2010年(RFC 5966):TCP対応を必須にし、「最初にUDP」を「UDPを先に送るべき(SHOULD)」に緩和しました。
- 2016年(RFC 7766、5966を置き換え):TCPから始めてもよい(MAY)と明記しました。2022年のRFC 9210で運用上の要件が追加されています。
「512の壁」
UDPで試して切り詰められてからTCPで出し直すため、1回の名前解決で往復が倍になります。DNSSECやIPv6の普及で応答が大きくなり、この非効率が本格的に問題になりました。これが「512の壁」です。そこで登場したのが、次に説明するEDNS(0)です。
実際に体験してみることもできます。+noednsで512バイトの制限に戻すと、大きなTXTやMXの応答でTCPフォールバックが起きます。
$ dig +noedns example.jp TXT
;; Truncated, retrying in TCP mode.
(中略)
;; SERVER: 192.0.2.53#53(192.0.2.53) (TCP)
PowerShellではResolve-DnsName -TcpOnlyで最初からTCPで問い合わせられます。パケットキャプチャでも、TCビットの立ったUDP応答の直後にTCPの接続が始まる様子を確認できます。
EDNS(0)とOPT疑似レコード
EDNS(0)(RFC 6891)は、追加セクションのOPT疑似レコードでDNSを拡張する仕組みです。

「このサイズまでUDPで受け取れる」と申告すると、サーバーは512バイトを超えてもUDPで返します。面白いのは、既存のレコードの枠を借りて、拡張の情報を運んでいる点です。
| OPTのフィールド | 通常のレコードでの意味 | OPTでの使い方 |
|---|---|---|
| NAME | 名前 | ルート(空) |
| TYPE | タイプ | 41(OPT) |
| CLASS | クラス(IN) | 受け取れるUDPの大きさ(例:1232バイト) |
| TTL | キャッシュする時間 | 拡張RCODE・EDNSのバージョン(0)・DOビット(DNSSECの要求) |
| RDATA | 値 | オプション(COOKIE、EDE、NSID、Client Subnet、Padding 等) |
digでは、次のように表示されます。
$ dig www.example.jp
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
2019年2月1日のDNS Flag Day 2019で、主要なリゾルバーはEDNSに正しく応答しないサーバーへの回避策を取りやめました。つまり、EDNSをまともに扱えない古い権威DNSサーバーは、この時点で名前解決に失敗するようになっています。なお、Resolve-DnsNameではOPTの中身は表示されません。
フラグメントとDNS Flag Day 2020
EDNS(0)で大きなUDP応答を送れるようになると、今度はIPフラグメントが問題になりました。

経路のMTU(イーサネットで通常1500バイト、VPNなどではもっと小さい)を超えるUDPの応答は、IPのレベルで分割されます。2つ目以降の断片にはUDPのヘッダーが無いため、ファイアウォールやNATで落とされやすく、断片の偽造によるキャッシュポイズニングの手口も知られています。
そこで2020年10月1日のDNS Flag Day 2020では、EDNSのバッファーサイズを1232バイトにすることが推奨されました。
1232の計算
1280(IPv6の最小MTU)− 40(IPv6ヘッダー)− 8(UDPヘッダー)= 1232
この大きさなら、ほぼすべての経路で分割されずに届きます。入りきらない応答はTCPで運ぶため、TCP/53の許可が前提です。BINDやUnbound(1.12.0以降)は既定値を1232にしています。
ポイント
・大きなUDPはIPで分割され、断片が落とされやすい
・推奨バッファーは1232バイト(1280−48、Flag Day 2020)
・入りきらない応答はTCPで運ぶ。TCP/53の許可が前提
ファイアウォールと経路の機器で起きる問題
ここまでの話を、現場で見かける症状に落とし込むと次のようになります。
| 症状 | よくある原因 | 対策 |
|---|---|---|
| 大きな応答(DNSSEC、長いTXT、多数のMX)だけ失敗する | TCP/53を閉じている | UDP/53とTCP/53の両方を許可する |
| 512バイトを超えるUDP応答だけ届かない | 古い機器のDNS検査の上限が512バイトのまま(例:Cisco ASAの message-length maximum 512) | EDNSの申告値に合わせる設定へ変更(ASAの message-length maximum client auto 等) |
| 特定のドメインだけ、ときどきタイムアウトする | 分割されたUDPの断片を落としている | バッファーを1232にし、TCPを許可する |
| FORMERRが返る、EDNSが通らない | ルーター等のDNSプロキシ・ALGがOPTを理解しない、削除する | DNSプロキシ・ALGを無効にする、機器を更新する |
| 戻りの応答が落ちる | 戻りの通信を送信元ポート53固定で許可している | 送信元ポートはランダム(RFC 5452)。ステートフルに許可する |
ファイアウォールでUDPの応答が落ちると、クライアントには応答が返らずタイムアウトになります。REFUSEDなどの応答コードが返るのは、DNSサーバー自身が応答したときだけです。
経路の対応状況は、dig +bufsize=1232と+bufsize=4096の比較(Windows標準のコマンドには無い機能のため、Linuxなどで行う)や、ISCのEDNS Compliance Tester(ednscomp.isc.org)で確かめられます。
DNS Cookies
UDPは接続の手順が無いため、送信元のIPアドレスを偽った問い合わせや、偽の応答を送り込まれやすいという弱点があります。DNS Cookies(RFC 7873、2016年)は、EDNSのオプションでこの弱点を軽く補う仕組みです。

- クライアントは問い合わせにクライアントCookie(8バイト)を付ける
- サーバーは応答にサーバーCookieを付けて返す
- 次からは両方を付けて送る
これにより、サーバーは「以前に本当にこのアドレスとやり取りした相手か」を、クライアントは「応答が自分の問い合わせへの本物の応答か」を確かめられます。送信元を偽った反射攻撃や、経路外からの偽の応答を減らせます。
RFC 9018(2021年)はサーバーCookieの作り方を統一し、複数の製品を混ぜたエニーキャスト(第7回)でも使えるようにしました。digでは; COOKIE:の行で確認できます。
応答コード(RCODE)
RCODEは、障害対応で最初に見る情報です。
| 値 | 名前 | 意味 | よくある原因 |
|---|---|---|---|
| 0 | NOERROR | 正常に処理した | 答えが0件でもNOERROR(下のNODATA) |
| 1 | FORMERR | 問い合わせの形式を解釈できない | EDNSのOPTを理解できない古いサーバー・DNSプロキシ |
| 2 | SERVFAIL | サーバー側で処理に失敗した | 権威DNSに到達できない、DNSSECの検証失敗 |
| 3 | NXDOMAIN | 名前そのものが存在しない | 名前の誤り、未登録、問い合わせ先の誤り |
| 4 | NOTIMP | 要求された機能を実装していない | 対応していない種類の問い合わせ |
| 5 | REFUSED | ポリシーにより拒否した | ACLで拒否、権威DNSに担当外の名前を聞いた |
| (なし) | NODATA | RCODEではなく状態の呼び名 | 名前はあるが、そのタイプが無い(NOERROR・回答0件) |
特にNODATAはRCODEではない点に注意してください。RCODEはNOERRORで、回答が0件、権威セクションにSOAが入る状態を指します。「NOERRORなのに答えがない」ときは、名前はあるけれどそのタイプのレコードが無い、ということです。
なお、6〜10(YXDOMAIN等)は動的更新(第7回)で使い、16以上はEDNSで拡張されたコードです。応答の型ごとのdigの見え方は第8回で扱います。
拡張エラー(EDE)
SERVFAILは「サーバー側で何か失敗した」としか言っていないので、それだけでは原因が分かりません。そこで登場したのがEDE(Extended DNS Errors、RFC 8914、2020年)です。SERVFAILなどの理由を、EDNSのオプションで返します。
| INFO-CODE | 名前 | 意味 |
|---|---|---|
| 3 | Stale Answer | 期限切れのキャッシュで答えた(serve-stale) |
| 6 | DNSSEC Bogus | DNSSECの検証に失敗した |
| 7 | Signature Expired | 署名の有効期限が切れている |
| 9 | DNSKEY Missing | DSに対応するDNSKEYが無い |
| 15/17 | Blocked/Filtered | リゾルバーのポリシーで遮断した |
| 18 | Prohibited | 問い合わせ元が許可されていない |
| 22 | No Reachable Authority | 権威DNSサーバーに到達できない |
| 23 | Network Error | 権威DNSとの通信でエラーが起きた |
検証用のドメインdnssec-failed.orgを公開リゾルバーに問い合わせると、次のようにEDEが返ってきます(抜粋)。
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 34492
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
; EDE: 9 (DNSKEY Missing): (no SEP matching the DS found for dnssec-failed.org.)
これなら、原因が「DNSSECの失敗」なのか「権威に届かない」のかを、応答だけで見分けられます。SERVFAILを見たら、まずEDEを確認する習慣をつけましょう。
考えてみよう
- あなたの環境のファイアウォールで、DNSのTCP/53は許可されていますか。クライアント⇔キャッシュDNS、キャッシュDNS⇔権威DNSのそれぞれで確認してみましょう。
- 「一部のドメインだけ、ときどき名前解決に時間がかかる」と報告を受けました。この記事の内容から、どんな原因が考えられますか。
- 社内のキャッシュDNSがSERVFAILを返したとき、原因の手がかりを増やすには、どの情報(フラグ、EDE、別のリゾルバーの結果)を見ればよいでしょうか。
ヒント:dig +bufsize=512、+tcp、+norec、+cdのようにオプションを変えて結果を比べると、どの段階で落ちているかが見えてきます。PowerShellでは-TcpOnly、-NoRecursion、-DnssecCdが同じ役割です。
まとめ
- DNSメッセージはヘッダー・質問・回答・権威・追加の5部分。digの表示はそのまま対応している
- aaは権威の答え、TCはTCPでやり直す合図、adはDNSSEC検証成功
- DNSはTCPも必須。両区間でUDP/53とTCP/53を許可する
- EDNSのバッファーは1232バイト。超える分はTCPで運ぶ
- NODATAはRCODEではない。SERVFAILを見たらEDEで理由を確かめる
次回からは、メッセージの中身であるリソースレコードの種類と書き方を、前編・後編の2回に分けて見ていきます。
連載「DNS入門」全12回
- DNSとは?名前解決の全体像と3つの登場人物
- ドメイン名の階層と委任
- 名前解決の流れ
- DNSメッセージとトランスポート(この記事)
- リソースレコード 前編
- リソースレコード 後編
- 権威DNSサーバーの運用
- コマンドで調べる
- DNSのセキュリティ
- クラウド・社内環境のDNS
- DNSトラブルシュート
- DNS設計演習


コメント