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