GetHostEntry非常慢

gre*_*mac 8 .net dns

我有一个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)


Ree*_*sey 5

不幸的是,没有办法(我知道)在客户端的Windows API中更改此超时.您可以做的最好的事情是编辑注册表以更改DNS查询中超时的长度.有关详细信息,请参阅此technet文章.据我所知,当你这样做时会尝试1,2和3,因此延迟5秒.

唯一的另一种选择是使用某种形式的后台处理,例如反向DNS查找的这种异步版本.但是,这将使用线程,所以你最终会遇到超时(它会更好,因为它将跨越许多等待线程,但仍然不完美).就个人而言,如果你要处理一个庞大的数字,我会混合两种方法 - 反复查找和修改注册表以缩短超时.


评论后编辑:

如果查看getnameinfo上的标志,就会有一个flags参数.我相信你可以P/Invoke到这个并设置标志NI_NAMEREQD | NI_NUMERICHOST来获得你所追求的行为.(如果没有DNS条目,第一个说立即出错,这有助于超时 - 第二个说反向查找.)


Mar*_*age 5

您可以通过查询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)