代码之家  ›  专栏  ›  技术社区  ›  Segfault

如何使用system.net.dns获取DNS名称的MX记录?

  •  41
  • Segfault  · 技术社区  · 14 年前

    .NET库中是否有任何内置方法将返回给定域的所有MX记录?我知道你是怎么得到CNAME的,但没有MX记录。

    7 回复  |  直到 6 年前
        1
  •  12
  •   MichaC    7 年前

    只是在我自己的库中滚动,因为没有.NET核心/xplat支持… https://github.com/MichaCo/DnsClient.NET

    它工作得很好,给了你 dig 如果你愿意的话,比如日志消息。

    简单易用

    var lookup = new LookupClient();
    var result = await lookup.QueryAsync("google.com", QueryType.ANY);
    

    并与运行在任何端口、多个服务器等上的自定义服务器一起工作…

    也见 DnsClient Website 有关详细信息

        2
  •  40
  •   Michael Kropat    6 年前

    更新2018/5/23 :

    退房 MichaC's answer 对于具有.NET标准支持的新库。

    原始答案:

    这个 ARSoft.Tools.Net 亚历山大雷内特的图书馆似乎做得很好。

    Nuget提供:

    PM> Install-Package ARSoft.Tools.Net
    

    导入命名空间:

    using ARSoft.Tools.Net.Dns;
    

    然后进行同步查找就简单到:

    var resolver = new DnsStubResolver();
    var records = resolver.Resolve<MxRecord>("gmail.com", RecordType.Mx);
    foreach (var record in records) {
        Console.WriteLine(record.ExchangeDomainName?.ToString());
    }
    

    这给了我们输出:

    gmail-smtp-in.l.google.com.
    alt1.gmail-smtp-in.l.google.com.
    alt2.gmail-smtp-in.l.google.com.
    alt3.gmail-smtp-in.l.google.com.
    alt4.gmail-smtp-in.l.google.com.
    

    在引擎盖下面,看起来库构造了发送到解析器所需的UDP(或TCP)数据包,正如您可能期望的那样。库甚至有逻辑(用 DnsClient.Default )以发现要查询的DNS服务器。

    可以找到完整的文档 here .

        3
  •  9
  •   Christian    13 年前

    我花了一整天的时间研究如何发送/接收DNS请求,并提出了这个问题。它是一个完整的通用处理程序。你只需要设置DNS服务器并输入'd'例如my.website.com?D = ITMANX.com

    <%@ WebHandler Language="C#" Class="Handler" %>
    
    using System;
    using System.Web;
    using System.Text;
    using System.Net;
    using System.Net.Sockets;
    using System.Collections.Generic;
    
    public class Handler : IHttpHandler
    {
        string dns = "dc1";  //change to your dns
        string qtype = "15"; //A=1  MX=15
        string domain = "";
        int[] resp;
    
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
    
            try
            {
                if (context.Request["t"] != null) qtype = context.Request["t"];
                if (context.Request["d"] != null) domain = context.Request["d"];
    
                if (string.IsNullOrEmpty(domain)) throw new Exception("Add ?d=<domain name> to url or post data");
    
                Do(context);
            }
            catch (Exception ex)
            {
                string msg = ex.Message;
                if (msg == "1") msg = "Malformed packet";
                else if (msg == "5") msg = "Refused";
                else if (msg == "131") msg = "No such name";
    
                context.Response.Write("Error: " + msg);
            }
        }
    
        public void Do(HttpContext context)
        {
            UdpClient udpc = new UdpClient(dns, 53);
    
            // SEND REQUEST--------------------
            List<byte> list = new List<byte>();
            list.AddRange(new byte[] { 88, 89, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 });
    
            string[] tmp = domain.Split('.');
            foreach (string s in tmp)
            {
                list.Add(Convert.ToByte(s.Length));
                char[] chars = s.ToCharArray();
                foreach (char c in chars)
                    list.Add(Convert.ToByte(Convert.ToInt32(c)));
            }
            list.AddRange(new byte[] { 0, 0, Convert.ToByte(qtype), 0, 1 });
    
            byte[] req = new byte[list.Count];
            for (int i = 0; i < list.Count; i++) { req[i] = list[i]; }
    
            udpc.Send(req, req.Length);
    
    
            // RECEIVE RESPONSE--------------
            IPEndPoint ep = null;
            byte[] recv = udpc.Receive(ref ep);
            udpc.Close();
    
            resp = new int[recv.Length];
            for (int i = 0; i < resp.Length; i++)
                resp[i] = Convert.ToInt32(recv[i]);
    
            int status = resp[3];
            if (status != 128) throw new Exception(string.Format("{0}", status));
            int answers = resp[7];
            if (answers == 0) throw new Exception("No results");
    
            int pos = domain.Length + 18;
            if (qtype == "15") // MX record
            {
                while (answers > 0)
                {
                    int preference = resp[pos + 13];
                    pos += 14; //offset
                    string str = GetMXRecord(pos, out pos);
                    context.Response.Write(string.Format("{0}: {1}\n", preference, str));
                    answers--;
                }
            }
            else if (qtype == "1") // A record
            {
                while (answers > 0)
                {
                    pos += 11; //offset
                    string str = GetARecord(ref pos);
                    context.Response.Write(string.Format("{0}\n", str));
                    answers--;
                }
            }
        }
    
        //------------------------------------------------------
        private string GetARecord(ref int start)
        {
            StringBuilder sb = new StringBuilder();
    
            int len = resp[start];
            for (int i = start; i < start + len; i++)
            {
                if (sb.Length > 0) sb.Append(".");
                sb.Append(resp[i + 1]);
            }
            start += len + 1;
            return sb.ToString();
        }
        private string GetMXRecord(int start, out int pos)
        {
            StringBuilder sb = new StringBuilder();
            int len = resp[start];
            while (len > 0)
            {
                if (len != 192)
                {
                    if (sb.Length > 0) sb.Append(".");
                    for (int i = start; i < start + len; i++)
                        sb.Append(Convert.ToChar(resp[i + 1]));
                    start += len + 1;
                    len = resp[start];
                }
                if (len == 192)
                {
                    int newpos = resp[start + 1];
                    if (sb.Length > 0) sb.Append(".");
                    sb.Append(GetMXRecord(newpos, out newpos));
                    start++;
                    break;
                }
            }
            pos = start + 1;
            return sb.ToString();
        }
    
        //------------------------------------------------------
        public bool IsReusable { get { return false; } }
    }
    
        4
  •  2
  •   Krishnan Venkiteswaran    7 年前

    接受的答案不适用于.NET Framework<4.5,因此建议不能使用arsoft.tools的人可以使用来自的dndndns https://dndns.codeplex.com

    下面给出的是一个控制台应用程序,它返回给定域的MX记录,修改它们的示例。

    using System;
    using System.Net.Sockets;
    using DnDns.Enums;
    using DnDns.Query;
    using DnDns.Records;
    
    namespace DnDnsExamples
    {
    class Program
    {
        static void Main(string[] args)
        {
            DnsQueryRequest request3 = new DnsQueryRequest();
            DnsQueryResponse response3 = request3.Resolve("gmail.com", NsType.MX, NsClass.INET, ProtocolType.Tcp);
            OutputResults(response3);
            Console.ReadLine();
        }
    
        private static void OutputResults(DnsQueryResponse response)
        {
            foreach (IDnsRecord record in response.Answers)
            {
                Console.WriteLine(record.Answer);
                Console.WriteLine("  |--- RDATA Field Length: " + record.DnsHeader.DataLength);
                Console.WriteLine("  |--- Name: " + record.DnsHeader.Name);
                Console.WriteLine("  |--- NS Class: " + record.DnsHeader.NsClass);
                Console.WriteLine("  |--- NS Type: " + record.DnsHeader.NsType);
                Console.WriteLine("  |--- TTL: " + record.DnsHeader.TimeToLive);
                Console.WriteLine();
            }            
        }
    }
    }
    
        5
  •  1
  •   Joseph Philbert    10 年前

    这是一个我只用来查找MX记录的类。

        using System;
        using System.Text;
        using System.Net;
        using System.Net.Sockets;
        using System.Collections.Specialized;
    
        namespace Mx.Dns
        {
            public class Query
            {
                //Build a DNS query buffer according to RFC 1035 4.1.1 e 4.1.2
                private readonly int id;
            private readonly int flags;
            private readonly int QDcount;
            private readonly int ANcount;
            private readonly int NScount;
            private readonly int ARcount;
            private readonly string Qname;
            private readonly int Qtype;
            private readonly  int Qclass;
            public byte[] buf;
    
            public Query(int ID, string query, int qtype)
            {
                //init vectors with given + default values
                id = ID;
                flags = 256;
                QDcount = 1;
                ANcount = 0;
                NScount = 0;
                ARcount = 0;
                Qname = query;
                Qtype = qtype;
                Qclass = 1; //Internet = IN = 1
    
                //build a buffer with formatted query data
    
                //header information (16 bit padding
                buf = new byte[12 + Qname.Length + 2 + 4];
                buf[0] = (byte)(id / 256);
                buf[1] = (byte)(id - (buf[0] * 256));
                buf[2] = (byte)(flags / 256);
                buf[3] = (byte)(flags - (buf[2] * 256));
                buf[4] = (byte)(QDcount / 256);
                buf[5] = (byte)(QDcount - (buf[4] * 256));
                buf[6] = (byte)(ANcount / 256);
                buf[7] = (byte)(ANcount - (buf[6] * 256));
                buf[8] = (byte)(NScount / 256);
                buf[9] = (byte)(NScount - (buf[8] * 256));
                buf[10] = (byte)(ARcount / 256);
                buf[11] = (byte)(ARcount - (buf[10] * 256));
                //QNAME (RFC 1035 4.1.2)
                //no padding
                string[] s = Qname.Split('.');
                int index = 12;
                foreach (string str in s) {
                    buf[index] = (byte)str.Length;
                    index++;
                    byte[] buf1 = Encoding.ASCII.GetBytes(str);
                    buf1.CopyTo(buf, index);
                    index += buf1.Length;
                }
                //add root domain label (chr(0))
                buf[index] = 0;
    
                //add Qtype and Qclass (16 bit values)
                index = buf.Length - 4;
                buf[index] = (byte)(Qtype / 256);
                buf[index + 1] = (byte)(Qtype - (buf[index] * 256));
                buf[index + 2] = (byte)(Qclass / 256);
                buf[index + 3] = (byte)(Qclass - (buf[index + 2] * 256));
            }
        }
        public class C_DNSquery
        {
            public StringCollection result = new StringCollection();
            public int Error = 0;
            public string ErrorTxt = "undefined text";
            public bool Done = false;
            public UdpClient udpClient;
            private string DNS;
            private string Query;
            private int Qtype;
            public bool IS_BLACKLIST_QUERY = false;
            public C_DNSquery(string IPorDNSname, string query, int type)
            {
                DNS = IPorDNSname;
                Query = query;
                Qtype = type;
            }
            public void doTheJob()
            {
                //check if provided DNS contains an IP address or a name
                IPAddress ipDNS;
                IPHostEntry he;
                try {
                    //try to parse an IPaddress
                    ipDNS = IPAddress.Parse(DNS);
                } catch (FormatException ) {
    //              Console.WriteLine(e);
                    //format error, probably is a FQname, try to resolve it
                    try {
                        //try to resolve the hostname
                        he = Dns.GetHostEntry(DNS);
                    } catch {
                        //Error, invalid server name or address
                        Error = 98;
                        ErrorTxt = "Invalid server name:" + DNS;
                        Done = true;
                        return;
                    }
                    //OK, get the first server address
                    ipDNS = he.AddressList[0];
                }
    
                //Query the DNS server
                //our current thread ID is used to match the reply with this process
    
                Query myQuery = new Query(System.Threading.Thread.CurrentThread.ManagedThreadId, Query, Qtype);
                //data buffer for query return value
                Byte[] recBuf;
    
                //use UDP protocol to connect
                udpClient = new UdpClient();
                do {
                    try {
                        //connect to given nameserver, port 53 (DNS)
                        udpClient.Connect(DNS, 53);
                        //send query
                        udpClient.Send(myQuery.buf, myQuery.buf.Length);
                        //IPEndPoint object allow us to read datagrams..
                        //..selecting only packet coming from our nameserver and port
                        IPEndPoint RemoteIpEndPoint = new IPEndPoint(ipDNS, 53);
                        //Blocks until a message returns on this socket from a remote host.
                        recBuf = udpClient.Receive(ref RemoteIpEndPoint);
                        udpClient.Close();
                    } catch (Exception e) {
                        //connection error, probably a wrong server address
                        udpClient.Close();
                        Error = 99;
                        ErrorTxt = e.Message + "(server:" + DNS + ")";
                        Done = true;
                        return;
                    }
                    //repeat until we get the reply with our threadID
                } while (System.Threading.Thread.CurrentThread.ManagedThreadId != ((recBuf[0] * 256) + recBuf[1]));
    
                //Check the DNS reply
                //check if bit QR (Query response) is set
                if (recBuf[2] < 128) {
                    //response byte not set (probably a malformed packet)
                    Error = 2;
                    ErrorTxt = "Query response bit not set";
                    Done = true;
                    return;
                }
                //check if RCODE field is 0
                if ((recBuf[3] & 15) > 0) {
                    //DNS server error, invalid reply
                    switch (recBuf[3] & 15) {
                        case 1:
                            Error = 31;
                            ErrorTxt = "Format error. The nameserver was unable to interpret the query";
                            break;
                        case 2:
                            Error = 32;
                            ErrorTxt = "Server failure. The nameserver was unable to process the query.";
                            break;
                        case 3:
                            Error = 33;
                            ErrorTxt = "Name error. Check provided domain name!!";
                            break;
                        case 4:
                            Error = 34;
                            ErrorTxt = "Not implemented. The name server does not support the requested query";
                            break;
                        case 5:
                            Error = 35;
                            ErrorTxt = "Refused. The name server refuses to reply for policy reasons";
                            break;
                        default:
                            Error = 36;
                            ErrorTxt = "Unknown. The name server error code was: " + Convert.ToString((recBuf[3] & 15));
                            break;
                    }
                    Done = true;
                    return;
                }
                //OK, now we should have valid header fields
                int QDcnt, ANcnt, NScnt, ARcnt;
                int index;
                QDcnt = (recBuf[4] * 256) + recBuf[5];
                ANcnt = (recBuf[6] * 256) + recBuf[7];
                NScnt = (recBuf[8] * 256) + recBuf[9];
                ARcnt = (recBuf[10] * 256) + recBuf[11];
                index = 12;
                //sometimes there are no erros but blank reply... ANcnt == 0...
                if (ANcnt == 0) { // if blackhole list query, means no spammer !!//if ((ANcnt == 0) & (IS_BLACKLIST_QUERY == false))
                    //error blank reply, return an empty array
                    Error = 4;
                    ErrorTxt = "Empty string array";
                    Done = true;
                    return;
                }
    
                //Decode received information
                string s1;
                // START TEST
                s1 = Encoding.ASCII.GetString(recBuf, 0, recBuf.Length);
                // END TEST
    
                if (QDcnt > 0) {
                    //we are not really interested to this string, just parse and skip
                    s1 = "";
                    index = parseString(recBuf, index, out s1);
                    index += 4; //skip root domain, Qtype and QClass values... unuseful in this contest
                }
                if (IS_BLACKLIST_QUERY) {
                    // get the answers, normally one !
                    // int the four last bytes there is the ip address
                    Error = 0;
                    int Last_Position = recBuf.Length - 1;
                    result.Add(recBuf[Last_Position - 3].ToString() + "." + recBuf[Last_Position - 2].ToString() + "." + recBuf[Last_Position - 1].ToString() + "." + recBuf[Last_Position].ToString());
                    Done = true;
                    return;
                }
                int count = 0;
                //get all answers
                while (count < ANcnt) {
                    s1 = "";
                    index = parseString(recBuf, index, out s1);
                    //Qtype
                    int QType = (recBuf[index] * 256) + recBuf[index + 1];
                    index += 2;
                    s1 += "," + QType.ToString();
                    //QClass
                    int QClass = (recBuf[index] * 256) + recBuf[index + 1];
                    index += 2;
                    s1 += "," + QClass.ToString();
                    //TTL (Time to live)
                    int TTL = (recBuf[index] * 16777216) + (recBuf[index + 1] * 65536) + (recBuf[index + 2] * 256) + recBuf[index + 3];
                    index += 4;
                    s1 += "," + TTL.ToString();
                    int blocklen = (recBuf[index] * 256) + recBuf[index + 1];
                    index += 2;
                    if (QType == 15) {
                        int MXprio = (recBuf[index] * 256) + recBuf[index + 1];
                        index += 2;
                        s1 += "," + MXprio.ToString();
                    }
                    string s2;
                    index = parseString(recBuf, index, out s2);
                    s1 += "," + s2;
                    result.Add(s1);
                    count++;
                }
                Error = 0;
                Done = true;
            }
            private int parseString(byte[] buf, int i, out string s)
            {
                int len;
                s = "";
                bool end = false;
                while (!end) {
                    if (buf[i] == 192) {
                        //next byte is a pointer to the string, get it..
                        i++;
                        s += getString(buf, buf[i]);
                        i++;
                        end = true;
                    } else {
                        //next byte is the string length
                        len = buf[i];
                        i++;
                        //get the string
                        s += Encoding.ASCII.GetString(buf, i, len);
                        i += len;
                        //check for the null terminator
                        if (buf[i] != 0) {
                            //not null, add a point to the name
                            s += ".";
                        } else {
                            //null char..the string is complete, exit
                            end = true;
                            i++;
                        }
                    }
                }
                return i;
            }
            private string getString(byte[] buf, int i)
            {
                string s = "";
                int len;
                bool end = false;
                while (!end) {
                    len = buf[i];
                    i++;
                    s += Encoding.ASCII.GetString(buf, i, len);
                    i += len;
                    if (buf[i] == 192) {
                        i++;
                        s += "." + getString(buf, buf[i]);
                        return s;
                    }
                    if (buf[i] != 0) {
                        s += ".";
                    } else {
                        end = true;
                    }
                }
                return s;
            }
        }
    }
    

    这是你的使用方法。

    /// <summary>
            /// Get the MX from the domain address.
            /// </summary>
            public static string getMXrecord(string domain)
            {
                domain = domain.Substring(domain.IndexOf('@') + 1);
                string LocalDNS = GetDnsAdress().ToString();
                Console.WriteLine("domain: " + domain);
    
                // resolv the authoritative domain (type=2)
                C_DNSquery DnsQry = new C_DNSquery(LocalDNS, domain, 2);
                Thread t1 = new Thread(new ThreadStart(DnsQry.doTheJob));
                t1.Start();
                int timeout = 20;
                while ((timeout > 0) & (!DnsQry.Done)) {
                    Thread.Sleep(100);
                    timeout--;
                }
                if (timeout == 0) {
                    if (DnsQry.udpClient != null) {
                        DnsQry.udpClient.Close();
                    }
                    t1.Abort();
                    DnsQry.Error = 100;
                }
    
                string[] ns1;
                string MyNs = "";
                if (DnsQry.Error == 0) {
                    ns1 = DnsQry.result[0].Split(',');
                    MyNs = ns1[4];
                    t1.Abort();
                } else {
                    t1.Abort();
                    MyNs = LocalDNS;
                }
    
                // Resolve MX (type = 15)
                DnsQry = new C_DNSquery(MyNs, domain, 15);
                Thread t2 = new Thread(new ThreadStart(DnsQry.doTheJob));
                t2.Start();
                timeout = 20;
                string TTL = "";
                string MXName = "";
                Int32 preference = 9910000;
                while ((timeout > 0) & (!DnsQry.Done)) {
                    Thread.Sleep(100);
                    timeout--;
                }
                if (timeout == 0) {
                    if (DnsQry.udpClient != null) {
                        DnsQry.udpClient.Close();
                    }
                    t2.Abort();
                    DnsQry.Error = 100;
                }
                if (DnsQry.Error == 0) {
    
                    if (DnsQry.result.Count == 1) {
                        string[] ns2 = DnsQry.result[0].Split(',');
                        MXName = ns2[5];
                        TTL = ns2[3];
                        preference = Int32.Parse(ns2[4]);
                        Console.WriteLine("domaine: {0} MX: {1} time: {2} pref: {3} ttl: {4}", domain.Substring(domain.IndexOf('@') + 1), MXName, 
                            DateTime.Now, preference, TTL);
    
    
                    } else {
                        for (int indns = 0; indns <= DnsQry.result.Count - 1; indns++) {
                            string[] ns2 = DnsQry.result[indns].Split(',');
                            if (Int32.Parse(ns2[4]) < preference) {
                                MXName = ns2[5];
                                TTL = ns2[3];
                                preference = Int32.Parse(ns2[4]);
    Console.WriteLine("domain: {0} MX: {1} time: {2} pref: {3} ttl: {4}", domain.Substring(domain.IndexOf('@') + 1), MXName, 
                                    DateTime.Now, preference, TTL);
    
                                }
                        }
                    }
                }
                return MXName;
            }
    
        6
  •  1
  •   Clemens Jung    7 年前

    我的方法是 nslookup.exe 检索MX记录。

    该解决方案并不像重写整个DNS或使用系统dll->那样花哨,但它可以工作,只需要少量的行。

    为了使事情正确,此代码仅起作用。它既不具有资源效率,也不快速,并且有很多改进空间(多个主机名、异步、更有用的返回值、添加优先级):

    static List<string> GetMxRecords(string host){
        ProcessStartInfo nslookup_config = new ProcessStartInfo("nslookup.exe");
        nslookup_config.RedirectStandardInput = true;
        nslookup_config.RedirectStandardOutput = true;
        nslookup_config.RedirectStandardError = true;
        nslookup_config.UseShellExecute = false;
        var nslookup  = Process.Start(nslookup_config);
        nslookup.StandardInput.WriteLine("set q=mx");
        nslookup.StandardInput.WriteLine(host);
        nslookup.StandardInput.WriteLine("exit");
        List<string> lines = new List<string>();
        while (!nslookup.StandardOutput.EndOfStream)
        {
            string l = nslookup.StandardOutput.ReadLine();
            if (l.Contains("internet address ="))
            {
                while (l.Contains("\t\t"))
                {
                    l = l.Replace("\t\t", "\t");
                }
                lines.Add(l.Replace("\tinternet address = ","="));
            }
        }
        nslookup.Close();
    }
    

    应该在国际上工作,因为 nslookup 不支持任何翻译(我在一台德国机器上工作,正在获取英文输出)。

    结果是这样的字符串:

    alt4.gmail-smtp-in.l.google.com=74.125.28.27
    alt2.gmail-smtp-in.l.google.com=74.125.200.27
    alt1.gmail-smtp-in.l.google.com=209.85.233.26
    gmail-smtp-in.l.google.com=66.102.1.27
    alt3.gmail-smtp-in.l.google.com=108.177.97.27
    
        7
  •  -1
  •   Shreyas Zare    7 年前

    你可以使用 this open source 库可以执行您通常需要的几乎任何类型的查询。

    用途:

    DnsClient dnsClient = new DnsClient();
    string mxDomain = dnsClient.ResolveMX("example.com");
    string mxDomainIP = dnsClient.ResolveMX("example.com", true);
    string mxDomainIPv6 = dnsClient.ResolveMX("example.com", true, true);