GetHostEntry is very slow

前端 未结 5 1004
情书的邮戳
情书的邮戳 2020-12-05 08:25

I have a WinForms app, and I\'m trying to get reverse DNS entries for a list of IPs displayed on the form.

The main issue I\'ve run into is System.Net.Dns.GetHostEn

5条回答
  •  被撕碎了的回忆
    2020-12-05 08:49

    You can improve the speed of a failed lookup considerably by querying the in-addr.arpa domain. E.g to perform a reverse IP lookup for IP address A.B.C.D you should query DNS for the domain D.C.B.A.in-addr.arpa. If reverse lookup is possible a PTR record with the host name is returned.

    Unfortunately .NET does not have a general API for querying DNS. But by using P/Invoke you can call the DNS API to get the desired result (the function will return null if the reverse lookup fails).

    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;
    }
    

提交回复
热议问题