Tiny DNS server

I try to implement a simple / tiny ‘DNS server’ The idea is to connect a NETMF board 1:1 to a PC using for example the ENC28 ethernet module. The netmf board runs a http webserver and the dns server code below. Instead of using an IP address to access the web application i like it to be accessible by a name. Running Wireshark i can see a proper DNS request when i enter the URL in the browser. I also see a well formed DNS response (at least what i programmed to return), but the browser does not continue to go to the resolved IP address.

I guess i’m missing something… Hope someone has some tips.

Here’s the DNS class:


using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace SenticsLib
{
    public class DNSServer
    {
        private readonly string _domainName;

        public DNSServer(string domainName)
        {
            _domainName = domainName;
            var dnsServerThread = new Thread(DNSServerThread);
            dnsServerThread.Start();
        }

        private void DNSServerThread()
        {
            DebugHelper.DebugPrint("DNS-Server started");
            while (true)
            {
                try
                {
                    var serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 53);
                    serverSocket.Bind(remoteEndPoint);

                    try
                    {
                        while (true)
                        {
                            if (serverSocket.Poll(-1, SelectMode.SelectRead))
                            {
                                var inBuf = new byte[serverSocket.Available];
                                serverSocket.ReceiveFrom(inBuf, ref remoteEndPoint);
                                DnsResponse dnsResponse = ProcessRequest(inBuf);
                                if (dnsResponse != null)
                                {
                                    byte[] dnsResp = dnsResponse.GetResponsePacket();
                                    serverSocket.SendTo(dnsResp, new IPEndPoint(IPAddress.Parse("192.168.2.11"), 53));
                                }
                            }
                        }
                    }
                    catch (SocketException socketException)
                    {
                        // TODO : ....
                        DebugHelper.DebugPrint("Socket exception:" + socketException.ErrorCode);
                        Thread.Sleep(1000);
                    }
                }

                catch (SocketException socketException)
                {
                    // TODO : ....
                    DebugHelper.DebugPrint("Socket exception:" + socketException.ErrorCode);
                    Thread.Sleep(1000);
                }
            }
        }

        private DnsResponse ProcessRequest(byte[] dataPacket)
        {
            var dnsRequest = new DnsRequest(dataPacket);
            if (dnsRequest.IsQuery)
            {
                if (dnsRequest.NameQuery == _domainName)
                {
                    DebugHelper.DebugPrint("WAHU domain request");
                    DebugHelper.DebugPrint("Queries:" + dnsRequest.NameQuery);
                    DebugHelper.DebugPrint("Id:" + dnsRequest.Id);
                    DebugHelper.DebugPrint("Flags:" + dnsRequest.Flags);
                    DebugHelper.DebugPrint("Questions:" + dnsRequest.QdCount);
                    DebugHelper.DebugPrint("AnCount:" + dnsRequest.AnCount);
                    DebugHelper.DebugPrint("NsCount:" + dnsRequest.NsCount);
                    DebugHelper.DebugPrint("ArCount:" + dnsRequest.ArCount);
                    DebugHelper.DebugPrint("Type:" + dnsRequest.ReqType);
                    DebugHelper.DebugPrint("Class:" + dnsRequest.ReqClass);

                    return new DnsResponse(dnsRequest.Id, dnsRequest.NameQuery, IPAddress.GetDefaultLocalAddress());
                }
            }
            else
            {
                DebugHelper.DebugPrint("NQ. flags:" + dnsRequest.Flags + " type:" + dnsRequest.ReqType);
            }
            return null;
        }

        private class DnsRequest
        {
            public DnsRequest(byte[] packet)
            {
                var memoryStream = new MemoryStream(packet);
                Id = StreamGetUInt16(memoryStream);
                Flags = StreamGetUInt16(memoryStream);
                QdCount = StreamGetUInt16(memoryStream);
                AnCount = StreamGetUInt16(memoryStream);
                NsCount = StreamGetUInt16(memoryStream);
                ArCount = StreamGetUInt16(memoryStream);

                var name = new StringBuilder(32);
                bool stop = false;
                do
                {
                    int length = memoryStream.ReadByte();
                    if (length == 0)
                    {
                        stop = true;
                    }
                    else
                    {
                        if (name.Length != 0)
                            name.Append('.');
                        var q = new byte[length];
                        memoryStream.Read(q, 0, length);
                        name.Append(new string(Encoding.UTF8.GetChars(q)));
                    }
                } while (stop == false);

                NameQuery = name.ToString();
                ReqType = StreamGetUInt16(memoryStream);
                ReqClass = StreamGetUInt16(memoryStream);
                memoryStream.Dispose();

                IsQuery = (Flags & 0x8000) == 0;
            }

            public UInt16 Id { get; private set; }
            public UInt16 Flags { get; private set; }
            public UInt16 QdCount { get; private set; }
            public UInt16 AnCount { get; private set; }
            public UInt16 NsCount { get; private set; }
            public UInt16 ArCount { get; private set; }
            public string NameQuery { get; private set; }
            public UInt16 ReqType { get; private set; }
            public UInt16 ReqClass { get; private set; }

            public bool IsQuery { get; private set; }

            private UInt16 StreamGetUInt16(MemoryStream stream)
            {
                var retValue = (UInt16)(stream.ReadByte() << 8);   // MSB
                retValue += (UInt16)(stream.ReadByte());           // LSB
                return retValue;
            }
        }

        private class DnsResponse
        {
            private readonly UInt16 _id;
            private readonly byte[] _ipBytes = new byte[4];
            private readonly string _domainName;

            public DnsResponse(UInt16 id, string domainName, IPAddress result)
            {
                _id = id;
                _domainName = domainName;
                _ipBytes = result.GetAddressBytes();
            }

            public byte[] GetResponsePacket()
            {
                byte[] name = Encoding.UTF8.GetBytes(_domainName);
                var buffer = new byte[32 + name.Length + 2];

                var memoryStream = new MemoryStream(buffer);

                StreamWriteUInt16(memoryStream, _id);
                StreamWriteUInt16(memoryStream, 0x8180);    // Flags = response
                StreamWriteUInt16(memoryStream, 1);         // QDcount
                StreamWriteUInt16(memoryStream, 1);         // ANcount
                StreamWriteUInt16(memoryStream, 0);         // NScount
                StreamWriteUInt16(memoryStream, 0);         // CRcount
                // Question data
                memoryStream.WriteByte((byte)name.Length);
                memoryStream.Write(name, 0, name.Length);
                memoryStream.WriteByte(0);
                StreamWriteUInt16(memoryStream, 1);         // type
                StreamWriteUInt16(memoryStream, 1);         // class

                // Answer Data
                StreamWriteUInt16(memoryStream, 0xC00C);    // Name is a pointer and Pointer is to the name at offset 0x000C
                StreamWriteUInt16(memoryStream, 1);         // Answer is a Type A
                StreamWriteUInt16(memoryStream, 1);         // Answer is class IN
                StreamWriteUInt32(memoryStream, 600);       // answer is valid for 600 seconds
                StreamWriteUInt16(memoryStream, 4);         // IP-Address is 4 byes long
                memoryStream.Write(_ipBytes, 0, 4);           // IP-Address
                memoryStream.Dispose();
                return buffer;
            }

            private void StreamWriteUInt16(MemoryStream stream, ushort data)
            {
                stream.WriteByte((byte)(data >> 8));    // MSB
                stream.WriteByte((byte)data);          // LSB
            }

            private void StreamWriteUInt32(MemoryStream stream, UInt32 data)
            {
                stream.WriteByte((byte)(data >> 24));   // HW.MSB
                stream.WriteByte((byte)(data >> 16));   // HW.LSB
                stream.WriteByte((byte)(data >> 8));    // LW.MSB
                stream.WriteByte((byte)data);           // LW.LSB
            }

        }
    }
}

@ andre.m - No, because the final goal is to use the device with the RS9110 WiFI module in adhoc mode and have access to the web application from an iPad and/or android device.

@ andre.m - Doe you say that netbios is “default” available on iPad (iOS) ?

I globally read the RFC’s

No reply on ping, although see a dns reply in wireshark.

@ andre.m - Thanks… continue digging :slight_smile: i have a simple DHCP server working… Once DNS works i will put both on codeshare

1 Like

you might want to review http://mip.codeplex.com code for educational purposes :wink:

1 Like