using System.Collections.Generic;
using System.Net.Sockets;
using Microsoft.VisualStudio.TestTools.UnitTesting;
private const int DNSPort = 53;
private static Dictionary<string, string> _records;
public ushort QuestionCount;
public ushort AnswerCount;
public ushort AuthorityCount;
public ushort AdditionalCount;
public struct DNSQuestion
public static string ConvertDomainNameFromDNSFormat(byte[] dnsFormat, int startIndex, out int bytesRead)
StringBuilder domainName = new StringBuilder();
while ((length = dnsFormat[index++]) != 0)
if (domainName.Length > 0)
domainName.Append(Encoding.ASCII.GetString(dnsFormat, index, length));
bytesRead = index - startIndex;
return domainName.ToString();
public static byte[] ConvertDomainNameToDNSFormat(string domainName)
List<byte> dnsFormat = new List<byte>();
foreach (string label in domainName.Split('.'))
dnsFormat.Add((byte)label.Length);
dnsFormat.AddRange(Encoding.ASCII.GetBytes(label));
return dnsFormat.ToArray();
public static byte[] ConvertIPv4AddressToBytes(string ipAddress)
return IPAddress.Parse(ipAddress).GetAddressBytes();
public static string ConvertBytesToIPv4Address(byte[] bytes) {
return new IPAddress(bytes).ToString();
public static byte[] CreateResponsePacket(DNSHeader requestHeader, DNSQuestion requestQuestion, string ipAddress)
DNSHeader responseHeader = new DNSHeader
DNSAnswer responseAnswer = new DNSAnswer
NAME = requestQuestion.QNAME,
byte[] headerBytes = new byte[12];
Buffer.BlockCopy(BitConverter.GetBytes(responseHeader.Id).Reverse().ToArray(), 0, headerBytes, 0, 2);
Buffer.BlockCopy(BitConverter.GetBytes(responseHeader.Flags).Reverse().ToArray(), 0, headerBytes, 2, 2);
Buffer.BlockCopy(BitConverter.GetBytes(responseHeader.QuestionCount).Reverse().ToArray(), 0, headerBytes, 4, 2);
Buffer.BlockCopy(BitConverter.GetBytes(responseHeader.AnswerCount).Reverse().ToArray(), 0, headerBytes, 6, 2);
Buffer.BlockCopy(BitConverter.GetBytes(responseHeader.AuthorityCount).Reverse().ToArray(), 0, headerBytes, 8, 2);
Buffer.BlockCopy(BitConverter.GetBytes(responseHeader.AdditionalCount).Reverse().ToArray(), 0, headerBytes, 10, 2);
byte[] questionBytes = ConvertDomainNameToDNSFormat(requestQuestion.QNAME);
questionBytes = questionBytes.Concat(BitConverter.GetBytes(requestQuestion.QTYPE).Reverse().ToArray()).ToArray();
questionBytes = questionBytes.Concat(BitConverter.GetBytes(requestQuestion.QCLASS).Reverse().ToArray()).ToArray();
byte[] answerBytes = ConvertDomainNameToDNSFormat(responseAnswer.NAME);
answerBytes = answerBytes.Concat(BitConverter.GetBytes(responseAnswer.TYPE).Reverse().ToArray()).ToArray();
answerBytes = answerBytes.Concat(BitConverter.GetBytes(responseAnswer.CLASS).Reverse().ToArray()).ToArray();
answerBytes = answerBytes.Concat(BitConverter.GetBytes(responseAnswer.TTL).Reverse().ToArray()).ToArray();
answerBytes = answerBytes.Concat(BitConverter.GetBytes(responseAnswer.RDLENGTH).Reverse().ToArray()).ToArray();
answerBytes = answerBytes.Concat(ConvertIPv4AddressToBytes(responseAnswer.RDATA)).ToArray();
return headerBytes.Concat(questionBytes).Concat(answerBytes).ToArray();
public static DNSHeader ParseDNSHeader(byte[] query) {
DNSHeader header = new DNSHeader
Id = (ushort)(query[0] << 8 | query[1]),
Flags = (ushort)(query[2] << 8 | query[3]),
QuestionCount = (ushort)(query[4] << 8 | query[5]),
AnswerCount = (ushort)(query[6] << 8 | query[7]),
AuthorityCount = (ushort)(query[8] << 8 | query[9]),
AdditionalCount = (ushort)(query[10] << 8 | query[11])
public static DNSQuestion ParseDNSQuestion(byte[] query, int offset, out int bytesRead)
string qname = ConvertDomainNameFromDNSFormat(query, offset, out bytesRead);
DNSQuestion question = new DNSQuestion
QTYPE = (ushort)(query[offset] << 8 | query[offset+1]),
QCLASS = (ushort)(query[offset + 2] << 8 | query[offset + 3])
public static void Start()
_records = new Dictionary<string, string>
{ "example.com", "192.168.1.1" },
{ "google.com", "172.217.160.142" },
{ "microsoft.com", "13.77.161.179" }
UdpClient udpServer = new UdpClient(DNSPort);
Console.WriteLine("DNS Server listening on port " + DNSPort);
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] query = udpServer.Receive(ref remoteEndPoint);
DNSHeader requestHeader = ParseDNSHeader(query);
DNSQuestion requestQuestion = ParseDNSQuestion(query, 12, out bytesRead);
Console.WriteLine($"Received query for: {requestQuestion.QNAME}");
if (_records.TryGetValue(requestQuestion.QNAME, out ipAddress))
byte[] responsePacket = CreateResponsePacket(requestHeader, requestQuestion, ipAddress);
udpServer.Send(responsePacket, responsePacket.Length, remoteEndPoint);
Console.WriteLine($"Sent response for {requestQuestion.QNAME}: {ipAddress}");
Console.WriteLine($"Unknown domain: {requestQuestion.QNAME}");
public static void Main()
Console.WriteLine("š Discovering and Running Tests...\n");
int totalPassed = 0, totalFailed = 0;
var testClasses = Assembly.GetExecutingAssembly()
.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)
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 ConvertDomainNameFromDNSFormat_ValidInput_ReturnsCorrectDomainName()
byte[] dnsFormat = { 3, 103, 111, 111, 7, 101, 120, 97, 109, 112, 108, 101, 3, 99, 111, 109, 0 };
string domainName = DNSServer.ConvertDomainNameFromDNSFormat(dnsFormat, startIndex, out bytesRead);
Assert.AreEqual("google.com", domainName);
Assert.AreEqual(17, bytesRead);
public void ConvertDomainNameFromDNSFormat_EmptyDomain_ReturnsEmptyString()
byte[] dnsFormat = { 0 };
string domainName = DNSServer.ConvertDomainNameFromDNSFormat(dnsFormat, startIndex, out bytesRead);
Assert.AreEqual("", domainName);
Assert.AreEqual(1, bytesRead);
public void ConvertDomainNameToDNSFormat_ValidDomain_ReturnsCorrectByteArray()
string domainName = "example.com";
byte[] dnsFormat = DNSServer.ConvertDomainNameToDNSFormat(domainName);
byte[] expected = { 7, 101, 120, 97, 109, 112, 108, 101, 3, 99, 111, 109, 0 };
CollectionAssert.AreEqual(expected, dnsFormat);
public void ConvertDomainNameToDNSFormat_EmptyDomain_ReturnsCorrectByteArray()
byte[] dnsFormat = DNSServer.ConvertDomainNameToDNSFormat(domainName);
CollectionAssert.AreEqual(expected, dnsFormat);
public void ConvertIPv4AddressToBytes_ValidIPAddress_ReturnsCorrectByteArray()
string ipAddress = "192.168.1.1";
byte[] ipBytes = DNSServer.ConvertIPv4AddressToBytes(ipAddress);
byte[] expected = { 192, 168, 1, 1 };
CollectionAssert.AreEqual(expected, ipBytes);
public void ConvertBytesToIPv4Address_ValidBytes_ReturnsCorrectIpAddress() {
byte[] bytes = { 192, 168, 1, 1 };
string ipAddress = DNSServer.ConvertBytesToIPv4Address(bytes);
Assert.AreEqual("192.168.1.1", ipAddress);
public void CreateResponsePacket_ValidInput_ReturnsCorrectlyFormattedPacket()
DNSHeader requestHeader = new DNSHeader { Id = 12345 };
DNSQuestion requestQuestion = new DNSQuestion { QNAME = "example.com", QTYPE = 1, QCLASS = 1 };
string ipAddress = "192.168.1.1";
byte[] responsePacket = DNSServer.CreateResponsePacket(requestHeader, requestQuestion, ipAddress);
Assert.AreEqual(12345, (ushort)(responsePacket[0] << 8 | responsePacket[1]));
Assert.AreEqual(0x8180, (ushort)(responsePacket[2] << 8 | responsePacket[3]));
Assert.AreEqual(1, (ushort)(responsePacket[4] << 8 | responsePacket[5]));
Assert.AreEqual(1, (ushort)(responsePacket[6] << 8 | responsePacket[7]));
Assert.AreEqual(192, responsePacket[responsePacket.Length - 4]);
Assert.AreEqual(168, responsePacket[responsePacket.Length - 3]);
Assert.AreEqual(1, responsePacket[responsePacket.Length - 2]);
Assert.AreEqual(1, responsePacket[responsePacket.Length - 1]);
public void ParseDNSHeader_ValidQuery_ParsesHeaderCorrectly()
byte[] query = new byte[12] {0x30, 0x39, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
DNSHeader header = DNSServer.ParseDNSHeader(query);
Assert.AreEqual(12345, header.Id);
Assert.AreEqual(256, header.Flags);
Assert.AreEqual(1, header.QuestionCount);
Assert.AreEqual(0, header.AnswerCount);
Assert.AreEqual(0, header.AuthorityCount);
Assert.AreEqual(0, header.AdditionalCount);
public void ParseDNSQuestion_ValidQuery_ParsesQuestionCorrectly() {
0x30, 0x39, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
DNSQuestion question = DNSServer.ParseDNSQuestion(query, offset, out bytesRead);
Assert.AreEqual("example.com", question.QNAME);
Assert.AreEqual(1, question.QTYPE);
Assert.AreEqual(1, question.QCLASS);
Assert.AreEqual(17, bytesRead);