DNS問い合わせ (C#)(F#)
名前解決には、GetHostEntry()の利用が推奨されていますが、これは、「ホスト名」ー「IPアドレス」の相互解決しか対応していません。また、実装上、NAT内のネットワーク(ホストにリンク ローカルアドレスまたは Teredoアドレスしか割り当てられていない場合)では、IPv6アドレス(AAAA)を解決することができません。
注:「Windows Vista でのドメイン ネーム システム クライアントの動作」
DNS名クエリを実行する際の DNSサーバーへの影響が最小限に抑えられるように設計されています。ホストにリンク ローカル アドレスまたは Teredo アドレスしか割り当てられていない場合、DNSクライアントサービスはAレコードに関するクエリだけを送信します。
「MX」や「AAAA」など、自由にDNSへの問い合わせを発行するには、DNSAPI.dllのDnsQuery_Wを使用します。
DnsQueryは、DNSサーバから帰ったリソースレコードの内、質問フィールド以外を全て返しますので、必要部分のみタイプに応じた型(構造体)でデコードして使用します。(サンプルでは、A,AAAA,CNAME,MXのみ定義しています。)
取得したデータは、DnsRecordListFree()で開放が必要です。また、型の定義はwindns.hにあります。
C#サンプル
using System; using System.Runtime.InteropServices; using System.Net; namespace ConsoleApplication3 { class Program { [DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Auto)] private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)]ref string name, QueryTypes type, QueryOptions options, int extra, ref IntPtr queryResultsSet, int reserved); [DllImport("dnsapi", CharSet = CharSet.Auto)] private static extern void DnsRecordListFree(IntPtr recordList, int freeType); private enum QueryOptions { DNS_QUERY_BYPASS_CACHE = 8, } private enum QueryTypes{ DNS_TYPE_A = 1, DNS_TYPE_NS = 2, DNS_TYPE_CNAME = 5, DNS_TYPE_SOA = 6, DNS_TYPE_PTR = 12, DNS_TYPE_HINFO = 13, DNS_TYPE_MX = 15, DNS_TYPE_TXT = 16, DNS_TYPE_AAAA = 28, } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] struct RR{ public IntPtr pNext; public string pName; public short wType; public short wDataLength; public int flags; public int dwTtl; public int dwReserved; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] struct MX_RR { public string pNameExchange; public short wPreference; } [StructLayout(LayoutKind.Sequential)] struct AAAA_RR { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte [] addr; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] struct CNAME_RR { public string pCName; } [StructLayout(LayoutKind.Sequential)] struct A_RR { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] addr; } static void Main(string[] args) { string name = "www.kame.net"; IntPtr buf = IntPtr.Zero; if(0 == DnsQuery(ref name, QueryTypes.DNS_TYPE_AAAA, QueryOptions.DNS_QUERY_BYPASS_CACHE, 0, ref buf, 0)){ IntPtr p = buf; while(p != IntPtr.Zero){ var rr = (RR) Marshal.PtrToStructure(p, typeof(RR));//1リソースレコード単位で処理する if ((rr.flags & 0x000f) == 0x9) { // Answer レコードのみを対象にする p = IntPtr.Add(p, Marshal.SizeOf(typeof(RR)));//タイプごとのデータへ移動 switch ((QueryTypes)rr.wType) { case QueryTypes.DNS_TYPE_A: var a = (A_RR)Marshal.PtrToStructure(p, typeof(A_RR)); Console.WriteLine(string.Format("A {0} {1}", rr.pName, new IPAddress(a.addr))); break; case QueryTypes.DNS_TYPE_AAAA: var aaaa = (AAAA_RR)Marshal.PtrToStructure(p, typeof(AAAA_RR)); Console.WriteLine(string.Format("AAAA {0} {1}", rr.pName, new IPAddress(aaaa.addr))); break; case QueryTypes.DNS_TYPE_CNAME: var cname = (CNAME_RR)Marshal.PtrToStructure(p, typeof(CNAME_RR)); Console.WriteLine(string.Format("CNAME {0}", cname.pCName)); break; case QueryTypes.DNS_TYPE_MX: var mx = (MX_RR)Marshal.PtrToStructure(p, typeof(MX_RR)); Console.WriteLine(string.Format("MX {0} {1}", mx.wPreference,mx.pNameExchange)); break; default: Console.WriteLine("unknown"); break; } } p = rr.pNext;//次のリソースレコード } DnsRecordListFree(buf, 0);//メモリ開放 } Console.WriteLine(); Console.WriteLine("何かのキーを押してください。"); Console.ReadKey(); } } }
F#サンプル
open System open System.Runtime.InteropServices open System.Net type QueryOptions = | DNS_QUERY_BYPASS_CACHE = 8 type QueryTypes = | DNS_TYPE_A = 1 | DNS_TYPE_NS = 2 | DNS_TYPE_CNAME = 5 | DNS_TYPE_SOA = 6 | DNS_TYPE_PTR = 12 | DNS_TYPE_HINFO = 13 | DNS_TYPE_MX = 15 | DNS_TYPE_TXT = 16 | DNS_TYPE_AAAA = 28 [<DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Auto)>] extern int DnsQuery(string name,QueryTypes wtype,QueryOptions options,int extra,IntPtr *queryResultsSet,int reserved); [<DllImport("dnsapi", CharSet = CharSet.Auto)>] extern void DnsRecordListFree(IntPtr recordList, int freeType); [<StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto)>] type IP_ADAPTER_INDEX_MAP = struct val Index : int [<MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)>] val Name : string end [<StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)>] type RR = struct val pNext:IntPtr val pName:string val wType:int16 val wDataLength:int16 val flags:int val dwTtl:int val dwReserved:int end [<StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)>] type MX_RR = struct val pNameExchange:string val wPreference:int16 end [<StructLayout(LayoutKind.Sequential)>] type AAAA_RR = struct [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)>] val addr:byte [] end [<StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)>] type CNAME_RR = struct val pCName:string end [<StructLayout(LayoutKind.Sequential)>] type A_RR = struct [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)>] val addr:byte[] end let name = "www.kame.net" let mutable buf = IntPtr.Zero; if 0 = DnsQuery(name,QueryTypes.DNS_TYPE_AAAA,QueryOptions.DNS_QUERY_BYPASS_CACHE, 0,&&buf, 0) then let mutable p = buf; while p<>IntPtr.Zero do let rr = Marshal.PtrToStructure(p,typeof<RR>) :?> RR//1リソースレコード単位で処理する if (rr.flags &&& 0x000f) = 0x9 then // Answer レコードのみを対象にする p <- IntPtr.Add(p,Marshal.SizeOf(typeof<RR>))//タイプごとのデータへ移動 match enum<QueryTypes>(int rr.wType) with |QueryTypes.DNS_TYPE_A -> let a = Marshal.PtrToStructure(p,typeof<A_RR>):?>A_RR printfn "%s" ((new IPAddress(a.addr)).ToString()) |QueryTypes.DNS_TYPE_AAAA -> let aaaa = Marshal.PtrToStructure(p, typeof<AAAA_RR>):?>AAAA_RR printfn "AAAA %s %s" rr.pName ((new IPAddress(aaaa.addr)).ToString()) |QueryTypes.DNS_TYPE_CNAME -> let cname = Marshal.PtrToStructure(p, typeof<CNAME_RR>):?>CNAME_RR printfn "CNAME %s" cname.pCName |QueryTypes.DNS_TYPE_MX -> let mx = Marshal.PtrToStructure(p, typeof<MX_RR>):?>MX_RR printfn "MX %d %s" mx.wPreference mx.pNameExchange |_ -> printfn "unknown" p <- rr.pNext//次のリソースレコード DnsRecordListFree(buf, 0);//メモリ開放 printfn "何かのキーを押してください。" Console.ReadKey() |> ignore
追記
このファンクションは、ping.exeのインポート関数を確認していて見つけました。
dumpbin ping.exe /IMPORTS
DNSAPI.dll
7FF7200BD74 63 DnsResolverOp
7FF7200313C 51 DnsQuery_W
7FF72003064 24 DnsFree
7FF72016670 27 DnsGetCacheDataTable
7FF72003828 4A DnsQueryConfigAllocEx
7FF720148D4 25 DnsFreeConfigStructure
7FF7201AC70 20 DnsFlushResolverCache