2009-06-15 226 views
7

我有一个WinForms应用程序,我试图获取表单上显示的IP列表的反向DNS条目。GetHostEntry非常慢

我碰到的主要问题是System.Net.Dns.GetHostEntry是可笑缓慢,特别是在没有反向DNS条目中找到。使用直接DNS时,这应该很快,因为DNS服务器将返回NXDOMAIN。在内部,它调用ws2_32.dll getnameinfo(),其中声明“名称解析可以通过域名系统(DNS),本地主机文件或其他命名机制” - 所以我假设它是那些“其他命名机制”,导致它这么慢,但有人知道这些机制是什么?

一般来说,这是服用5每秒的IP,除非它找到一个相反的条目,然后它的几乎是即时的。我已经使用线程部分解决了这个问题,但是由于我正在做一个大的列表,而且我只能同时运行这么多的线程,所以还需要一段时间才能完成。

是否有更好的方法来查找反向DNS条目将会更快?

回答

5

不幸的是,在客户端的Windows API中改变这种超时是没有办法的(我知道)。最好的办法是编辑注册表来改变DNS查询中超时的长度。详情请参阅this technet article。据我所知,尝试1,2,& 3时,你这样做,因此5秒的延迟。

唯一的其他选择是使用某种形式的后台处理的,诸如此asynchronous version反向DNS查找的。但是,这将使用线程,所以最终你会遇到超时(这会更好,因为它会跨越很多等待线程,但仍然不完美)。就个人而言,如果你要处理一个庞大的数字,我会混合使用这两种方法 - ansyncrhonously进行反向查找,并修改注册表以缩短超时时间。评论后


编辑:

如果你看一下在getnameinfo标志,有一个标志参数。我相信你可以P/Invoke到这个并设置标志NI_NAMEREQD | NI_NUMERICHOST来获得你以后的行为。 (第一次说要立刻报错了,如果没有DNS条目,这有助于超时 - 第二个说做反向查找。)

+1

我实际上确实使用该版本开始。它有效地解决了超时问题。我的问题更多的是必须暂停。去运行nslookup或用一些随机的IP在命令行上挖 - 它通常将返回<1秒,说“*** server.pf.local找不到42.23.1.42:不存在的域名”(或NXDOMAIN,在挖的情况下) - 我想知道为什么GetHostEntry()不以相同的方式工作。 – gregmac 2009-06-15 17:37:22

+0

我相信你可以通过P/Invoke完成你想要的,通过使用不同的标志而不是getnameinfo上的默认值。看我的编辑。 – 2009-06-15 18:14:38

0

主要加入,以防有人通过谷歌找到这个评论,因为我做到了。 ...

该行为可能是OS版本特定的;这些说明适用于Server 2008 R2。

NI_NUMERICHOST标志不会做你想要的;这种情况下API返回主机标识符的数字版本(即:IP地址),而不是主机名。

即使NI_NAMEREQD,仍有一个超时如果信息没有找到(缺省为5秒)。我不确定这是由于级联查找超时还是其他原因,但此标志不会阻止超时(也不会有其他标志,据我所知)。

看来这调用API WSALookupService内部的,虽然目前还不清楚正在传递什么样的标志。另外请注意,返回的信息可能不正确;在我的其中一个测试用例中,nslookup没有返回任何结果,但getnameinfo返回的是不准确且不合格的名称。所以...是的,没有好的答案,但希望这些信息是有帮助的。

8

也许这可以帮到您?的dead link.

(Dead link: 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; 
    } 
} 
+1

死链接... :( – 2016-09-22 17:24:30

4

可以大大地通过查询in-addr.arpa域提高故障查找的速度。 E.g执行反向IP查找的IP地址A.B.C.D您应该查询的DNS域D.C.B.A.in-addr.arpa。如果反向查找是可能的,则返回带有主机名的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; 
} 
+0

这工作得很好的解析地址,但是当我把它无法解决的地址挂起。 – JimSTAT 2015-04-01 18:55:52

0

在任何情况下,达到这个......

我使用的TcpClient的构造函数调用过时Dns.GetHostByName而不是切换。

无论出于何种原因,它执行好得多。

public TcpClientIP(string hostname, int port) : base() 
{ 
    try 
    { 
     if (_legacyDnsEnabled) 
     { 
      var host = Dns.GetHostByName(hostname); 
      var ips = host.AddressList.Select(o => new IPAddress(o.GetAddressBytes())).ToArray(); 
      Connect(ips, port); 
      return; 
     } 
    } 
    catch(SocketException e) 
    { } 

    Connect(hostname, port); 
}