DNSプロトコルと解決

23 分で読了
このページの目次

ドメイン名のAレコードを変更した後、一部のユーザーはすぐに新しいアドレスを表示しますが、他のユーザーは依然として古いアドレスにアクセスします。通常、これはDNSがすべてのサーバー間で更新を一度にゆっくりとブロードキャストするのではなく、権威データ、再帰キャッシュ、システムおよびアプリケーションのキャッシュがそれぞれ状態を持っているためです。1回の解決を理解するには、誰がデータを担当し、誰が代わりに照会し、どの結果がキャッシュされ、応答が「存在しない」のか「照会失敗」なのかを区別する必要があります。

問題この記事のエントリポイント
ルート、再帰、権威はそれぞれ何をするのか委任と照会の役割
NSがあるのになぜグルーが必要なのか権威サーバーアドレスの解決における起動依存関係
DNSパケットの読み方12Bのヘッダー、セクション、ラベル、圧縮ポインタ
変更後、いつ有効になるのかRRset TTL、ネガティブキャッシュ、階層型キャッシュ
大きな応答にはなぜTCPが必要なのかEDNS通知、パスMTU、切り捨て
スレーブサーバーはどのように同期するのかAXFR/IXFRと再帰キャッシュの違い

名前空間、ドメイン、ゾーン

DNS名は階層型ラベルで構成され、ルートラベルで終わります。www.example.com. の末尾のドットは、明確に絶対名であることを示します。ドメインは名前木のサブツリーを記述し、ゾーンは特定の権威サーバーが管理するデータ範囲です。親ゾーンが子ドメインを委任すると、親ゾーンは委任情報を保持し続けますが、子ゾーンのすべてのレコードを権威管理しなくなります。

DNSはアドレスのみを返すわけではありません。MXはメール交換ターゲットを表現し、TXTはアプリケーションの合意情報を保持し、SRVはサービスの位置を表現します。同じowner name、type、classを持つレコードのグループはRRsetを構成し、これは「1つのドメイン名が1つのIPに対応する」というモデルよりも、キャッシュ、署名、マルチアドレスを説明するのに適しています。RFC 1034:DNSの概念

スタブ、フォワーダー、再帰器、権威

再帰器は権威階層に個別にアクセスする。権威サーバーはそれを代わりに逐層転送しない クライアント / スタブ 最終的な回答を要求 再帰解決器 キャッシュ、反復、検証 ルートゾーンサーバー com. の委任を返す com. サーバー example.com. の委任を返す example.com. 権威 ターゲットRRsetを返す コールドキャッシュの概念的手順 キャッシュがある場合は一部の手順をスキップ可能 フォワーダーはアップストリームの再帰器に委任する 各接続線は対応するクエリ/レスポンストランザクションを示す。実際にはNSアドレスの解決、エイリアスの追跡、またはDNSSEC検証が必要になる場合もある。

スタブは通常、クエリを構成された解決サービスに委任します。フォワーダーは作業をアップストリームに渡し、再帰器は委任を追跡するか、特定のポリシーの下で転送できます。権威サーバーは、自分が担当するゾーンに回答します。1つのソフトウェアが複数の役割を担うことができ、特定の製品名を固定されたアーキテクチャと直接同一視することはできません。

RDはクライアントが再帰を要求することを示し、RAはサーバーが再帰をサポートすることを示しますが、RD=1のクエリがすべて再帰を許可されるわけではありません。コールドキャッシュの再帰器は、ルートヒントから開始し、referralに基づいて次のサーバーグループを見つけることができます。委任または回答のキャッシュがある場合、毎回ルートに問い合わせる必要はありません。

権威階層間では、通常、このクエリのために逐層リクエストを転送しません。実際に複数のクエリを発行するのは再帰器です。また、NSアドレス、エイリアス、検証の必要性により、追加のクエリが発生する可能性があり、毎回ちょうど3回とは限らず、測定されていない「数十回」を一般的なオーバーヘッドとして見なすべきではありません。

QNAME最小化は上位への情報開示を変更する

従来の反復では、完全なターゲット名が各レベルのサーバーに渡される可能性があります。QNAME minimisationは、現在のレベルに委任を進めるために必要な名前部分のみを問い合わせて尽量减少します。例えば、上位は必ずしも完全な www.example.com. を知る必要はありません。具体的なQTYPEとフォールバックプロセスはアルゴリズムに従って処理され、簡略化された図のNSクエリを唯一のオンラインシーケンスとして実装してはいけません。RFC 9156:QNAME最小化

グルーはアドレス依存関係を解く

