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

43,685

Solution 1

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

Solution 2

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.

Solution 3

I spent all day figuring out how to send/receive dns requests and came up with this. Its a complete generic handler. You just have to set the dns server and pass in 'd' eg. 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; } }
}

Solution 4

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

Solution 5

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();
        }            
    }
}
}
Share:
43,685
Segfault
Author by

Segfault

backend developer

Updated on July 09, 2022

Comments

  • Segfault
    Segfault almost 2 years

    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.