2009-06-15 223 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); 
}