example.com. のNSが ns1.example.com. の場合、解決器はサブゾーンに問い合わせる前に、このNSのアドレスを見つける必要がありますが、アドレス自体はサブゾーン内にあります。親ゾーンは、起動を支援するためにグルーアドレスを提供できます。これは、実際の権威から取得して検証されたアドレスRRsetとは、同じ信頼レベルではありません。

Additionalセクションは、内容が無条件でキャッシュできることを意味しません。解決器は相関性とbailiwickなどのチェックを行う必要があります。NSがゾーン外にある場合、そのアドレスを別途解決する必要があるかもしれません。すべてのreferralがすべてのNSのA/AAAAを含むわけではなく、すべてのAdditionalアドレスがグルーと呼ばれるわけではありません。

メッセージ構造:まずセクションを特定し、次にレコードを解析する

DNSメッセージの基本ヘッダーは12Bで、ID、Flags、およびQuestion/Answer/Authority/Additionalの4つのカウンター含みます。TCPでの転送では、各DNSメッセージの前に2バイトの長さプレフィックスが配置され、このプレフィックスはDNSメッセージのオフセットには含まれません。TCPはバイトストリームであり、1回の読み込み操作で完全に1つのメッセージが得られるとは限りません。RFC 1035:メッセージフォーマット、RFC 7766:TCP上のDNS

Flagsマスク意味
0x8000QR:クエリまたはレスポンス
0x7800OPCODEフィールド、シフトして解釈する必要がある
0x0400AA:権威回答フラグ
0x0200TC:切り捨て
0x0100 / 0x0080RD / RA
0x0020 / 0x0010AD / CD、DNSSEC関連のセマンティクス
0x000f基本RCODE、EDNSで拡張可能

歴史図の3つのZは、現代のメッセージで一律に「ゼロにする必要がある3つのビット」として扱うことはできません。AD/CDにはすでに用途があります。ビットマップの左からの番号付けとコードの最下位ビットの番号付けも異なるため、マスクを使用すると逆転を防ぐのに役立ちます。IDの一致は、レスポンスを関連付ける条件の一つに過ぎず、エンドポイント、問題、およびコンテキストの確認も必要です。

QuestionにはQNAME、QTYPE、QCLASSが含まれます。通常の資源レコードにはNAME、TYPE、CLASS、TTL、RDLENGTH、RDATAが含まれます。RDATAはTYPEに従って解釈され、すべてのタイプを1つの文字列または1つの圧縮可能ドメインとして扱うことはできません。

ラベルと圧縮:完全な33 → 49 Bの計算例

www.example.com. のオンラインラベルは、長さ3 + www、長さ7 + example、長さ3 + com、最後の長さ0のルートラベルで、合計17Bです。12Bのヘッダー、2BのQTYPE、2BのQCLASSを加えると、単一問題のAクエリは33Bになります。

応答に同じ名前のAレコードが含まれている場合、c0 0c を使用してDNSメッセージの先頭オフセット12を指すQNAMEを参照できます。レコード自体には2BのNAMEポインタ、10Bの固定フィールド、4BのIPv4アドレスが必要で、合計16Bです。同じ問題を保持した状態で応答の合計は49Bになります。NAMEを圧縮しない場合、17+10+4=31Bが必要になり、ここでは実際には15B節約されています。名前の重複回数に基づいて自由に推定することはできません。RFC 1035:名前圧縮

通常のラベルの最大長は63Bで、完全なオンライン名には総長制限があります。圧縮ポインタの上位2ビットは11で、下位14ビットはDNSメッセージ全体に対する相対オフセットです。解決器は、範囲外、ループ、展開長をチェックする必要があり、攻撃者が提供したポインタを無限に追跡することはできません。TCPの長さプレフィックスは、c00c が指すDNS内部位置を変更しません。

ポインタジャンプ後、レコードのカーソルはどこに停止するか?

c00cは33から12にジャンプして名前を展開しますが、AnswerのTYPEは依然として35から始まります。33自身を指すポインタに切り替えると、読み込み位置は既にアクセスされたコレクションに入り、解析は拒否しなければなりません。「さらに数ステップ読む」ことで運よく終了することはできません。

図を準備しています
ポインタジャンプ後、レコードのカーソルはどこに停止するか?

圧縮ポインタは元の位置の2バイトを占めますが、展開により遠くのラベルを読み取ることができます。名前を読み取る位置は、元のレコードの後のフィールドの位置とは異なります。異常なポインタは終了しなければなりません。

レコードタイプと解決依存関係

