How to get mx records for a dns name with System.Net.DNS?

前端 未结 7 1271
难免孤独
难免孤独 2020-11-29 00:34

Is there any built in method in the .NET library that will return all of the MX records for a given domain? I see how you get CNAMES, but not MX records.

相关标签:
7条回答
  • 2020-11-29 01:09

    My approach was to use nslookup.exe to retreive the MX record.

    The solution is not as fancy as rewriting whole DNS or using a System DLL -> but it works, with a little amount of lines.

    To get things right, this code >just works< it's not ressource efficient nor fast and has a lots of room for improvment (multiple hostnames, async, more usefull return value,adding the priority):

        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();
            return lines;
        }
    

    Should be working international, since nslookup does not support any translation (I'm working on a German machine and I'm getting english output).

    The result are strings like this:

    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
    
    0 讨论(0)
  • 2020-11-29 01:09

    Here is a Class I use to look up MX records only.

        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)
                    uint TTL = (recBuf[index] * 16777216u) + (recBuf[index + 1] * 65536u) + (recBuf[index + 2] * 256u) + 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;
            }
        }
    }
    

    Here is how you use it.

    /// <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;
            }
            
    
    0 讨论(0)
  • 2020-11-29 01:10

    Just roled my own library because there was nothing for .net core / xplat support... https://github.com/MichaCo/DnsClient.NET

    It works pretty great and gives you dig like log messages if you want.

    Simple to use

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

    and works with custom servers running on any ports, multiple servers, etc...

    see also DnsClient Website for more details

    0 讨论(0)
  • 2020-11-29 01:15

    The accepted answer doesn't work for .NET framework < 4.5, so would suggest that those of you who can't use ARSOFT.Tools can use DNDNs from https://dndns.codeplex.com

    Given below is a console application that returns the MX record for a given domain modifying their examples.

    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();
            }            
        }
    }
    }
    
    0 讨论(0)
  • 2020-11-29 01:17

    You can use this open source library to do almost any kind of query you would usually need.

    Usage:

    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);
    
    0 讨论(0)
  • 2020-11-29 01:20

    Update 2018/5/23:

    Check out MichaC's answer for a newer library that has .NET standard support.

    Original Answer:

    The ARSoft.Tools.Net library by Alexander Reinert seems to do the job pretty well.

    It's available from NuGet:

    PM> Install-Package ARSoft.Tools.Net
    

    Import the namespace:

    using ARSoft.Tools.Net.Dns;
    

    Then making a synchronous lookup is as simple as:

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

    Which gives us the output:

    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.
    

    Underneath the hood, it looks like the library constructs the UDP (or TCP) packets necessary to send to the resolver, like you might expect. The library even has logic (invoked with DnsClient.Default) to discover which DNS server to query.

    Full documentation can be found here.

    0 讨论(0)
提交回复
热议问题