如果您仍然感兴趣,这是我的经验。我使用 DnsQuery_W,速度足够快,无需异步操作,而且不像 DnsQueryEx 那样复杂。您可以很好地指定多个 DNS 服务器地址并也使用 IPv6。您肯定安装了 Powershell。如果您使用 ILSpy 或 .NET Reflector 查看已安装模块中的“dnslookup.dll”文件,您会在“ResolveDnsName”下找到一个如何使用 DnsQuery_W 的很好示例。它是 Powershell 模块“Resolve-DnsName”的源代码。反向查询比 GetHostEntry 快得多。
这是一个 - 不是很短 - 总结。我仅将 DnsQuery 用于反向查询,因此这是一个真正精简的示例。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
public class DnsQuerySample
{
#region structs and enums
[StructLayout(LayoutKind.Sequential)]
public struct sockaddr_in
{
private ushort Family;
private ushort sin6_port;
private uint IP4Addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8, ArraySubType = UnmanagedType.U1)]
private char[] zero;
}
[StructLayout(LayoutKind.Sequential)]
public struct sockaddr_in6
{
internal ushort sin6_family;
private ushort sin6_port;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U1)]
public byte[] IP4Addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
public byte[] IP6Address;
private uint sin6_scope_id;
}
[StructLayout(LayoutKind.Explicit)]
internal struct DnsAddr
{
[FieldOffset(0)]
internal sockaddr_in6 SockAddr;
[FieldOffset(0x20)]
internal uint SockAddrLength;
[FieldOffset(0x24)]
internal uint SubnetLength;
[FieldOffset(40)]
internal uint Flags;
[FieldOffset(0x2c)]
internal uint Status;
[FieldOffset(0x30)]
internal uint Priority;
[FieldOffset(0x34)]
internal uint Weight;
[FieldOffset(0x38)]
internal uint Tag;
[FieldOffset(60)]
internal uint PayloadSize;
}
[StructLayout(LayoutKind.Sequential)]
internal struct DnsAddrArray
{
internal uint MaxCount;
internal uint AddrCount;
private uint Tag;
internal ushort Family;
private ushort Reserved;
private uint Flags;
private uint MatchFlag;
private uint Reserved1;
private uint Reserved2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
internal DnsAddr[] DnsAddr;
}
[StructLayout(LayoutKind.Sequential, Size = 120)]
internal struct DnsExtraInfo
{
public uint Version;
public uint Size;
public IntPtr pNext;
public uint ID;
public uint Reserved;
public IntPtr ServerList;
public DnsExtraInfo(IPAddress[] DnsServers)
{
this = new DnsExtraInfo();
this.ID = 10;
this.Version = 0x80000001;
this.pNext = IntPtr.Zero;
DnsAddrArray structure = new DnsAddrArray
{
AddrCount = (uint)DnsServers.Length,
MaxCount = 5,
DnsAddr = new DnsAddr[5]
};
for (int i = 0; (i < DnsServers.Length) && (i < 5); i++)
{
structure.Family = (ushort)DnsServers[i].AddressFamily;
structure.DnsAddr[i].SockAddr.sin6_family = (ushort)DnsServers[i].AddressFamily;
if (DnsServers[i].AddressFamily == ((AddressFamily)((int)AddressFamily.InterNetworkV6)))
{
structure.DnsAddr[i].SockAddr.IP6Address = DnsServers[i].GetAddressBytes();
structure.DnsAddr[i].SockAddrLength = (uint)Marshal.SizeOf<sockaddr_in6>();
}
else
{
structure.DnsAddr[i].SockAddr.IP4Addr = DnsServers[i].GetAddressBytes();
structure.DnsAddr[i].SockAddrLength = (uint)Marshal.SizeOf<sockaddr_in>();
}
}
this.ServerList = Marshal.AllocHGlobal(Marshal.SizeOf<DnsAddrArray>());
Marshal.StructureToPtr<DnsAddrArray>(structure, this.ServerList, false);
}
}
internal enum RecordType : ushort
{
PTR = 12
}
internal enum QueryOptions : ulong
{
DNS_QUERY_BYPASS_CACHE = 0x00000008, // Bypasses the resolver cache on the lookup.
DNS_QUERY_NO_HOSTS_FILE = 0x00000040, // Prevents the DNS query from consulting the HOSTS file.Windows 2000 Server and Windows 2000 Professional: This value is not supported.
DNS_QUERY_NO_NETBT = 0x00000080, // Prevents the DNS query from using NetBT for resolution.Windows 2000 Server and Windows 2000 Professional: This value is not supported.
DNS_QUERY_WIRE_ONLY = 0x00000100, // Directs DNS to perform a query using the network only, bypassing local information.Windows 2000 Server and Windows 2000 Professional: This value is not supported.
DNS_QUERY_MULTICAST_ONLY = 0x00000400, // Prevents the query from using DNS and uses only Local Link Multicast Name Resolution (LLMNR).Windows Vista and Windows Server 2008 or later.: This value is supported.
DNS_QUERY_NO_MULTICAST = 0x00000800, // (opposite of DNS_QUERY_MULTICAST_ONLY)
DNS_QUERY_TREAT_AS_FQDN = 0x00001000 // Prevents the DNS response from attaching suffixes to the submitted name in a name resolution process.
}
/// <summary>
/// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms682082(v=vs.85).aspx
/// These field offsets could be different depending on endianness and bitness
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct DnsRecord
{
public IntPtr pNext; // DnsRecord*
public IntPtr pName; // string
public RecordType wType;
public ushort wDataLength;
public FlagsUnion Flags;
public uint dwTtl;
public uint dwReserved; // maybe QuestionClass, alway be 1 (DNS_CLASS_INTERNET)
public DataUnion Data;
}
[StructLayout(LayoutKind.Explicit)]
internal struct DataUnion
{
[FieldOffset(0)]
public DNS_PTR_DATA PTR, NS, CNAME, DNAME, MB, MD, MF, MG, MR;
[FieldOffset(0)]
public IntPtr pDataPtr;
}
/// <summary>
/// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms682080(v=vs.85).aspx
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct DNS_PTR_DATA
{
public IntPtr pNameHost; // string
public string NameHost { get { return Marshal.PtrToStringAuto(pNameHost); } }
}
[StructLayout(LayoutKind.Explicit)]
internal struct FlagsUnion
{
[FieldOffset(0)]
public uint DW;
[FieldOffset(0)]
public DnsRecordFlags S;
}
/// <summary>
/// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms682084(v=vs.85).aspx
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct DnsRecordFlags
{
internal uint data;
// DWORD Section :2;
public DnsSection Section
{
get { return (DnsSection)(data & 0x3u); }
set { data = (data & ~0x3u) | (((uint)value) & 0x3u); }
}
// DWORD Delete :1;
public uint Delete // Reserved. Do not use.
{
get { return (data >> 2) & 0x1u; }
set { data = (data & ~(0x1u << 2)) | (value & 0x1u) << 2; }
}
// DWORD CharSet :2;
public DnsCharset CharSet
{
get { return (DnsCharset)((data >> 3) & 0x3u); }
set { data = (data & ~(0x3u << 3)) | (((uint)value) & 0x3u) << 3; }
}
// DWORD Unused :3;
public uint Unused // Reserved. Do not use.
{
get { return (data >> 5) & 0x7u; }
set { data = (data & ~(0x7u << 5)) | (value & 0x7u) << 5; }
}
// DWORD Reserved :24;
public uint Reserved // Reserved. Do not use.
{
get { return (data >> 8) & 0xFFFFFFu; }
set { data = (data & ~(0xFFFFFFu << 8)) | (value & 0xFFFFFFu) << 8; }
}
}
internal enum DnsSection : uint
{
Question = 0,
Answer = 1,
Authority = 2,
Additional = 3
}
internal enum DnsCharset : uint
{
Unknown = 0,
Unicode = 1,
Utf8 = 2,
Ansi = 3
}
/// <summary>
/// Call Win32 DNS API DnsQuery.
/// </summary>
/// <param name="pszName">Host name.</param>
/// <param name="wType">DNS Record type.</param>
/// <param name="options">DNS Query options.</param>
/// <param name="pExtra">Fka aipServers, now reserved but functional; IP4AddressArray or DnsExtraInfo, we use DnsExtraInfo.</param>
/// <param name="ppQueryResults">Query results.</param>
/// <param name="pReserved">Reserved argument.</param>
/// <returns>WIN32 status code</returns>
/// <remarks>For aipServers, DnqQuery expects either null or an array of one IPv4 address.</remarks>
[DllImport("dnsapi", CharSet = CharSet.Unicode, EntryPoint = "DnsQuery_W", ExactSpelling = true, SetLastError = true)]
private static extern int _DnsQueryW(
[In] string pszName,
[In] ushort wType,
[In] uint options,
[In][Out] ref DnsExtraInfo pExtra,
[Out] out IntPtr ppQueryResults,
[Out] IntPtr pReserved);
internal static int DnsQuery(string pszName, RecordType wType, QueryOptions options, ref DnsExtraInfo pExtra, out IntPtr ppQueryResults)
{
return _DnsQueryW(pszName, (ushort)wType, (uint)options, ref pExtra, out ppQueryResults, IntPtr.Zero);
}
/// <summary>
/// Call Win32 DNS API DnsRecordListFree.
/// </summary>
/// <param name="pRecordList">DNS records pointer</param>
/// <param name="FreeType">Record List Free type</param>
[DllImport("dnsapi", CharSet = CharSet.Auto, EntryPoint = "DnsRecordListFree", ExactSpelling = true, SetLastError = true)]
private static extern void _DnsRecordListFree(IntPtr pRecordList, int FreeType);
#endregion structs and enums
internal static void DnsRecordListFree(IntPtr pRecordList)
{
_DnsRecordListFree(pRecordList, 0);
}
public static List<string> GetHostName(IPAddress iPAddress, IPAddress[] DNSServerAddresses)
{
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
{
throw new NotSupportedException();
}
// Use DNS only, fastest queries with these options.
QueryOptions options = QueryOptions.DNS_QUERY_BYPASS_CACHE |
QueryOptions.DNS_QUERY_NO_HOSTS_FILE |
QueryOptions.DNS_QUERY_NO_MULTICAST |
QueryOptions.DNS_QUERY_NO_NETBT |
QueryOptions.DNS_QUERY_TREAT_AS_FQDN |
QueryOptions.DNS_QUERY_WIRE_ONLY;
DnsExtraInfo pExtra = new DnsExtraInfo();
if (DNSServerAddresses != null)
{
if (DNSServerAddresses.Length > 5)
{
throw new ArgumentException("A maximum of 5 addresses are supported!", "DNSServerAddresses");
}
pExtra = new DnsExtraInfo(DNSServerAddresses);
}
var recordsArray = IntPtr.Zero;
string reverse = ToReverseLookup(iPAddress);
try
{
var result = DnsQuery(reverse,
RecordType.PTR,
options,
ref pExtra,
out recordsArray);
if (result != 0)
{
Win32Exception ex = new Win32Exception(result);
throw new Win32Exception(result, iPAddress.ToString() + " : " + ex.Message);
}
DnsRecord record;
var recordList = new List<string>();
for (var recordPtr = recordsArray; !recordPtr.Equals(IntPtr.Zero); recordPtr = record.pNext)
{
record = (DnsRecord)Marshal.PtrToStructure(recordPtr, typeof(DnsRecord));
if (record.wType == RecordType.PTR)
{
recordList.Add(record.Data.PTR.NameHost);
//break; // one name is enough
}
}
return recordList;
}
finally
{
if (recordsArray != IntPtr.Zero)
{
DnsRecordListFree(recordsArray);
}
if (pExtra.ServerList != IntPtr.Zero)
{
Marshal.FreeHGlobal(pExtra.ServerList);
pExtra.ServerList = IntPtr.Zero;
}
}
}
internal static string ToReverseLookup(IPAddress iPAddress)
{
string text = iPAddress.ToString();
if (iPAddress.AddressFamily == AddressFamily.InterNetwork)
{
string[] array = text.Split(new char[1] { '.' });
text = array[3] + "." + array[2] + "." + array[1] + "." + array[0] + ".in-addr.arpa";
}
else
{
if (iPAddress.AddressFamily != AddressFamily.InterNetworkV6)
{
throw new NotSupportedException();
}
text = "ip6.arpa";
byte[] addressBytes = iPAddress.GetAddressBytes();
foreach (byte num in addressBytes)
{
byte b = (byte)(num & 0xFu);
byte b2 = (byte)((num & 0xF0) >> 4);
text = string.Format("{0:X}.{1:X}.{2}", b, b2, text);
}
}
return text;
}
public DnsQuerySample()
{ }
}
您可以将其用于:
IPAddress[] servers =
{
IPAddress.Parse("1.1.1.1"),
IPAddress.Parse("8.8.8.8"),
IPAddress.Parse("2606:4700:4700::1111"),
IPAddress.Parse("2001:4860:4860::8888")
};
IPAddress search = IPAddress.Parse("2a02:2e0:3fe:1001:302::");
var ret = DnsQuerySample.GetHostName(search, servers);
foreach (var name in ret)
{
Console.WriteLine(name);
}
使用 Wireshark 或防火墙,您可以看到它适用于 IPv6,并且 DnsQuery 使用服务器地址。不知道DnsQuery中是否存储了对5个地址的限制。如果相应地调整结构,也许可以增加数量。玩得开心!