using System.Collections.Generic;
using System.Net.Sockets;
using Microsoft.VisualStudio.TestTools.UnitTesting;
public ushort QuestionCount;
public ushort AnswerCount;
public ushort AuthorityCount;
public ushort AdditionalCount;
public struct DNSQuestion
private static readonly Dictionary<string, string> dnsRecords = new Dictionary<string, string>
public static void StartServer()
UdpClient udpServer = new UdpClient(53);
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
Console.WriteLine("DNS Server started. Listening on port 53...");
byte[] receivedBytes = udpServer.Receive(ref remoteEP);
Console.WriteLine("Received query from " + remoteEP.Address.ToString());
DNSHeader header = ParseHeader(receivedBytes);
DNSQuestion question = ParseQuestion(receivedBytes, 12);
if (question.QTYPE == 1 && question.QCLASS == 1)
if (dnsRecords.ContainsKey(question.QNAME))
byte[] response = ConstructResponse(header, question);
udpServer.Send(response, response.Length, remoteEP);
Console.WriteLine("Responded with " + dnsRecords[question.QNAME]);
Console.WriteLine("Domain not found: " + question.QNAME);
Console.WriteLine("Unsupported query type or class.");
Console.WriteLine("Exception: " + e.Message);
private static DNSHeader ParseHeader(byte[] data)
DNSHeader header = new DNSHeader();
header.Id = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 0));
header.Flags = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 2));
header.QuestionCount = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 4));
header.AnswerCount = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 6));
header.AuthorityCount = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 8));
header.AdditionalCount = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 10));
private static DNSQuestion ParseQuestion(byte[] data, int offset)
DNSQuestion question = new DNSQuestion();
question.QNAME = ConvertDnsNameToString(data, offset, out int endOfName);
question.QTYPE = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, endOfName));
question.QCLASS = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, endOfName + 2));
private static byte[] ConstructResponse(DNSHeader header, DNSQuestion question)
byte[] headerBytes = new byte[12];
Array.Copy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)header.Id)), 0, headerBytes, 0, 2);
Array.Copy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)header.Flags)), 0, headerBytes, 2, 2);
Array.Copy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)header.QuestionCount)), 0, headerBytes, 4, 2);
Array.Copy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)header.AnswerCount)), 0, headerBytes, 6, 2);
Array.Copy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)header.AuthorityCount)), 0, headerBytes, 8, 2);
Array.Copy(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)header.AdditionalCount)), 0, headerBytes, 10, 2);
DNSAnswer answer = new DNSAnswer();
answer.NAME = question.QNAME;
answer.RDATA = dnsRecords[question.QNAME];
List<byte> response = new List<byte>();
response.AddRange(headerBytes);
response.AddRange(ConvertStringToDnsName(question.QNAME));
response.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)question.QTYPE)));
response.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)question.QCLASS)));
response.AddRange(ConvertStringToDnsName(answer.NAME));
response.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)answer.TYPE)));
response.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)answer.CLASS)));
response.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((int)answer.TTL)));
response.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)answer.RDLENGTH)));
response.AddRange(IPAddress.Parse(answer.RDATA).GetAddressBytes());
return response.ToArray();
public static byte[] ConvertStringToDnsName(string domain)
List<byte> dnsName = new List<byte>();
string[] labels = domain.Split('.');
foreach (string label in labels)
dnsName.Add((byte)label.Length);
dnsName.AddRange(Encoding.ASCII.GetBytes(label));
return dnsName.ToArray();
public static string ConvertDnsNameToString(byte[] data, int offset, out int endOfName)
StringBuilder domainName = new StringBuilder();
int labelLength = data[current];
for (int i = 0; i < labelLength; i++)
domainName.Append((char)data[current + i + 1]);
current += labelLength + 1;
labelLength = data[current];
return domainName.ToString();
public static string ConvertIPAddressToString(byte[] ipBytes)
return new IPAddress(ipBytes).ToString();
public static byte[] ConvertStringToIPAddress(string ipString)
return IPAddress.Parse(ipString).GetAddressBytes();
public static void Main()
Console.WriteLine("š Discovering and Running Tests...\n");
int totalPassed = 0, totalFailed = 0;
var testClasses = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.GetCustomAttribute<TestClassAttribute>() != null);
foreach (var testClass in testClasses)
Console.WriteLine($"š Running tests in: {testClass.Name}");
(int passed, int failed) = RunTests(testClass);
Console.WriteLine("\nš FINAL SUMMARY:");
Console.WriteLine($"ā
Total Passed: {totalPassed}");
Console.WriteLine($"ā Total Failed: {totalFailed}");
Console.WriteLine($"š Total Tests: {totalPassed + totalFailed}");
Console.WriteLine("\nā
Test execution completed.");
private static (int passed, int failed) RunTests(Type testClassType)
object testInstance = Activator.CreateInstance(testClassType);
MethodInfo[] testMethods = testClassType.GetMethods(BindingFlags.Instance | BindingFlags.Public).Where(m => m.GetCustomAttribute<TestMethodAttribute>() != null).ToArray();
int passed = 0, failed = 0;
foreach (var method in testMethods)
method.Invoke(testInstance, null);
Console.WriteLine($" ā
{method.Name} PASSED");
catch (TargetInvocationException ex)
Console.WriteLine($" ā {method.Name} FAILED: {ex.InnerException?.Message ?? ex.Message}");
Console.WriteLine($" ā ļø {method.Name} ERROR: {ex.Message}");
public class DNSServerTests
public void ConvertStringToDnsName_ValidDomain_ReturnsDnsNameBytes()
string domain = "test.example.com";
byte[] expected = new byte[]
byte[] actual = DNSServer.ConvertStringToDnsName(domain);
CollectionAssert.AreEqual(expected, actual);
public void ConvertStringToDnsName_RootDomain_ReturnsDnsNameBytes()
byte[] expected = new byte[]
byte[] actual = DNSServer.ConvertStringToDnsName(domain);
CollectionAssert.AreEqual(expected, actual);
public void ConvertDnsNameToString_ValidDnsName_ReturnsDomainString()
byte[] dnsName = new byte[]
string expected = "test.example.com";
string actual = DNSServer.ConvertDnsNameToString(dnsName, 0, out int endOfName);
Assert.AreEqual(expected, actual);
Assert.AreEqual(dnsName.Length, endOfName);
public void ConvertDnsNameToString_RootDomainName_ReturnsDomainString()
byte[] dnsName = new byte[]
string actual = DNSServer.ConvertDnsNameToString(dnsName, 0, out int endOfName);
Assert.AreEqual(expected, actual);
Assert.AreEqual(dnsName.Length, endOfName);
public void ConvertIPAddressToString_ValidIPAddressBytes_ReturnsIPAddressString()
byte[] ipBytes = new byte[]
string expected = "127.0.0.1";
string actual = DNSServer.ConvertIPAddressToString(ipBytes);
Assert.AreEqual(expected, actual);
public void ConvertStringToIPAddress_ValidIPAddressString_ReturnsIPAddressBytes()
string ipString = "192.168.1.1";
byte[] expected = new byte[]
byte[] actual = DNSServer.ConvertStringToIPAddress(ipString);
CollectionAssert.AreEqual(expected, actual);