【问题标题】:C# Newbie: How do I fix this code to do a DNS lookup?C# 新手:如何修复此代码以进行 DNS 查找?
【发布时间】:2010-11-29 08:08:28
【问题描述】:

这是我第一次使用 C#,所以我非常不喜欢我的元素。但我相信 Stack Overflow 的优秀员工可以帮助我!我根据我在互联网上发现的其他一些代码提出了以下代码(如下)。我要做的是查找给定域的所有“文本”DNS 记录 (TXT)。我一开始很简单,使用 Visual C# 2008 Express 中的控制台应用程序查找 google.com 的记录。我会担心稍后根据命令行参数自定义域;现在我只想让这个工作。任何 DNS 查找工具都可以告诉您 google.com 在其 DNS 中有此 DNS 文本记录:
v=spf1 include:_netblocks.google.com ip4:216.73.93.70/31 ip4:216.73.93.72/31 ~all
但不幸的是,这不是我得到的。这是我的代码:

namespace DnsUtils
{
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Text;

    public class DnsTxt
    {
        [DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
        private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)]ref string pszName, QueryTypes wType, QueryOptions options, int aipServers, ref IntPtr ppQueryResults, int pReserved);

        [DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);

        public static string[] GetTXTRecords(string domain)
        {
            IntPtr ptr1 = IntPtr.Zero;
            IntPtr ptr2 = IntPtr.Zero;
            TXTRecord recTxt;

            if (Environment.OSVersion.Platform != PlatformID.Win32NT)
            {
                throw new NotSupportedException();
            }

            ArrayList list1 = new ArrayList();
            UnicodeEncoding encoding = new UnicodeEncoding();

            int num1 = DnsTxt.DnsQuery(ref domain, QueryTypes.DNS_TYPE_TEXT, QueryOptions.DNS_QUERY_BYPASS_CACHE, 0, ref ptr1, 0);
            if (num1 != 0)
            {
                throw new Win32Exception(num1);
            }

            for (ptr2 = ptr1; !ptr2.Equals(IntPtr.Zero); ptr2 = recTxt.pNext)
            {
                recTxt = (TXTRecord)Marshal.PtrToStructure(ptr2, typeof(TXTRecord));
                if (recTxt.wType == 16)
                {
                    IntPtr pointerToAddressStringArray = Marshal.AllocHGlobal(IntPtr.Size);
                    IntPtr addressStringArray = Marshal.ReadIntPtr(pointerToAddressStringArray);

                    for (int i = 0; i < recTxt.dwStringCount; i++)
                    {
                        IntPtr addressCharArray = Marshal.ReadIntPtr(recTxt.pStringArray, i * 4);
                        int offset = 0;

                        ArrayList bytesList = new ArrayList();
                        byte newByte = Marshal.ReadByte(addressCharArray, offset++);
                        while (newByte != 0)
                        {
                            bytesList.Add(newByte);
                            newByte = Marshal.ReadByte(addressCharArray, offset++);
                        }
                        byte[] bytesArray = new byte[offset];
                        bytesList.CopyTo(bytesArray);
                        string textValue = encoding.GetString(bytesArray);

                        list1.Add(textValue);
                    }

                    Marshal.FreeHGlobal(pointerToAddressStringArray);
                }
            }

            DnsTxt.DnsRecordListFree(ptr2, 0);
            return (string[])list1.ToArray(typeof(string));
        }

        private enum QueryOptions
        {
            DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1,
            DNS_QUERY_BYPASS_CACHE = 8,
            DNS_QUERY_DONT_RESET_TTL_VALUES = 0x100000,
            DNS_QUERY_NO_HOSTS_FILE = 0x40,
            DNS_QUERY_NO_LOCAL_NAME = 0x20,
            DNS_QUERY_NO_NETBT = 0x80,
            DNS_QUERY_NO_RECURSION = 4,
            DNS_QUERY_NO_WIRE_QUERY = 0x10,
            DNS_QUERY_RESERVED = -16777216,
            DNS_QUERY_RETURN_MESSAGE = 0x200,
            DNS_QUERY_STANDARD = 0,
            DNS_QUERY_TREAT_AS_FQDN = 0x1000,
            DNS_QUERY_USE_TCP_ONLY = 2,
            DNS_QUERY_WIRE_ONLY = 0x100
        }

        private enum QueryTypes
        {
            DNS_TYPE_TEXT = 16
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct TXTRecord
        {
            public IntPtr pNext;
            public string pName;
            public short wType;
            public short wDataLength;
            public int flags;
            public int dwTtl;
            public int dwReserved;
            public int dwStringCount;
            public IntPtr pStringArray;
        }

        static void Main(string[] args)
        {
            try
            {
                string[] s = DnsUtils.DnsTxt.GetTXTRecords("google.com");
                foreach (string st in s)
                {
                    Console.WriteLine("Value: {0}", st);
                }
            }
            catch (Win32Exception e)
            {
                Console.WriteLine(e.Message);
            }
            Console.ReadLine();
        }
    }
}

当我运行这段代码时,它会打印出Value: ?。现在我希望这意味着它实际上做了 DNS 查询并得到了预期的结果,但我只是在它试图将 IntPtr 转换为字符串数组的部分搞砸了(因为我猜它更难调试前者)。无论如何,有什么建议吗?谁能看到我哪里出错了?提前致谢。

【问题讨论】:

    标签: c# dns pinvoke marshalling dllimport


    【解决方案1】:

    这条线在我看来有点不对劲:

    IntPtr addressCharArray = Marshal.ReadIntPtr(recTxt.pStringArray, i * 4);
    

    您似乎将 TXT 记录的第一个字符串条目的前 4 个字节作为 IntPtr 读取。我认为是这样的:

    string s = Marshal.PtrToStringAuto(recTxt.pStringArray);
    

    会给你第一个条目。之后,我认为是这样的:

    IntPtr p = new IntPtr(recTxt.pStringArray.ToInt32() + sizeof(uint) * i);
    string s = Marshal.PtrToStringAuto(p);
    

    会得到余数。

    【讨论】:

    • 效果很好。谢谢!顺便问一下,你知道学习 C# 的任何好的免费资源吗?
    • 我要给出的第一个提示是不要从互操作开始! ;) 除此之外,我自己基本上只是一个 MSDN 人。
    【解决方案2】:

    作为一种更简单的替代方法,您可以通过创建新的 nslookup 进程并解析 StandardOuput 来获取您需要的内容,从而避免处理所有 COM 互操作。

    using System;
    using System.Diagnostics;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var startInfo = new ProcessStartInfo("nslookup");
                startInfo.Arguments = "-type=TXT google.com";
                startInfo.RedirectStandardOutput = true;
                startInfo.UseShellExecute = false;
                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
    
                using (var cmd = Process.Start(startInfo))
                {
                  // This is where you grab the output from nslookup.
                    Console.WriteLine(cmd.StandardOutput.ReadToEnd());
                }
                Console.Read();
            }
        }
    }
    

    【讨论】:

    • 但是你必须解析输出(不是微不足道的),当使用参数而不是硬连线的“google.com”时,你必须防止注入等。
    猜你喜欢
    • 2020-03-08
    • 1970-01-01
    • 2017-05-12
    • 1970-01-01
    • 1970-01-01
    • 2019-06-12
    • 1970-01-01
    • 2010-10-28
    • 2012-07-09
    相关资源
    最近更新 更多