c# .net dns mx-record

c# - ¿Cómo obtener registros mx para un nombre de DNS con System.Net.DNS?



mx-record (7)

¿Hay algún método incorporado en la biblioteca .NET que devolverá todos los registros MX para un dominio dado? Veo cómo recibes CNAMES, pero no registros MX.


Acabo de rolar mi propia biblioteca porque no había nada para .net core / xplat support ... https://github.com/MichaCo/DnsClient.NET

Funciona muy bien y te permite dig como mensajes de registro si quieres.

Fácil de usar

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

y funciona con servidores personalizados que se ejecutan en cualquier puerto, servidor múltiple, etc.

ver también el sitio web de DnsClient para más detalles


Aquí hay una Clase I que uso para buscar registros MX solamente.

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

Aquí es cómo lo usa.

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


La respuesta aceptada no funciona para .NET framework <4.5, por lo que sugeriría que aquellos de ustedes que no pueden usar ARSOFT.Tools puedan usar DNDN desde https://dndns.codeplex.com

A continuación se presenta una aplicación de consola que devuelve el registro MX de un dominio determinado y modifica sus ejemplos.

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


Mi enfoque era usar nslookup.exe para recuperar el registro MX.

La solución no es tan elegante como reescribir DNS completos o usar una DLL del sistema -> pero funciona, con una pequeña cantidad de líneas.

Para hacer las cosas bien, este código> simplemente funciona <no es eficiente en recursos ni rápido y tiene mucho espacio para mejorar (nombres de host múltiples, asincrónico, valor de retorno más útil, agregando la prioridad):

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

Debería estar funcionando internacionalmente, ya que nslookup no admite ninguna traducción (estoy trabajando en una máquina alemana y obtengo resultados en inglés).

El resultado son cadenas como esta:

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


Pasé todo el día averiguando cómo enviar / recibir solicitudes de DNS y se me ocurrió esto. Es un controlador genérico completo. Solo tiene que configurar el servidor DNS y pasar ''d'', por ej. 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; } } }


Puede usar esta biblioteca de código abierto para hacer casi cualquier tipo de consulta que generalmente necesite.

Uso:

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


Actualización 2018/5/23 :

Vea la respuesta de MichaC para una biblioteca más nueva que tenga soporte estándar .NET.

Respuesta Original:

La biblioteca ARSoft.Tools.Net de Alexander Reinert parece hacer bastante bien el trabajo.

Está disponible desde NuGet:

PM> Install-Package ARSoft.Tools.Net

Importar el espacio de nombres:

using ARSoft.Tools.Net.Dns;

Luego hacer una búsqueda sincrónica es tan simple como:

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

Lo que nos da la salida:

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.

Debajo del capó, parece que la biblioteca construye los paquetes UDP (o TCP) necesarios para enviar al resolver, como cabría esperar. La biblioteca incluso tiene lógica (invocada con DnsClient.Default ) para descubrir qué servidor DNS consultar.

La documentación completa se puede encontrar here .