タイプ読み方一般的な誤解
A / AAAAIPv4 / IPv6 アドレスAAAAがないからといって、名前全体が存在しないわけではない
CNAME現在のownerは別名のエイリアス通常、同じownerで通常のA/MXなどのデータを同時に設定することはできない
NS権威サーバー名を指すNSターゲットは名前であり、IPアドレスを直接書く場所ではない
MX優先度が低いほど優先され、後にメールターゲット名が続く最小値のターゲットが常に利用可能であるとは限らない
SOAゾーン管理、シーケンス番号、同期/ネガティブキャッシュ関連フィールドMINIMUMはすべての正のレコードの統一TTLではない
PTR逆名前空間でターゲット名を照会歴史のIQUERY opcodeとは同じメカニズムではない
SRVpriority、weight、port、targetpriorityとweightを同じソート番号として混同してはいけない
TXT長さ付きの1つ以上の文字列1つの文字列の制限が、RR全体が常に255Bしか持てないことを意味しない
CAA証明書発行権限を制約するDNSデータDNSを暗号化するものではなく、現在表示されている証明書を直接検証するものでもない

IPv4逆引きは、192.0.2.10 の 10.2.0.192.in-addr.arpa. のPTRを照会するもので、通常の標準クエリです。PTRと前方のA/AAAAには自動的な1対1の対応保証がなく、逆引き名のみでホストIDを認証することはできません。

CNAMEの追跡は、ターゲットクエリをさらにトリガーする可能性があります。エイリアスとターゲットRRsetにはそれぞれTTLがあります。通常のCNAMEは、ゾーン頂点で存在しなければならないSOA/NSと競合します。ホスティングプラットフォームのALIAS/flattening動作は、通常のオンラインCNAMEの共存ルールを解除するものではありません。

キャッシュ:正の結果、負の結果、失敗を分ける

TTLは各キャッシュコピーの残り寿命であり、世界的な統一切り替えのカウントダウンではない t=0 t=100 t=200 t=300 s 再帰器が古いAをキャッシュ 初期TTL=300 s 権威が新しいAに変更 古いキャッシュは依然として200 s残っている 古いコピーが期限切れ 次のリクエストで再取得可能 他のキャッシュの取得時刻は異なる。新しいTTLを下げても、既に送信された古いTTLを遡って短縮することはできず、serve-staleには明確な例外がある。

TTLは、キャッシュコピーが関連するルールに従って使用できる期間を制御し、既存の接続にアドレスの切り替えを通知するものではありません。再帰器はt=0でTTL=300の古いAをキャッシュし、権威はt=100で値を変更しますが、古いコピーはt=300まで使用され続ける可能性があります。この時点で新しいレコードのTTLを30に変更しても、既に送信された300を遡って変更することはありません。

移行前にTTLを下げるには、古いキャッシュが自然に期限切れになる時間を確保する必要があります。実際のユーザーパスには、アプリケーションキャッシュ、ネガティブキャッシュ、および異なるビューが含まれる可能性があります。一部の解決器は、特定の障害条件下でserve-staleメカニズムに従って期限切れデータを提供しますが、これは明確なポリシーが必要であり、すべてのTTLが無効になると一般化して見なしてはいけません。RFC 8767:serve-stale

応答意味キャッシュ解釈
NXDOMAIN、RCODE=3照会名が存在しない名前のレベルでの否定結果。エイリアスなどのコンテキストと組み合わせて判断する必要がある
NOERRORだがターゲットタイプにデータなし一般的にNODATAと呼ばれる。独立したRCODEではない名前は存在するが、そのタイプが存在しない。SOAなどと組み合わせて判断する必要がある
SERVFAIL、RCODE=2完了できない。例えばアップストリーム/検証失敗名前が存在しないことを権威が証明したものとして扱ってはならない
REFUSED、RCODE=5サーバーポリシーにより拒否ドメイン名自体が存在するかどうかを説明するものではない

通常のネガティブキャッシュTTLは、応答SOAのTTLとSOA.MINIMUMの小さい方を取ります。解決器はローカル制限を適用する可能性があります。両方が600と300の場合、ネガティブキャッシュの基本値は300 sです。新しく作成された本来存在しないレコードも、既存のネガティブキャッシュによって一時的に隠される可能性があります。RFC 2308:ネガティブキャッシュ

権威が更新されたのに、キャッシュは同期して変化しないのはなぜか?

t=140では、古いコピーにはまだ160秒残り、新しいコピーには残り10秒しかありません。新しいからといって、期限切れが遅いわけではありません。t=150以降、2番目のコピーは通常のTTLルールに従って使用できなくなりますが、それは1番目のコピーが同時に無効になることを意味するものではありません。

