我有一个WinForms应用程序,我正在尝试获取表单上显示的IP列表的反向DNS条目.
我遇到的主要问题是System.Net.Dns.GetHostEntry非常慢,特别是当没有找到反向DNS条目时.使用直接DNS,这应该很快,因为DNS服务器将返回NXDOMAIN.在内部,它调用ws2_32.dll中则getnameinfo() ,其中规定"名称解析可以通过域名系统(DNS),本地hosts文件,或通过其他命名机制" -所以我假设它的那些"其他命名机制"这导致它如此缓慢,但有谁知道这些机制是什么?
通常,每个IP需要5秒,除非它找到反向条目,然后它几乎是立即的.我使用线程部分地解决了这个问题,但由于我正在做一个大型列表而且我只能同时运行这么多线程,所以它仍然需要一段时间来完成它们.
有没有更好的方法来查找更快的反向DNS条目?
ra0*_*00l 11
也许这有用吗?WayBack Machine版本的死链接.
(死链接:http://www.chapleau.info/blog/2008/09/09/reverse-dns-lookup-with-timeout-in-c.html)
代码,为子孙后代:
private delegate IPHostEntry GetHostEntryHandler(string ip);
public string GetReverseDNS(string ip, int timeout)
{
try
{
GetHostEntryHandler callback = new GetHostEntryHandler(Dns.GetHostEntry);
IAsyncResult result = callback.BeginInvoke(ip,null,null);
if (result.AsyncWaitHandle.WaitOne(timeout, false))
{
return callback.EndInvoke(result).HostName;
}
else
{
return ip;
}
}
catch (Exception)
{
return ip;
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,没有办法(我知道)在客户端的Windows API中更改此超时.您可以做的最好的事情是编辑注册表以更改DNS查询中超时的长度.有关详细信息,请参阅此technet文章.据我所知,当你这样做时会尝试1,2和3,因此延迟5秒.
唯一的另一种选择是使用某种形式的后台处理,例如反向DNS查找的这种异步版本.但是,这将使用线程,所以你最终会遇到超时(它会更好,因为它将跨越许多等待线程,但仍然不完美).就个人而言,如果你要处理一个庞大的数字,我会混合两种方法 - 反复查找和修改注册表以缩短超时.
评论后编辑:
如果查看getnameinfo上的标志,就会有一个flags参数.我相信你可以P/Invoke到这个并设置标志NI_NAMEREQD | NI_NUMERICHOST来获得你所追求的行为.(如果没有DNS条目,第一个说立即出错,这有助于超时 - 第二个说反向查找.)
您可以通过查询in-addr.arpa域来显着提高失败查找的速度。例如,要对 IP 地址ABCD执行反向 IP 查找,您应该查询域DCBAin-addr.arpa 的DNS 。如果可以进行反向查找,则返回带有主机名的 PTR 记录。
不幸的是,.NET 没有用于查询 DNS 的通用 API。但是通过使用 P/Invoke,您可以调用 DNS API 以获得所需的结果(null如果反向查找失败,该函数将返回)。
using System;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
public static String ReverseIPLookup(IPAddress ipAddress) {
if (ipAddress.AddressFamily != AddressFamily.InterNetwork)
throw new ArgumentException("IP address is not IPv4.", "ipAddress");
var domain = String.Join(
".", ipAddress.GetAddressBytes().Reverse().Select(b => b.ToString())
) + ".in-addr.arpa";
return DnsGetPtrRecord(domain);
}
static String DnsGetPtrRecord(String domain) {
const Int16 DNS_TYPE_PTR = 0x000C;
const Int32 DNS_QUERY_STANDARD = 0x00000000;
const Int32 DNS_ERROR_RCODE_NAME_ERROR = 9003;
IntPtr queryResultSet = IntPtr.Zero;
try {
var dnsStatus = DnsQuery(
domain,
DNS_TYPE_PTR,
DNS_QUERY_STANDARD,
IntPtr.Zero,
ref queryResultSet,
IntPtr.Zero
);
if (dnsStatus == DNS_ERROR_RCODE_NAME_ERROR)
return null;
if (dnsStatus != 0)
throw new Win32Exception(dnsStatus);
DnsRecordPtr dnsRecordPtr;
for (var pointer = queryResultSet; pointer != IntPtr.Zero; pointer = dnsRecordPtr.pNext) {
dnsRecordPtr = (DnsRecordPtr) Marshal.PtrToStructure(pointer, typeof(DnsRecordPtr));
if (dnsRecordPtr.wType == DNS_TYPE_PTR)
return Marshal.PtrToStringUni(dnsRecordPtr.pNameHost);
}
return null;
}
finally {
const Int32 DnsFreeRecordList = 1;
if (queryResultSet != IntPtr.Zero)
DnsRecordListFree(queryResultSet, DnsFreeRecordList);
}
}
[DllImport("Dnsapi.dll", EntryPoint = "DnsQuery_W", ExactSpelling=true, CharSet = CharSet.Unicode, SetLastError = true)]
static extern Int32 DnsQuery(String lpstrName, Int16 wType, Int32 options, IntPtr pExtra, ref IntPtr ppQueryResultsSet, IntPtr pReserved);
[DllImport("Dnsapi.dll", SetLastError = true)]
static extern void DnsRecordListFree(IntPtr pRecordList, Int32 freeType);
[StructLayout(LayoutKind.Sequential)]
struct DnsRecordPtr {
public IntPtr pNext;
public String pName;
public Int16 wType;
public Int16 wDataLength;
public Int32 flags;
public Int32 dwTtl;
public Int32 dwReserved;
public IntPtr pNameHost;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
15446 次 |
| 最近记录: |