
ZDNS は、大規模な DNS 測定を実行するための高速 DNS リゾルバ兼コマンドラインユーティリティです。ZDNS は Go で書かれており、独自の再帰解決コードと、多様な名前のルックアップに最適化されたキャッシュを備えています。生の DNS パケットの構築と解析には、https://github.com/zmap/dns を使用しています。ZDNS のアーキテクチャとパフォーマンスの詳細については、ACM の Internet Measurement Conference '22 で発表された次の論文を参照してください。
[!TIP] ZDNS Wiki には、ZDNS に関する追加情報と使用例・サンプルが含まれています。
ZDNS は、リポジトリをチェックアウトして make install を実行することでインストールできます。
git clone https://github.com/zmap/zdns.git
cd zdns
make install
ZDNS は、再帰リゾルバのライブラリと CLI ラッパーで構成されています。
ライブラリは、すべてのルックアップの設定オプションを含む ResolverConfig 構造体で構成されています。ResolverConfig を使用して、すべてのルックアップを実行する 1 つ以上の Resolver 構造体を作成します。Resolver は一度に 1 つのルックアップのみを実行する必要があり(スレッドセーフではありません)、並列処理には複数の Resolver 構造体を使用する必要があります。ライブラリの使用方法については、examples を参照してください。Modules はルックアップの動作を定義するために使用されます。
ZDNS はいくつかのタイプのモジュールを提供します。
Raw DNS モジュール は、dig と同様にサーバーからの生の DNS 応答を JSON 形式で提供します。(ほとんど)すべての DNS レコードタイプに対応するモジュールがあります。
Lookup モジュール は、複数のクエリが必要な場合(NSLOOKUP で NS を受信した場合の IP アドレスの追加 A ルックアップの完了など)に、より便利な応答を提供します。
Misc モジュール は、サーバーへのクエリの他の追加手段を提供します(例:bind.version)。
モジュールの詳細は以下の通りです。
A、AAAA、AFSDB、ANY、ATMA、AVC、AXFR、BINDVERSION、CAA、CDNSKEY、CDS、CERT、CNAME、CSYNC、DHCID、DMARC、DNSKEY、DS、EID、EUI48、EUI64、GID、GPOS、HINFO、HIP、HTTPS、ISDN、KEY、KX、L32、L64、LOC、LP、MB、MD、MF、MG、MR、MX、NAPTR、NID、NINFO、NS、NSAPPTR、NSEC、NSEC3、NSEC3PARAM、NSLOOKUP、NULL、NXT、OPENPGPKEY、PTR、PX、RP、RRSIG、RT、SVCBS、MIMEA、SOA、SPF、SRV、SSHFP、TALINK、TKEY、TLSA、TXT、UID、UINFO、UNSPEC、URI モジュールは、dig と同様に生の DNS 応答を JSON 形式で提供します。
例えば、次のコマンド:
echo "censys.io" | zdns A
は以下を返します:
{
"name": "censys.io",
"results": {
"A": {
"data": {
"additionals": [
{
"flags": "",
"type": "EDNS0",
"udpsize": 512,
"version": 0
}
],
"answers": [
{
"answer": "104.18.10.85",
"class": "IN",
"name": "censys.io",
"ttl": 300,
"type": "A"
},
{
"answer": "104.18.11.85",
"class": "IN",
"name": "censys.io",
"ttl": 300,
"type": "A"
}
],
"protocol": "udp",
"resolver": "[2603:6013:9d00:3302::1]:53"
},
"duration": 0.285295416,
"status": "NOERROR",
"timestamp": "2024-08-23T13:12:43-04:00"
}
}
}
生の DNS 応答は、必要なデータを提供しないことがよくあります。例えば、MX 応答には、追加セクションに関連する A レコードが含まれていない場合があり、追加のルックアップが必要になります。このギャップを埋め、より使いやすいインターフェースを提供するために、alookup、mxlookup、nslookup という lookup モジュールも提供しています。
alookup は nslookup と同様に動作し、CNAME レコードを辿ります。
mxlookup は、交換レコードに対応する IP アドレスの A ルックアップを追加で実行します。
nslookup は、NS レコードに対応する IP アドレスの A/AAAA ルックアップを追加で実行します。
例えば、
echo "censys.io" | zdns mxlookup --ipv4-lookup
は以下を返します:
{
"name": "censys.io",
"results": {
"MXLOOKUP": {
"data": {
"exchanges": [
{
"class": "IN",
"ipv4_addresses": [
"209.85.202.27"
],
"name": "alt1.aspmx.l.google.com",
"preference": 5,
"ttl": 300,
"type": "MX"
},
{
"class": "IN",
"ipv4_addresses": [
"142.250.31.26"
],
"name": "aspmx.l.google.com",
"preference": 1,
"ttl": 300,
"type": "MX"
}
]
},
"duration": 0.154786958,
"status": "NOERROR",
"timestamp": "2024-08-23T13:10:11-04:00"
}
}
}
ZDNS は特別な「デバッグ」DNS クエリもサポートしています。モジュールには BINDVERSION が含まれます。
ZDNS は、目的の動作に応じて様々な形式での入力をサポートしています。
最も基本的な入力は、改行で区切られた名前のリストです。例:
標準入力から:
echo "google.com\nyahoo.com" | zdns A
cat list_of_domains.txt | zdns A
ファイルから:
zdns A --input-file=list_of_domains.txt
多くのドメインを解決する必要がない場合は、使いやすさのために dig と同様にドメインを CLI 引数として指定することができます。
例:
zdns A google.com --name-servers=1.1.1.1
dig -t A google.com @1.1.1.1 と同等
通常、ZDNS は --name-servers から各ドメインルックアップにランダムなネームサーバーを選択します。代わりにドメインごとに異なるネームサーバーを指定したい場合は、ドメイン名とネームサーバーの IP をカンマ区切りで改行で区切って指定できます。これは --name-servers で指定されたネームサーバーを上書きします。
例:
echo "google.com,1.1.1.1\nfacebook.com,8.8.8.8" | zdns A
出力で各ドメインに対して指定されたリゾルバを確認できます(簡潔にするため追加/回答は省略):
$ echo "google.com,1.1.1.1\nfacebook.com,8.8.8.8" | zdns A
{"name":"google.com","results":{"A":{"data":{"additionals":...,"answers":[...],"protocol":"udp","resolver":"1.1.1.1:53"},"duration":0.030490042,"status":"NOERROR","timestamp":"2024-09-13T09:51:34-04:00"}}}
{"name":"facebook.com","results":{"A":{"data":{"additionals":[...],"answers":[...],"protocol":"udp","resolver":"8.8.8.8:53"},"duration":0.061365459,"status":"NOERROR","timestamp":"2024-09-13T09:51:34-04:00"}}}
ICANN CZDS などからのゾーンファイルは、--zone-file フラグを使用して ZDNS の入力ソースとして使用できます。これにより、標準入力(デフォルト)または --input-file フラグを使用したファイルからのゾーンファイルの解析が可能になります。
デフォルトでは、ZDNS は各ゾーンファイルレコードから名前のみを抽出します。CNAME や NS レコードなどの回答セクションで参照される名前も解決したい場合は、--zone-file-include-targets CLI フラグを使用できます。
例えば、--zone-file-include-targets と次の DNS ゾーンファイルエントリを使用した場合:
example.com. 3600 IN NS ns1.example.com
example.com と ns1.example.com の両方が解決されます。
ZDNS は、入力行を特定のモジュールにマッピングする、入力行ごとの「トリガー」の受け渡しもサポートしています。これにより、特定のドメインを特定のモジュールでルックアップするように指定できます。
入力形式は次のとおりです:
domain_name,name_server,trigger,trigger_2,etc ここで、nameServer はデフォルトのネームサーバーを使用する場合は空にでき、1 つ以上のトリガーを指定できます。
input.csv ファイルの例:
example.com,,a-trigger
google.com,,a-trigger,cname-trigger
example.com,1.1.1.1,aaaa-trigger
yahoo.com
apnews.com,1.1.1.1
対応する multiple.ini:
; グローバルオプションをここで指定
[Application Options]
iterative=true
prefer-ipv6-iteration="true"
; モジュールとそれぞれのモジュール固有オプションをここにリストします。各モジュールは一度だけリストできます。
[A]
trigger = "a-trigger"
[AAAA]
trigger = "aaaa-trigger"
[CNAME]
trigger = "cname-trigger"
これにより、次がルックアップされます:
example.com を A モジュール、デフォルトネームサーバーを使用google.com を A + CNAME モジュール、デフォルトネームサーバーを使用example.com を AAAA モジュール、Cloudflare の 1.1.1.1 リゾルバを使用yahoo.com を指定されたすべてのモジュール、デフォルトネームサーバーを使用apnews.com を指定されたすべてのモジュール、Cloudflare の 1.1.1.1 リゾルバを使用コマンドの実行:
zdns MULTIPLE --multi-config-file="./multiple.ini" --input-file="input.csv"
ZDNS は、再帰リゾルバ(例:組織の DNS サーバー)に対して動作するか(デフォルトの動作)、内部で独自の再帰を実行できます。少数(数百万程度)のルックアップを実行し、10,000 未満の goroutine を使用する場合は、通常、Cloudflare や Google などの一般的な再帰リゾルバを使用するのが最速です。Cloudflare はほとんどの場合 Google よりも高速です。これは、特に人気のある名前をルックアップする場合に当てはまります。なぜなら、それらはキャッシュされており、1 ラウンドトリップで応答できるからです。数万の同時スレッドを使用する場合は、再帰リゾルバへの DoS 攻撃やレート制限を避けるために、内部で反復を実行することを検討してください。
ローカル再帰を実行するには、--iterative フラグを指定して zdns を実行します。このフラグを使用すると、ZDNS は公開ルートサーバー(例:198.41.0.4)間でラウンドロビンを行います。反復モードでは、--cache-size を指定してローカルキャッシュのサイズを制御し、--iteration-timeout を設定して個々の反復のタイムアウトを制御できます。--timeout フラグは、特定の入力に対する解決全体のタイムアウト(つまり、すべての反復ステップの合計)を制御します。
ZDNS のパフォーマンスは、軽量な Go ルーチンを使用した大規模な並列化に由来します。このアーキテクチャにはいくつかの注意点があります:
各 Go ルーチンは専用のネットワークソケットを使用します。したがって、--threads で指定された数のスレッド数だけ、多くのソケット(最大ファイル記述子数とエフェメラルポート数の両方の点で)を開くことができる必要があります。デフォルトでは、ZDNS は 1,000 スレッドを使用します。これは Linux のデフォルトの最大オープン FD 数 1024 未満ですが、Mac OS のデフォルト 256 を上回ります。ulimit -n を実行すると、許可されている最大オープン FD 数(したがってソケット数)を確認できます。この数よりも多くのスレッドで実行したい場合は、OS レベルでオープンファイル数を増やす必要があります。そうしないと、FATA[0000] unable to create socketlisten udp <client IP address>:0: socket: too many open files のような致命的なエラーが発生します。利用可能なエフェメラルポートよりも多くのスレッドを実行したい場合は、複数のクライアント IP アドレスを使用する必要があります: --local-addr=A,B,C
デフォルトでは、ZDNS は軽量ルーチンごとに起動時にバインドされていない UDP ソケットを作成し、それをすべてのクエリ(送信先 IP に関係なく)に使用することで UDP ソケットを「再利用」します。これにより、DNS クエリ/応答は通常 1 パケットであるため、ZDNS とホスト OS が各パケットを送信するためにソケットのセットアップと破棄を行う必要がなくなり、パフォーマンスが大幅に向上します。ただし、これは ZDNS が起動時にスレッドごとにソケットを事前割り当てすることを意味します。これは、少数の名前のみをルックアップする場合には最適ではない可能性があります。例えば、100 個の名前だけをルックアップする必要があるが、デフォルトの 1,000 スレッドを使用する場合、900 個の UDP ソケットをバインドしても使用しないことになります。ソケットの再利用を気にする代わりに、使用例に適した妥当な数のスレッドを指定することをお勧めします(これにより、それらのスレッドを開始する作業も不要になります)。そのため、名前が 1 つだけでも、多数のソケットを開くことができないというエラーが発生することがあります。クエリごとに新しいソケットを作成することが重要な場合は、--recycle-sockets=false を指定することでこの再利用を無効にできます。
Go は利用可能なすべての CPU コアを喜んで使用し、多数のスレッドを指定すると膨大な CPU を使用する可能性があります。CPU は主に解析と JSON エンコードに使用されます。CPU コアの数を制限したい場合は、--go-processes=n フラグを含めるか、GOMAXPROCS 環境変数を設定することで制限できます。
--threads の正確な量を推奨することは困難です。これはいくつかの要因に依存するためです。以下のグラフは、スレッド数が増加するにつれて、サンプルワークフローの実行時間が短くなる一方で、名前解決の失敗率が高くなることを示しています。
実際に得られるパフォーマンスの多くは、ワークフロー、ハードウェア、および負荷が分散されるネームサーバーの数に依存します。ワークフロー/ハードウェアのパフォーマンスを最大化したい場合は、100 スレッドから開始し、名前解決の失敗が増加し始めるまで増やすことをお勧めします。これを支援するために、--output-file=output.jsonl と grep -v "NOERROR" output.jsonl | wc -l を使用して、解決に失敗した名前の数をカウントできます。パフォーマンスチューニングに役立つ可能性のあるフラグは次のとおりです:
--timeout ZDNS が 1 つの名前に費やす最大時間--iteration-timeout ZDNS が 1 つの反復ステップに費やす最大時間(例:.com 層で google.com を解決する)--network-timeout ZDNS がネームサーバーからの応答を待機する最大時間--retries=N --iterative で特定のネームサーバーへの接続が失敗した場合、ZDNS はその層でまだクエリされていない別のネームサーバーで再試行します。再試行は名前ごとに行われるため、--retries=1 の場合、ZDNS はその名前に対して完全な反復プロセス中に 1 回、新しいネームサーバーで再試行します。すべてのネームサーバーがクエリされた場合は、ランダムなネームサーバーが選択されます。--name-servers ルックアップに使用するネームサーバーのリスト。主に --iterative=false で有用です。DNS には常に有用とは限らない多くの余分なデータが含まれています。結果の冗長性レベルは short、normal(デフォルト)、long、trace の 4 つです:
short: 最も簡潔な結果出力です。応答に関する情報のみが含まれます。normal: short に含まれるすべての情報に加え、応答サーバーに関するデータも提供します。long: サーバーが DNS パケットに含めたすべての情報(フラグを含む)を出力します。trace: 再帰プロセスの各ステップからのすべての情報を出力します。ユーザーは --include-fields フラグとフィールドのリストを使用して、特定の追加フィールドを含めることもできます。例:--include-fields=flags,resolver。追加フィールドは次のとおりです: class、protocol、ttl、resolver、flags、dnssec。
デフォルトでは、ZDNS は少数のネームサーバーでルックアップする名前のリストを受け取ることを想定しています。例:
echo "google.com" | zdns A --name-servers=8.8.8.8,8.8.4.4
ただし、多数のサーバーで同じ名前をルックアップしたい場合もあります。これは ネームサーバーモード を使用することで実現できます。例:
echo "8.8.8.8" | zdns A --name-server-mode --override-name="google.com"
ここでは、ZDNS にパイプされたすべての行に対して、google.com の A クエリが送信されます。ZDNS は、name,nameServer のカンマ区切りリストをパイプすることで、両方のモードを組み合わせることもサポートしています。例:
echo "google.com,8.8.8.8" | zdns A は、google.com の A クエリを 8.8.8.8 に送信します。これは、--name-servers= フラグで指定されたネームサーバーに関係なく行われます。明示的にネームサーバーを指定しない行は、通常どおり OS または --name-servers フラグで指定されたサーバーを使用します。
特定の DNS クエリをすべてのネームサーバーに対して実行する機能があります。例えば、特定のドメインのすべてのネームサーバーから A レコードを取得したい場合があります。これを行うには、次のようにします:
echo "google.com" | zdns A --all-nameservers
ZDNS は、1 回の呼び出しで複数のルックアップモジュールを使用することをサポートしています。例えば、あるドメインセットに対して A、AAAA、MXLOOKUP を実行し、それらを反復解決で実行したいとします。この場合は、MULTIPLE モジュールを使用し、使用するモジュールとモジュール固有のフラグを指定した設定ファイルを提供する必要があります。
グローバルおよびモジュール固有のオプションの詳細については、zdns --help および zdns <MODULE_NAME> --help を参照してください。これらのオプションは設定ファイルで使用できます。
例:
cat 1000k_domains.txt | zdns MULTIPLE --multi-config-file="./multiple.ini"
ここで、multiple.ini は次のようなファイルです:
; グローバルオプションをここで指定
[Application Options]
iterative=true
; モジュールとそれぞれのモジュール固有オプションをここにリストします。各モジュールは一度だけリストできます。
[MXLOOKUP]
ipv4-lookup = true
; オプションを指定する必要がない場合は、デフォルト値を使用してモジュールをリストするだけです。
[A]
[AAAA]
サンプルの multiple.ini ファイルは src/cli/multiple.ini に提供されています。
デフォルトでは、ZDNS は 1,000 の軽量 Go ルーチンで動作します。注意しないと、これにより多くの上流 DNS プロバイダーが圧倒される可能性があります。スキャンを実行する前に、ユーザーはローカルネットワーク管理者と調整することをお勧めします。同時接続数は、--threads および --go-processes コマンドライン引数で制御できます。代替ネームサーバーは --name-servers で指定できます。ZDNS はリクエストを行う際にこれらのサーバーを順に使用します。私たちは、数万の軽量ルーチンで ZDNS を正常に実行したことがあります。
zdns がサポートしていないレコードタイプに遭遇した場合、type フィールドが正しく設定され、unparsed_rr フィールドに基盤となるデータ構造の表現が含まれた出力レコードが生成されます。このフィールドの存在や構造に依存しないでください。このフィールド(およびその存在)は、追加のレコードタイプのサポートを拡大するにつれて、いつでも変更される可能性があります。このフィールドを使用している場合は、パーサーサポートを追加するプルリクエストを提出することを検討してください。
benchmark/ にベンチマークが用意されており、ZDNS を予測可能な方法で実行し、実行に関する統計情報を出力できます。これは、ZDNS への変更前後のパフォーマンスを比較するのに役立ちます。詳細は、benchmark README を参照してください。
ZDNS への貢献に興味がある場合は、CONTRIBUTING を参照してください。
ZDNS Copyright 2020 Regents of the University of Michigan
Apache License, Version 2.0(「ライセンス」)に基づいてライセンスされています。このファイルは、ライセンスに準拠する場合を除き、使用することはできません。ライセンスのコピーは、http://www.apache.org/licenses/LICENSE-2.0 から入手できます。
適用法で義務付けられている場合、または書面で同意された場合を除き、ライセンスに基づいて配布されるソフトウェアは、明示的または黙示的を問わず、いかなる保証も条件もなく「現状のまま」で配布されます。特定の言語での権利と制限を管理するためのライセンスについては、LICENSE を参照してください。