図を準備しています
権威が更新されたのに、キャッシュは同期して変化しないのはなぜか?

TTLは取得されたコピーに付随します。権威が内容と新しいTTLを変更しても、古いキャッシュの期限時刻を遡って書き換えることはありません。

EDNS、切り捨て、パスサイズ

EDNSなしの従来のDNS/UDPメッセージの上限は512Bです。EDNSはAdditional内のOPT擬似レコードを使用して能力を表現します。TYPE=41、CLASSはUDPペイロードサイズを通知するために使用され、元のTTL位置は拡張RCODE、バージョン、およびフラグを保持します。これは通常のキャッシュ可能なドメインRRではなく、DOは検証成功を意味しません。RFC 6891:EDNS

4096Bを受信できることを通知しても、パスが断片化なしで4096Bを保持できることを証明するものではありません。1232Bという予算の根拠は、IPv6最小MTU 1280−40B基本ヘッダー−8B UDPヘッダーです。さらに多くのオーバーヘッドがある場合は、それを考慮する必要があります。応答が適用されるUDP制限を超えた場合、切り捨てられてTCが設定され、クライアントはこれに基づいてTCPなどの適切な転送媒体を使用して完全な結果を再取得します。

TCPはゾーン転送専用のチャネルだけでなく、通常のクエリでもサポートしなければならない重要な転送媒体です。TCP 53をフィルタリングすると、小さなクエリは正常に見え、大きな応答が失敗する可能性があります。単にEDNS値を大きくすると、IPフラグメントの問題を引き起こす可能性があります。RFC 7766

DNSSECはデータの出所と整合性を検証し、暗号化されたDNSは選択された転送リンクを保護します。これらは異なる問題を解決します。AAは暗号学的認証ではなく、ADの信頼性は、ローカルマシンと使用されている解決器の信頼関係に依存します。これらについては次の記事で詳しく展開します。

ゾーン転送:権威コピーの同期、グローバル再帰キャッシュの更新はしない

AXFRは完全なゾーン転送で、複数のDNSメッセージにまたがり、TCPで転送され、SOAなどのルールによって転送内容が定義されます。IXFRはシーケンス番号に基づいて増分を要求し、サーバーに適切な差分がない場合は、完全転送にフォールバックする可能性があります。シーケンス番号の比較には独自のラップアラウンドルールがあり、常に通常の整数の大小で新旧を判断することはできません。RFC 5936:AXFR

プライマリ/セカンダリが同期を完了しただけでは、権威コピーが更新されたことを示すだけで、インターネットの再帰器が期限切れになっていないキャッシュを強制的にクリアさせることはできません。NOTIFYは、対応するセカンダリサーバーに変更をチェックするよう通知できますが、それはインターネット全体キャッシュの無効化ブロードキャストを意味するものではありません。

ローカルで1つのクエリとレスポンスを構築する

import struct
from ipaddress import IPv4Address

def encode_name(name):
    labels = name.rstrip('.').split('.')
    result = b''.join(bytes([len(x.encode('ascii'))])+x.encode('ascii') for x in labels)+bytes(1)
    assert all(0<len(x.encode('ascii'))<=63 for x in labels)
    assert len(result)<=255
    return result
qname = encode_name('www.example.com.')
question = qname+struct.pack('!HH',1,1)
query = struct.pack('!6H',0x1234,0x0100,1,0,0,0)+question
assert len(qname)==17 and len(query)==33
answer = bytes.fromhex('c00c')+struct.pack('!HHIH',1,1,300,4)+IPv4Address('192.0.2.10').packed
response = struct.pack('!6H',0x1234,0x8180,1,1,0,0)+question+answer
assert len(answer)==16 and len(response)==49
pointer = int.from_bytes(answer[:2],'big')&0x3fff
assert pointer==12 and response[pointer:pointer+17]==qname
flags = struct.unpack('!H',response[2:4])[0]
assert flags&0x8000 and flags&0x0080 and not flags&0x0400
assert 1280-40-8==1232
assert min(600,300)==300
assert max(0,300-100)==200
assert IPv4Address('192.0.2.10').reverse_pointer=='10.2.0.192.in-addr.arpa'
print('DNS クエリ/レスポンスの長さ、圧縮オフセット、Flags、ネガティブキャッシュ、EDNS予算が通過')

この例は、固定された教育用のA応答を生成するものであり、完全なDNSパーサーやキャッシュ実装ではありません。すべてのアドレスはドキュメントの例として使用されており、実際のドメインは照会または変更されていません。

続きを読む