概要
DNS (Domain Name System) とは、インターネットの重要な基盤技術の1つであり、ドメイン名 (ホスト名) とIPアドレスの対応関係を管理するシステムである。
DNSはインターネットの電話帳のような役割を果たしており、Webサイトにアクセスする時には不可欠な仕組みである。
- 名前解決
- 人間が覚えやすいドメイン名 (例: www.example.com) をコンピュータが理解できるIPアドレス (例: 192.0.2.1) に変換する。
- 階層構造
- ドメイン名は階層的に構成されており、ルートドメイン、トップレベルドメイン、セカンドレベルドメイン等がある。
- 分散システム
- 世界中に多数のDNSサーバが存在して、お互いに連携して動作する。
- キャッシュ機能
- 1度解決した情報を一定期間保存して、応答速度を向上させる。
- その他の情報
- IPアドレス以外にも、メールサーバの情報等、様々なデータを管理する。
DNSレコードの確認
DnsClient.NETライブラリとは
DnsClient.NETライブラリは、.NET Standardに準拠したクロスプラットフォームの高機能なDNS (Domain Name System) クライアントライブラリである。
このライブラリは以下の環境で動作する。
- .NET Framework 4.6.1以降
- .NET Core 2.0以降
DnsClient.NETライブラリの特徴および用途を以下に示す。
- 主な機能
- DNSクエリの実行 (A、AAAA、CNAME、MX、NS、PTR、SOA、SRV、TXTレコード等)
- 例: DNSレコードの存在を確認する場合
- 非同期DNSルックアップ
- DNSキャッシュのサポート
- DNSSEC (DNS Security Extensions) のサポート
- IPv4およびIPv6のサポート
- DNSクエリの実行 (A、AAAA、CNAME、MX、NS、PTR、SOA、SRV、TXTレコード等)
- 使用目的
- ドメイン名の解決
- メールサーバーの検索 (MXレコード)
- サービスディスカバリ (SRVレコード)
- リバースDNSルックアップ
- カスタムDNSサーバの使用
- メリット
- パフォーマンスの最適化
- システムのDNSリゾルバよりも高速な場合がある。
- 細かな制御
- タイムアウト、リトライ、DNSサーバの選択などを詳細に設定できる。
- クロスプラットフォーム
- Windows、Linux、MacOSで動作可能。
- 豊富な機能
- 標準的なDNS操作に加えて、高度な機能も提供している。
- パフォーマンスの最適化
- 一般的な使用シナリオ
- マイクロサービスアーキテクチャでのサービスディスカバリ
- ネットワーク診断ツール
- メールシステムの実装
- カスタムネットワークアプリケーション
DnsClient.NETライブラリのインストール
Riderからインストールする場合
Riderを起動して、対象のプロジェクトを開く。
[ツール]メニューバー - [NuGet] - [パッケージマネージャーの管理]を選択して、NuGetパッケージマネージャーを開く。
または、ソリューションエクスプローラでプロジェクトを右クリックして、[NuGetパッケージの管理]を選択して、NuGetパッケージマネージャーを開く。
次に、DnsClient.NETライブラリを検索する。
検索バーにて、DnsClient.NETと入力する。
DnsClient.NETライブラリのインストールする。
検索結果からDnsClient.NETライブラリを選択して、[インストール]ボタンを押下する。
インストールが完了した後、[閉じる]ボタンを押下してNuGetパッケージマネージャーを閉じる。
.NET CLIからインストールする場合
プロジェクトのディレクトリに移動して、DnsClient.NETライブラリをインストールする。
# 最新の安定版のDnsClient.NETライブラリをインストールする場合 dotnet add package DnsClient.NET # 特定のバージョンを指定する場合 # 例: DnsClient.NET 1.7.0ライブラリをインストール dotnet add package DnsClient.NET --version 1.7.0
ライブラリを追加した後、依存関係を復元する。
dotnet restore
DNSレコードタイプの取得 (PTRレコードおよびSRVレコード以外)
以下の例では、duckduckgo.comから複数のDNSレコードタイプを取得および表示している。
- LookupClientクラスのインスタンスを生成する。
- 取得するレコードタイプの配列を定義する。
- 各レコードタイプに対して、DNSクエリを送信する。
- 結果を取得して、レコードタイプに応じて表示する。
※注意
PTRレコードの逆引きはIPアドレスに対して行うものであることに注意する。
IPアドレスを逆にした形式 (例: 4.4.8.8.in-addr.arpa) を使用する。
SRVレコードは特定のサービスに対して使用されるため、適切なサービス名とプロトコルを指定する必要がある。
using System;
using System.Threading.Tasks;
using DnsClient;
class Program
{
public static async Task Main(string[] args)
{
var lookup = new LookupClient();
var domain = "duckduckgo.com";
var recordTypes = new[] {
QueryType.A, QueryType.AAAA, QueryType.CNAME,
QueryType.MX, QueryType.NS, QueryType.PTR,
QueryType.SOA, QueryType.SRV, QueryType.TXT
};
foreach (var recordType in recordTypes) {
Console.WriteLine($"--- {recordType} Records for {domain} ---");
var result = await lookup.QueryAsync(domain, recordType);
if (result.Answers.Count == 0) {
Console.WriteLine($"No {recordType} records found.");
continue;
}
foreach (var record in result.Answers) {
switch (record)
{
case ARecord a:
Console.WriteLine($"A Record: {a.Address}");
break;
case AaaaRecord aaaa:
Console.WriteLine($"AAAA Record: {aaaa.Address}");
break;
case CNameRecord cname:
Console.WriteLine($"CNAME Record: {cname.CanonicalName}");
break;
case MxRecord mx:
Console.WriteLine($"MX Record: {mx.Exchange} (Preference: {mx.Preference})");
break;
case NsRecord ns:
Console.WriteLine($"NS Record: {ns.NSDName}");
break;
case SoaRecord soa:
Console.WriteLine($"SOA Record: Primary NS: {soa.MName}, Responsible: {soa.RName}");
break;
case TxtRecord txt:
Console.WriteLine($"TXT Record: {string.Join(", ", txt.Text)}");
break;
default:
Console.WriteLine($"Unknown Record Type: {record}");
break;
}
}
}
}
}
PTRレコードおよびSRVレコードの取得
PTRレコードはIPアドレスの逆引き用であり、SRVレコードは特定のサービスに対して使用される。
以下の例では、指定したIPアドレスのPTRレコードおよび指定したサービスのSRVレコードを取得して表示する。
もし、特定のIPアドレスやサービスに対して実行する場合は、GetPtrRecordメソッドおよびGetSrvRecordメソッドの引数を適宜変更すること。
- PTRレコードの取得
- GetPtrRecordメソッドでは、指定されたIPアドレスに対してPTRレコードを取得する。
QueryReverseAsync
メソッドを使用して、IPアドレスの逆引きを行う。- 以下の例では、"8.8.8.8" (Google Public DNS) を使用しているが、任意のIPアドレスに変更可能である。
- SRVレコードの取得
- GetSrvRecordメソッドでは、指定されたサービス名に対してSRVレコードを取得する。
- SRVレコードのクエリ形式は、一般的に、_service._proto.nameの形式である。
- 以下の例では、_sip._tcp.example.comを使用しているが、実際のサービスとドメインに応じて変更する必要がある。
※注意
PTRレコードの取得に使用するIPアドレスは、実際に逆引きが設定されているものを使用すること。
SRVレコードの取得に使用するサービス名は、実際に存在するサービスとドメインの組み合わせを使用すること。
エラーハンドリングを行っているが、ネットワーク状況やDNSサーバの設定によっては結果が得られない場合がある。
using System;
using System.Net;
using System.Threading.Tasks;
using DnsClient;
class Program
{
public static async Task Main(string[] args)
{
// タイムアウトを5秒に設定する場合
// ネットワークの状態が悪い場合やDNSサーバが応答しない場合でも、プログラムが無限に待機することなく適切に終了する
// 設定したタイムアウト時間を超える場合、TimeoutExceptionがスローされる
// この例外は捕捉され、ユーザにタイムアウトが発生したことを通知する
var options = new LookupClientOptions
{
Timeout = TimeSpan.FromSeconds(5)
};
var lookup = new LookupClient(options);
// デフォルト設定のタイムアウト時間を使用する場合
// 通常、数秒程度のタイムアウトが設定されているが、正確な時間はライブラリのバージョンにより異なる可能性がある
//var lookup = new LookupClient();
// PTRレコードの取得
await GetPtrRecord(lookup, "8.8.8.8");
// SRVレコードの取得
await GetSrvRecord(lookup, "_sip._tcp.example.com");
}
private static async Task GetPtrRecord(LookupClient lookup, string ipAddress)
{
Console.WriteLine($"--- PTR Record for {ipAddress} ---");
try {
var ip = IPAddress.Parse(ipAddress);
var result = await lookup.QueryReverseAsync(ip);
if (result.Answers.PtrRecords.Count == 0) {
Console.WriteLine("No PTR records found.");
return;
}
foreach (var ptrRecord in result.Answers.PtrRecords) {
Console.WriteLine($"PTR Record: {ptrRecord.PtrDomainName}");
}
}
catch (DnsResponseException ex) {
Console.WriteLine($"DNS query failed: {ex.Message}");
}
catch (TimeoutException) {
Console.WriteLine("The DNS query timed out.");
}
catch (Exception ex) {
Console.WriteLine($"Error retrieving PTR record: {ex.Message}");
}
}
private static async Task GetSrvRecord(LookupClient lookup, string service)
{
Console.WriteLine($"--- SRV Records for {service} ---");
try {
var result = await lookup.QueryAsync(service, QueryType.SRV);
if (result.Answers.SrvRecords.Count == 0) {
Console.WriteLine("No SRV records found.");
return;
}
foreach (var srvRecord in result.Answers.SrvRecords) {
Console.WriteLine($"SRV Record: Target: {srvRecord.Target}, " +
$"Port: {srvRecord.Port}, " +
$"Priority: {srvRecord.Priority}, " +
$"Weight: {srvRecord.Weight}");
}
}
catch (DnsResponseException ex) {
Console.WriteLine($"DNS query failed: {ex.Message}");
}
catch (TimeoutException) {
Console.WriteLine("The DNS query timed out.");
}
catch (Exception ex) {
Console.WriteLine($"Error retrieving SRV record: {ex.Message}");
}
}
}
ドメインの確認
以下の例では、対象のDNSのリストが記述されたファイル (domains.txt) を上から順に読み込み、ドメインが存在しない場合は別ファイルに書き込んでいる。
LookupClient
クラスのQuery
メソッドを使用して、対象のドメインとレコードの種類 (AレコードおよびNSレコード) を指定する。
取得結果が無い場合は、存在しないと判断する。
MXレコードを対象としているが、QueryType (Enum) でレコードの種類 (AレコードおよびNSレコード) が定義されているため、
様々な種類のレコードを対象にすることができる。
# domains.txtファイル # MXレコードの確認用のドメインリスト google.com microsoft.com yahoo.com apple.com amazon.com example.com github.com hoge.org oracle.com ibm.com
using System;
using System.IO;
using DnsClient;
namespace DNSRecordCheck;
class Program
{
public static async void Main(string[] args)
{
// LookupClientクラスのインスタンスを生成
var lookupClient = new LookupClient();
using (var sreader = new StreamReader(@"C:\domains.txt")) {
while (!sreader.EndOfStream) {
// 調査対象のリストから1行ずつ読み込み
var line = await sreader.ReadLineAsync();
using (var swriter = File.AppendText(@"C:\ignores.txt")) {
// 読み込んだDNSを問い合わせる
// QueryType.MX : MXレコードを対象とする
var result = lookupClient.Query(line, QueryType.MX);
if (result.Answers.Count == 0) {
// 結果が0件の場合は存在しないと判断
// 別ファイル (ignores.txt) に書き込む
swriter.WriteLine(line);
}
}
}
}
}
}