web-dev-qa-db-fra.com

Comment obtenir des enregistrements mx pour un nom de DNS avec System.Net.DNS?

Existe-t-il une méthode intégrée dans la bibliothèque .NET qui renverra tous les enregistrements MX d'un domaine donné? Je vois comment vous obtenez les CNAMES, mais pas les enregistrements MX.

40
Segfault

Je viens de lancer ma propre bibliothèque car il n'y avait rien pour le support .net core/xplat ... https://github.com/MichaCo/DnsClient.NET

Cela fonctionne très bien et vous donne Dig comme les messages de journal si vous le souhaitez.

Simple à utiliser 

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

et fonctionne avec des serveurs personnalisés fonctionnant sur tous les ports, plusieurs serveurs, etc.

voir aussi Site Web DnsClient pour plus de détails

11
MichaC

Mise à jour 2018/5/23:

Découvrez la réponse de MichaC pour une bibliothèque plus récente prenant en charge la norme .NET.

Réponse originale:

La bibliothèque ARSoft.Tools.Net de Alexander Reinert semble très bien faire son travail.

Il est disponible à partir de NuGet:

PM> Install-Package ARSoft.Tools.Net

Importer l'espace de nom:

using ARSoft.Tools.Net.Dns;

Ensuite, effectuer une recherche synchrone est aussi simple que:

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

Ce qui nous donne la sortie:

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.

Sous le capot, il semble que la bibliothèque construise les paquets UDP (ou TCP) nécessaires pour être envoyés au résolveur, comme vous pouvez vous y attendre. La bibliothèque a même une logique (appelée avec DnsClient.Default) pour savoir quel serveur DNS interroger.

Une documentation complète peut être trouvée ici .

40
Michael Kropat

J'ai passé toute la journée à trouver comment envoyer/recevoir des demandes DNS et je l’ai proposé. C'est un gestionnaire générique complet. Il vous suffit de définir le serveur DNS et de transmettre «d», par exemple. 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; } }
}
9
Christian

La réponse acceptée ne fonctionne pas pour .NET Framework <4.5; par conséquent, il est suggéré que ceux d'entre vous qui ne peuvent pas utiliser ARSOFT.Tools puissent utiliser les DNDN de https://dndns.codeplex.com

Ci-dessous, une application console qui renvoie l’enregistrement MX d’un domaine donné en modifiant ses exemples.

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

Mon approche consistait à utiliser nslookup.exe pour retrouver l'enregistrement MX.

La solution n’est pas aussi sophistiquée que de réécrire tout le DNS ou d’utiliser un système DLL ->, mais cela fonctionne, avec un petit nombre de lignes.

Pour que tout fonctionne bien, ce code> fonctionne seulement <ce n'est pas une ressource efficace ni rapide et il y a beaucoup de place pour l'amélioration (noms d'hôtes multiples, async, valeur de retour plus utile, ajout de la priorité):

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

Devrait travailler à l'international, étant donné que nslookup ne prend en charge aucune traduction (je travaille sur une machine allemande et j'obtiens une sortie en anglais).

Le résultat est des chaînes comme ceci:

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
1
Clemens Jung

Voici une classe que j'utilise pour rechercher des enregistrements MX uniquement.

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

Voici comment vous l'utilisez.

/// <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;
        }
1
Joseph Philbert