diff --git a/EonaCat.DnsTester/Helpers/DnsHelper.cs b/EonaCat.DnsTester/Helpers/DnsHelper.cs index d70a384..6160b19 100644 --- a/EonaCat.DnsTester/Helpers/DnsHelper.cs +++ b/EonaCat.DnsTester/Helpers/DnsHelper.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Net; using System.Net.Sockets; using System.Text; @@ -11,12 +10,10 @@ namespace EonaCat.DnsTester.Helpers class DnsHelper { public static event EventHandler OnLog; - - private static readonly Random random = new Random(); - - public static async Task SendDnsQueryPacket(string dnsId, string server, int port, byte[] queryBytes) + public static async Task SendDnsQueryPacketAsync(string dnsId, string server, int port, byte[] queryBytes) { - var stopwatch = System.Diagnostics.Stopwatch.StartNew(); + // Start the clock + var startTime = DateTime.Now.Ticks; var endPoint = new IPEndPoint(IPAddress.Parse(server), port); using (var client = new UdpClient(endPoint.AddressFamily)) @@ -25,68 +22,106 @@ namespace EonaCat.DnsTester.Helpers client.EnableBroadcast = false; client.Client.SendTimeout = DnsSendTimeout; client.Client.ReceiveTimeout = DnsReceiveTimeout; - - byte[] responseBytes; + byte[] responseBytes = null; if (FakeResponse) { - responseBytes = GetExampleResponse(); + responseBytes = DnsHelper.GetExampleResponse(); } else { - await client.SendAsync(queryBytes, queryBytes.Length, endPoint); - var responseResult = await client.ReceiveAsync(); + await client.SendAsync(queryBytes, queryBytes.Length, endPoint).ConfigureAwait(false); + var responseResult = await client.ReceiveAsync().ConfigureAwait(false); responseBytes = responseResult.Buffer; } - var response = ParseDnsResponsePacket(dnsId, stopwatch.ElapsedTicks, server, responseBytes); + var response = ParseDnsResponsePacket(dnsId, startTime, server, responseBytes); return response; } } // For testing purposes public static bool FakeResponse { get; set; } + public static int DnsSendTimeout { get; set; } = 5; public static int DnsReceiveTimeout { get; set; } = 5; + public static byte[] CreateDnsQueryPacket(string domainName, DnsRecordType recordType) { + var random = new Random(); + + // DNS header var id = (ushort)random.Next(0, 65536); var flags = (ushort)0x0100; // recursion desired - var qdcount = (ushort)1; - var ancount = (ushort)0; - var nscount = (ushort)0; - var arcount = (ushort)0; - - using (var stream = new MemoryStream()) - using (var writer = new BinaryWriter(stream)) + ushort qdcount = 1; + ushort ancount = 0; + ushort nscount = 0; + ushort arcount = 0; + var headerBytes = new byte[] { - writer.Write(id); - writer.Write(flags); - writer.Write(qdcount); - writer.Write(ancount); - writer.Write(nscount); - writer.Write(arcount); + (byte)(id >> 8), (byte)(id & 0xff), + (byte)(flags >> 8), (byte)(flags & 0xff), + (byte)(qdcount >> 8), (byte)(qdcount & 0xff), + (byte)(ancount >> 8), (byte)(ancount & 0xff), + (byte)(nscount >> 8), (byte)(nscount & 0xff), + (byte)(arcount >> 8), (byte)(arcount & 0xff), + }; - var labels = domainName.Split('.'); - foreach (var label in labels) + // DNS question + var labels = domainName.Split('.'); + var qnameBytes = new byte[domainName.Length + 2]; + var qnameIndex = 0; + foreach (var label in labels) + { + qnameBytes[qnameIndex++] = (byte)label.Length; + foreach (var c in label) { - writer.Write((byte)label.Length); - writer.Write(Encoding.ASCII.GetBytes(label)); + qnameBytes[qnameIndex++] = (byte)c; } - - writer.Write((byte)0); // Null terminator - writer.Write((ushort)recordType); - writer.Write((ushort)1); // Record class: IN (Internet) - - return stream.ToArray(); } + qnameBytes[qnameIndex++] = 0; + + var qtypeBytes = new byte[] { (byte)((ushort)recordType >> 8), (byte)((ushort)recordType & 0xff) }; + var qclassBytes = new byte[] { 0, 1 }; // internet class + var questionBytes = new byte[qnameBytes.Length + qtypeBytes.Length + qclassBytes.Length]; + qnameBytes.CopyTo(questionBytes, 0); + qtypeBytes.CopyTo(questionBytes, qnameBytes.Length); + qclassBytes.CopyTo(questionBytes, qnameBytes.Length + qtypeBytes.Length); + + // Combine the header and question to form the DNS query packet + var queryBytes = new byte[headerBytes.Length + questionBytes.Length]; + headerBytes.CopyTo(queryBytes, 0); + questionBytes.CopyTo(queryBytes, headerBytes.Length); + + return queryBytes; + } + + static DnsQuestion ParseDnsQuestionRecord(byte[] queryBytes, ref int offset) + { + // Parse the DNS name + var name = DnsNameParser.ParseName(queryBytes, ref offset); + if (name == null) + { + return null; + } + + // Parse the DNS type and class + var type = (ushort)((queryBytes[offset] << 8) | queryBytes[offset + 1]); + var qclass = (ushort)((queryBytes[offset + 2] << 8) | queryBytes[offset + 3]); + offset += 4; + + return new DnsQuestion + { + Name = name, + Type = (DnsRecordType)type, + Class = (DnsRecordClass)qclass, + }; } public static byte[] GetExampleResponse() { // Example response bytes for the A record of google.com - return new byte[] - { + byte[] response = { 0x9d, 0xa9, // Query ID 0x81, 0x80, // Flags 0x00, 0x01, // Questions: 1 @@ -105,31 +140,43 @@ namespace EonaCat.DnsTester.Helpers 0x00, 0x04, // Data length: 4 bytes 0xac, 0xd9, 0x03, 0x3d // Data: 172.217.3.61 }; + return response; } - private static DnsResponse ParseDnsResponsePacket(string dnsId, long startTime, string server, byte[] responseBytes) + static DnsResponse ParseDnsResponsePacket(string dnsId, long startTime, string server, byte[] responseBytes) { + Console.WriteLine("new byte[] { " + responseBytes + " }"); + + // Check if response is valid if (responseBytes.Length < 12) { throw new Exception("Invalid DNS response"); } + // Set the offset to the start var offset = 0; - var id = (ushort)((responseBytes[offset++] << 8) | responseBytes[offset++]); - var flags = (ushort)((responseBytes[offset++] << 8) | responseBytes[offset++]); + + // Parse the DNS header + var id = (ushort)((responseBytes[0] << 8) | responseBytes[1]); + var flags = (ushort)((responseBytes[2] << 8) | responseBytes[3]); var isResponse = (flags & 0x8000) != 0; - var qdcount = (ushort)((responseBytes[offset++] << 8) | responseBytes[offset++]); + var qdcount = (ushort)((responseBytes[4] << 8) | responseBytes[5]); + var ancount = (ushort)((responseBytes[6] << 8) | responseBytes[7]); if (!isResponse) { throw new Exception("Invalid DNS response"); } - var nscount = (ushort)((responseBytes[offset++] << 8) | responseBytes[offset++]); - var arcount = (ushort)((responseBytes[offset++] << 8) | responseBytes[offset++]); + var nscount = (ushort)((responseBytes[8] << 8) | responseBytes[9]); + var arcount = (ushort)((responseBytes[10] << 8) | responseBytes[11]); + + // We parsed the header set the offset past the header + offset = 12; var questions = new List(); - for (int i = 0; i < qdcount; i++) + + for (var i = 0; i < qdcount; i++) { var question = ParseDnsQuestionRecord(responseBytes, ref offset); if (question != null) @@ -138,8 +185,9 @@ namespace EonaCat.DnsTester.Helpers } } + // Parse the DNS answer records var answers = new List(); - for (int i = 0; i < qdcount; i++) + for (var i = 0; i < ancount; i++) { try { @@ -151,13 +199,13 @@ namespace EonaCat.DnsTester.Helpers } catch (Exception exception) { - OnLog?.Invoke(null, $"Answer exception: {exception.Message}"); + OnLog?.Invoke(null, $"Answer exception: " + exception.Message); } } // Parse the DNS authority records var authorities = new List(); - for (int i = 0; i < nscount; i++) + for (var i = 0; i < nscount; i++) { try { @@ -169,13 +217,13 @@ namespace EonaCat.DnsTester.Helpers } catch (Exception exception) { - OnLog?.Invoke(null, $"Authority answer exception: {exception.Message}"); + OnLog?.Invoke(null, $"Authority answer exception: " + exception.Message); } } // Parse the DNS additional records var additionals = new List(); - for (int i = 0; i < arcount; i++) + for (var i = 0; i < arcount; i++) { try { @@ -187,7 +235,7 @@ namespace EonaCat.DnsTester.Helpers } catch (Exception exception) { - OnLog?.Invoke(null, $"Additional answer exception: {exception.Message}"); + OnLog?.Invoke(null, $"Additional answer exception: " + exception.Message); } } @@ -206,61 +254,53 @@ namespace EonaCat.DnsTester.Helpers }; } - private static DnsQuestion ParseDnsQuestionRecord(byte[] queryBytes, ref int offset) - { - var name = DnsNameParser.ParseName(queryBytes, ref offset); - if (name == null) - { - return null; - } - - var type = (DnsRecordType)((queryBytes[offset++] << 8) | queryBytes[offset++]); - var qclass = (DnsRecordClass)((queryBytes[offset++] << 8) | queryBytes[offset++]); - - return new DnsQuestion - { - Name = name, - Type = type, - Class = qclass, - }; - } - - private static ResourceRecord ParseDnsAnswerRecord(byte[] responseBytes, ref int offset) + static ResourceRecord ParseDnsAnswerRecord(byte[] responseBytes, ref int offset) { + // Parse the DNS name var name = DnsNameParser.ExtractDomainName(responseBytes, ref offset); if (name == null) { return null; } + // Parse the DNS type, class, ttl, and data length var type = (DnsRecordType)((responseBytes[offset++] << 8) + responseBytes[offset++]); var klass = (DnsRecordClass)((responseBytes[offset++] << 8) + responseBytes[offset++]); var ttl = (responseBytes[offset++] << 24) + (responseBytes[offset++] << 16) + (responseBytes[offset++] << 8) + responseBytes[offset++]; - var dataLength = (responseBytes[offset++] << 8) + responseBytes[offset++]; - string dataAsString = null; - switch (type) + // Extract record data length + var dataLength = (responseBytes[offset] << 8) + responseBytes[offset + 1]; + offset += 2; + + // Extract record data + var recordData = new byte[dataLength]; + Buffer.BlockCopy(responseBytes, offset, recordData, 0, dataLength); + + string recordDataAsString = null; + switch ((DnsRecordType)type) { case DnsRecordType.A: if (dataLength != 4) { return null; } - dataAsString = new IPAddress(responseBytes, offset).ToString(); - offset += dataLength; + recordDataAsString = new IPAddress(recordData).ToString(); + offset += recordData.Length; break; case DnsRecordType.CNAME: + recordDataAsString = DnsNameParser.ExtractDomainName(responseBytes, ref offset); + break; case DnsRecordType.NS: - dataAsString = DnsNameParser.ExtractDomainName(responseBytes, ref offset); + recordDataAsString = DnsNameParser.ExtractDomainName(responseBytes, ref offset); break; case DnsRecordType.MX: - var preference = (responseBytes[offset++] << 8) + responseBytes[offset++]; + var preference = (responseBytes[0] << 8) + responseBytes[1]; + offset += 2; var exchange = DnsNameParser.ExtractDomainName(responseBytes, ref offset); - dataAsString = $"{preference} {exchange}"; + recordDataAsString = $"{preference} {exchange}"; break; case DnsRecordType.TXT: - dataAsString = Encoding.ASCII.GetString(responseBytes, offset, dataLength); - offset += dataLength; + recordDataAsString = Encoding.ASCII.GetString(recordData); break; default: offset += dataLength; @@ -273,10 +313,19 @@ namespace EonaCat.DnsTester.Helpers Type = type, Class = klass, Ttl = TimeSpan.FromSeconds(ttl), - Data = dataAsString, - DataLength = (ushort)dataLength, + Data = recordDataAsString, }; } + + static string GetString(byte[] bytes, ref int index, int length) + { + var str = ""; + for (var i = 0; i < length; i++) + { + str += (char)bytes[index++]; + } + return str; + } } public class DnsQuestion @@ -322,11 +371,35 @@ namespace EonaCat.DnsTester.Helpers public enum DnsRecordClass : ushort { + /// + /// The Internet. + /// Internet = 1, + + /// + /// The CSNET class (Obsolete - used only for examples insome obsolete RFCs). + /// CS = 2, + + /// + /// The CHAOS class. + /// CH = 3, + + /// + /// Hesiod[Dyer 87]. + /// HS = 4, + + /// + /// Used in UPDATE message to signify no class. + /// None = 254, + + /// + /// Only used in QCLASS. + /// + /// ANY = 255 } } diff --git a/EonaCat.DnsTester/Helpers/DnsNameParser.cs b/EonaCat.DnsTester/Helpers/DnsNameParser.cs index 984688e..4233981 100644 --- a/EonaCat.DnsTester/Helpers/DnsNameParser.cs +++ b/EonaCat.DnsTester/Helpers/DnsNameParser.cs @@ -7,7 +7,7 @@ namespace EonaCat.DnsTester.Helpers { public static string ParseName(byte[] responseBytes, ref int offset) { - List labels = new List(); + var labels = new List(); int length; while ((length = responseBytes[offset++]) != 0) @@ -15,8 +15,8 @@ namespace EonaCat.DnsTester.Helpers if ((length & 0xC0) == 0xC0) { // The name is compressed - int pointer = ((length & 0x3F) << 8) | responseBytes[offset++]; - int savedOffset = offset; + var pointer = ((length & 0x3F) << 8) | responseBytes[offset++]; + var savedOffset = offset; offset = pointer; labels.AddRange(ParseName(responseBytes, ref offset).Split('.')); offset = savedOffset; @@ -24,7 +24,7 @@ namespace EonaCat.DnsTester.Helpers } // The name is not compressed - labels.Add(System.Text.Encoding.ASCII.GetString(responseBytes, offset, length)); + labels.Add(Encoding.ASCII.GetString(responseBytes, offset, length)); offset += length; } @@ -33,11 +33,11 @@ namespace EonaCat.DnsTester.Helpers public static string ExtractDomainName(byte[] buffer, ref int offset) { - List labels = new List(); + var labels = new List(); while (true) { - byte labelLength = buffer[offset++]; + var labelLength = buffer[offset++]; if (labelLength == 0) { @@ -47,12 +47,12 @@ namespace EonaCat.DnsTester.Helpers if ((labelLength & 0xC0) == 0xC0) { // Compressed domain name - int pointer = (int)(((labelLength & 0x3F) << 8) + buffer[offset++]); + var pointer = (int)(((labelLength & 0x3F) << 8) + buffer[offset++]); labels.Add(ExtractDomainName(buffer, ref pointer)); break; } - string label = Encoding.ASCII.GetString(buffer, offset, labelLength); + var label = Encoding.ASCII.GetString(buffer, offset, labelLength); labels.Add(label); offset += labelLength; } diff --git a/EonaCat.DnsTester/Helpers/UrlHelper.cs b/EonaCat.DnsTester/Helpers/UrlHelper.cs index 5ca1235..801dd79 100644 --- a/EonaCat.DnsTester/Helpers/UrlHelper.cs +++ b/EonaCat.DnsTester/Helpers/UrlHelper.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; +using System.Net.Http; using System.Security.Cryptography; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -10,84 +10,95 @@ namespace EonaCat.DnsTester.Helpers { internal class UrlHelper { - private static readonly RandomNumberGenerator _randomNumberGenerator = RandomNumberGenerator.Create(); + private static readonly RandomNumberGenerator RandomNumberGenerator = RandomNumberGenerator.Create(); public static event EventHandler Log; public static bool UseSearchEngineYahoo { get; set; } public static bool UseSearchEngineBing { get; set; } public static bool UseSearchEngineGoogle { get; set; } public static bool UseSearchEngineQwant { get; set; } - public static bool UseSearchEngineAsk { get; set; } public static bool UseSearchEngineWolfram { get; set; } public static bool UseSearchEngineStartPage { get; set; } public static bool UseSearchEngineYandex { get; set; } - private static List GetRandomUrls(int totalUrls) + private static async Task> GetRandomUrlsAsync(int totalUrls) { var letters = GetRandomLetters(); var searchEngineUrls = GetSearchEngines(); + var rand = new Random(); + var urls = new List(); - List urls = new List(); - - Parallel.ForEach(searchEngineUrls, searchEngine => + while (urls.Count < totalUrls) { - if (urls.Count >= totalUrls) - return; + var index = rand.Next(searchEngineUrls.Count); + var searchEngine = searchEngineUrls.ElementAt(index); + var url = searchEngine.Value + letters; - string url = searchEngine.Value + letters; - - using (var client = new WebClient()) + using (var httpClient = new HttpClient()) { - string responseString = null; try { - responseString = client.DownloadString(url); + var response = await httpClient.GetAsync(url).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + // find all .xxx.com addresses + var hostNames = Regex.Matches(responseString, @"[.](\w+[.]com)"); + + // Loop through the match collection to retrieve all matches and delete the leading "." + var uniqueNames = new HashSet(); + foreach (Match match in hostNames) + { + var name = match.Groups[1].Value; + if (name != $"{searchEngine.Key.ToLower()}.com") + { + uniqueNames.Add(name); + } + } + + // Add the names to the list + foreach (var name in uniqueNames) + { + if (urls.Count >= totalUrls) + { + break; + } + + if (!urls.Contains(name)) + { + urls.Add(name); + } + } + } + else + { + // Handle non-successful status codes (optional) + searchEngineUrls.Remove(searchEngine.Key); + SetStatus($"{searchEngine.Key}: {response.StatusCode}"); + } } catch (Exception ex) { + // Handle exceptions (optional) searchEngineUrls.Remove(searchEngine.Key); SetStatus($"{searchEngine.Key}: {ex.Message}"); } - - if (responseString == null) - return; - - // find all .xxx.com addresses - MatchCollection hostNames = Regex.Matches(responseString, @"[.](\w+[.]com)"); - - // Loop through the match collection to retrieve all matches and delete the leading "." - HashSet uniqueNames = new HashSet(); - foreach (Match match in hostNames) - { - string name = match.Groups[1].Value; - if (name != $"{searchEngine.Key.ToLower()}.com") - { - uniqueNames.Add(name); - } - } - - lock (urls) - { - // Add the names to the list - foreach (string name in uniqueNames) - { - if (urls.Count >= totalUrls) - break; - - if (!urls.Contains(name)) - urls.Add(name); - } - } } - }); + + letters = GetRandomLetters(); + await Task.Delay(100).ConfigureAwait(false); + } var urlText = "url" + (urls.Count > 1 ? "'s" : string.Empty); SetStatus($"{urls.Count} random {urlText} found"); return urls; } + private static Dictionary GetSearchEngines() { - Dictionary searchEngineUrls = new Dictionary(); + var searchEngineUrls = new Dictionary(); if (UseSearchEngineYahoo) { searchEngineUrls.Add("Yahoo", "https://search.yahoo.com/search?p="); @@ -108,11 +119,6 @@ namespace EonaCat.DnsTester.Helpers searchEngineUrls.Add("Qwant", "https://www.qwant.com/?q="); } - if (UseSearchEngineAsk) - { - searchEngineUrls.Add("Ask", "https://www.ask.com/web?q="); - } - if (UseSearchEngineWolfram) { searchEngineUrls.Add("WolframAlpha", "https://www.wolframalpha.com/input/?i="); @@ -131,18 +137,20 @@ namespace EonaCat.DnsTester.Helpers return searchEngineUrls; } - public static List RetrieveUrls(int numThreads, int numUrlsPerThread) + public static async Task> RetrieveUrlsAsync(int numThreads, int numUrlsPerThread) { - List urlList = new List(); + var tasks = new Task[numThreads]; - Parallel.For(0, numThreads, _ => + var urlList = new List(); + + // start each thread to retrieve a subset of unique URLs + for (var i = 0; i < numThreads; i++) { - var threadUrls = GetRandomUrls(numUrlsPerThread); - lock (urlList) - { - urlList.AddRange(threadUrls); - } - }); + tasks[i] = Task.Run(async () => urlList.AddRange(await GetRandomUrlsAsync(numUrlsPerThread).ConfigureAwait(false))); + } + + // wait for all threads to complete + await Task.WhenAll(tasks).ConfigureAwait(false); return urlList; } @@ -150,8 +158,8 @@ namespace EonaCat.DnsTester.Helpers private static string GetRandomLetters() { // Generate a cryptographically strong random string - byte[] randomBytes = new byte[32]; - _randomNumberGenerator.GetBytes(randomBytes); + var randomBytes = new byte[32]; + RandomNumberGenerator.GetBytes(randomBytes); return Convert.ToBase64String(randomBytes); } diff --git a/EonaCat.DnsTester/MainForm.Designer.cs b/EonaCat.DnsTester/MainForm.Designer.cs index d4d8763..c0277e8 100644 --- a/EonaCat.DnsTester/MainForm.Designer.cs +++ b/EonaCat.DnsTester/MainForm.Designer.cs @@ -37,7 +37,6 @@ checkBox7 = new System.Windows.Forms.CheckBox(); checkBox6 = new System.Windows.Forms.CheckBox(); checkBox5 = new System.Windows.Forms.CheckBox(); - checkBox4 = new System.Windows.Forms.CheckBox(); checkBox3 = new System.Windows.Forms.CheckBox(); checkBox2 = new System.Windows.Forms.CheckBox(); checkBox1 = new System.Windows.Forms.CheckBox(); @@ -108,6 +107,7 @@ // // tabPage1 // + tabPage1.BackColor = System.Drawing.Color.LightGray; tabPage1.Controls.Add(panel2); tabPage1.Controls.Add(panel1); tabPage1.Location = new System.Drawing.Point(10, 58); @@ -116,16 +116,14 @@ tabPage1.Size = new System.Drawing.Size(2239, 1449); tabPage1.TabIndex = 0; tabPage1.Text = "Dns tester"; - tabPage1.UseVisualStyleBackColor = true; // // panel2 // - panel2.BackColor = System.Drawing.SystemColors.ControlLight; + panel2.BackColor = System.Drawing.Color.Gold; panel2.Controls.Add(checkBox8); panel2.Controls.Add(checkBox7); panel2.Controls.Add(checkBox6); panel2.Controls.Add(checkBox5); - panel2.Controls.Add(checkBox4); panel2.Controls.Add(checkBox3); panel2.Controls.Add(checkBox2); panel2.Controls.Add(checkBox1); @@ -142,7 +140,7 @@ checkBox8.AutoSize = true; checkBox8.Checked = true; checkBox8.CheckState = System.Windows.Forms.CheckState.Checked; - checkBox8.Location = new System.Drawing.Point(1301, 42); + checkBox8.Location = new System.Drawing.Point(1172, 38); checkBox8.Name = "checkBox8"; checkBox8.Size = new System.Drawing.Size(143, 45); checkBox8.TabIndex = 70; @@ -154,7 +152,7 @@ checkBox7.AutoSize = true; checkBox7.Checked = true; checkBox7.CheckState = System.Windows.Forms.CheckState.Checked; - checkBox7.Location = new System.Drawing.Point(1301, 135); + checkBox7.Location = new System.Drawing.Point(1172, 135); checkBox7.Name = "checkBox7"; checkBox7.Size = new System.Drawing.Size(150, 45); checkBox7.TabIndex = 69; @@ -166,7 +164,7 @@ checkBox6.AutoSize = true; checkBox6.Checked = true; checkBox6.CheckState = System.Windows.Forms.CheckState.Checked; - checkBox6.Location = new System.Drawing.Point(1109, 135); + checkBox6.Location = new System.Drawing.Point(959, 135); checkBox6.Name = "checkBox6"; checkBox6.Size = new System.Drawing.Size(181, 45); checkBox6.TabIndex = 68; @@ -178,31 +176,19 @@ checkBox5.AutoSize = true; checkBox5.Checked = true; checkBox5.CheckState = System.Windows.Forms.CheckState.Checked; - checkBox5.Location = new System.Drawing.Point(824, 135); + checkBox5.Location = new System.Drawing.Point(659, 135); checkBox5.Name = "checkBox5"; checkBox5.Size = new System.Drawing.Size(244, 45); checkBox5.TabIndex = 67; checkBox5.Text = "WolframAlpha"; checkBox5.UseVisualStyleBackColor = true; // - // checkBox4 - // - checkBox4.AutoSize = true; - checkBox4.Checked = true; - checkBox4.CheckState = System.Windows.Forms.CheckState.Checked; - checkBox4.Location = new System.Drawing.Point(654, 135); - checkBox4.Name = "checkBox4"; - checkBox4.Size = new System.Drawing.Size(103, 45); - checkBox4.TabIndex = 66; - checkBox4.Text = "Ask"; - checkBox4.UseVisualStyleBackColor = true; - // // checkBox3 // checkBox3.AutoSize = true; checkBox3.Checked = true; checkBox3.CheckState = System.Windows.Forms.CheckState.Checked; - checkBox3.Location = new System.Drawing.Point(1109, 42); + checkBox3.Location = new System.Drawing.Point(959, 42); checkBox3.Name = "checkBox3"; checkBox3.Size = new System.Drawing.Size(154, 45); checkBox3.TabIndex = 65; @@ -214,7 +200,7 @@ checkBox2.AutoSize = true; checkBox2.Checked = true; checkBox2.CheckState = System.Windows.Forms.CheckState.Checked; - checkBox2.Location = new System.Drawing.Point(824, 41); + checkBox2.Location = new System.Drawing.Point(819, 41); checkBox2.Name = "checkBox2"; checkBox2.Size = new System.Drawing.Size(115, 45); checkBox2.TabIndex = 64; @@ -226,7 +212,7 @@ checkBox1.AutoSize = true; checkBox1.Checked = true; checkBox1.CheckState = System.Windows.Forms.CheckState.Checked; - checkBox1.Location = new System.Drawing.Point(654, 42); + checkBox1.Location = new System.Drawing.Point(659, 42); checkBox1.Name = "checkBox1"; checkBox1.Size = new System.Drawing.Size(138, 45); checkBox1.TabIndex = 63; @@ -246,6 +232,7 @@ // // StatusBox // + StatusBox.BackColor = System.Drawing.Color.OldLace; StatusBox.FormattingEnabled = true; StatusBox.HorizontalScrollbar = true; StatusBox.ItemHeight = 41; @@ -257,6 +244,7 @@ // // ResultView // + ResultView.BackColor = System.Drawing.Color.OldLace; ResultView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { Url, DNS1DATA, DNS1Performance, DNS2DATA, DNS2Performance }); ResultView.GridLines = true; ResultView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; @@ -300,7 +288,7 @@ // // panel1 // - panel1.BackColor = System.Drawing.SystemColors.ControlLight; + panel1.BackColor = System.Drawing.Color.Gold; panel1.Controls.Add(numericUpDown2); panel1.Controls.Add(label2); panel1.Controls.Add(comboBox1); @@ -578,6 +566,7 @@ AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; AutoSize = true; AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + BackColor = System.Drawing.SystemColors.MenuHighlight; ClientSize = new System.Drawing.Size(2517, 1693); Controls.Add(tabControl1); Controls.Add(RunTest); @@ -647,7 +636,6 @@ private System.Windows.Forms.CheckBox checkBox7; private System.Windows.Forms.CheckBox checkBox6; private System.Windows.Forms.CheckBox checkBox5; - private System.Windows.Forms.CheckBox checkBox4; } } diff --git a/EonaCat.DnsTester/MainForm.cs b/EonaCat.DnsTester/MainForm.cs index b240762..96e6d8c 100644 --- a/EonaCat.DnsTester/MainForm.cs +++ b/EonaCat.DnsTester/MainForm.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; -using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using EonaCat.DnsTester.Helpers; @@ -44,13 +43,13 @@ namespace EonaCat.DnsTester return; } - List urls = new List(); + var urls = new List(); SetupView(); - int numThreads = (int)numericUpDown2.Value; // number of concurrent threads to use - int maxUrls = (int)numericUpDown1.Value; // maximum number of unique URLs to retrieve - int numUrlsPerThread = maxUrls / numThreads; + var numThreads = (int)numericUpDown2.Value; // number of concurrent threads to use + var maxUrls = (int)numericUpDown1.Value; // maximum number of unique URLs to retrieve + var numUrlsPerThread = maxUrls / numThreads; if (numUrlsPerThread == 0) { numUrlsPerThread = maxUrls; @@ -58,11 +57,11 @@ namespace EonaCat.DnsTester } SetSearchEngines(); - urls = UrlHelper.RetrieveUrls(numThreads, numUrlsPerThread); + urls = await UrlHelper.RetrieveUrlsAsync(numThreads, numUrlsPerThread).ConfigureAwait(false); AddUrlToView(urls); IsRunning = true; - await Process(_recordType, urls.ToArray(), _dnsServer1, _dnsServer2).ConfigureAwait(false); + await ProcessAsync(_recordType, urls.ToArray(), _dnsServer1, _dnsServer2).ConfigureAwait(false); IsRunning = false; } @@ -72,7 +71,6 @@ namespace EonaCat.DnsTester UrlHelper.UseSearchEngineBing = checkBox2.Checked; UrlHelper.UseSearchEngineGoogle = checkBox3.Checked; UrlHelper.UseSearchEngineQwant = checkBox8.Checked; - UrlHelper.UseSearchEngineAsk = checkBox4.Checked; UrlHelper.UseSearchEngineWolfram = checkBox5.Checked; UrlHelper.UseSearchEngineStartPage = checkBox6.Checked; UrlHelper.UseSearchEngineYandex = checkBox7.Checked; @@ -127,23 +125,26 @@ namespace EonaCat.DnsTester private void AddUrlToView(List urls) { - foreach (var currentUrl in urls) + ResultView.Invoke(() => { - ListViewItem listURL = new ListViewItem(currentUrl); - listURL.SubItems.Add(" "); - listURL.SubItems.Add(" "); - listURL.SubItems.Add(" "); - listURL.SubItems.Add(" "); + foreach (var currentUrl in urls) + { + var listUrl = new ListViewItem(currentUrl); + listUrl.SubItems.Add(" "); + listUrl.SubItems.Add(" "); + listUrl.SubItems.Add(" "); + listUrl.SubItems.Add(" "); - ResultView.Items.Add(listURL); - } + ResultView.Items.Add(listUrl); + } - if (ResultView.Items.Count > 1) - { - ResultView.EnsureVisible(ResultView.Items.Count - 1); - } + if (ResultView.Items.Count > 1) + { + ResultView.EnsureVisible(ResultView.Items.Count - 1); + } - ResultView.Update(); + ResultView.Update(); + }); Application.DoEvents(); } @@ -162,13 +163,13 @@ namespace EonaCat.DnsTester dnsList2.DisplayMember = "name"; var serverList = Path.Combine(Application.StartupPath, "Servers.xml"); - DataSet servers1 = new DataSet(); - DataSet servers2 = new DataSet(); + var servers1 = new DataSet(); + var servers2 = new DataSet(); servers1.ReadXml(serverList); servers2.ReadXml(serverList); - DataTable dataTable1 = servers1.Tables[0]; - DataTable dataTable2 = servers2.Tables[0]; + var dataTable1 = servers1.Tables[0]; + var dataTable2 = servers2.Tables[0]; dnsList1.DataSource = dataTable1; dnsList2.DataSource = dataTable2; } @@ -195,46 +196,46 @@ namespace EonaCat.DnsTester } - private async Task Process(DnsRecordType recordType, string[] urls, string dnsAddress1, string dnsAddress2) + private async Task ProcessAsync(DnsRecordType recordType, string[] urls, string dnsAddress1, string dnsAddress2) { if (recordType == 0) { recordType = DnsRecordType.A; } - int urlsTotal = urls.Length; + var urlsTotal = urls.Length; const string dnsId1 = "Dns1"; const string dnsId2 = "Dns2"; DnsHelper.OnLog -= DnsHelper_OnLog; DnsHelper.OnLog += DnsHelper_OnLog; - for (int i = 0; i < urlsTotal; i++) + for (var i = 0; i < urlsTotal; i++) { var currentUrl = urls[i]; - await ExecuteDns1(recordType, dnsAddress1, currentUrl, dnsId1, i); + await ExecuteDns1Async(recordType, dnsAddress1, currentUrl, dnsId1, i).ConfigureAwait(false); if (chkDns2.Checked) { - await ExecuteDns2(recordType, dnsAddress2, currentUrl, dnsId2, i); + await ExecuteDns2Async(recordType, dnsAddress2, currentUrl, dnsId2, i).ConfigureAwait(false); } - await Task.Delay(100); + await Task.Delay(100).ConfigureAwait(false); } } - private async Task ExecuteDns2(DnsRecordType recordType, string dnsAddress2, string currentUrl, string dnsId2, + private async Task ExecuteDns2Async(DnsRecordType recordType, string dnsAddress2, string currentUrl, string dnsId2, int i) { try { DnsResponse response2 = null; - byte[] queryBytes2 = DnsHelper.CreateDnsQueryPacket(currentUrl, recordType); - response2 = await DnsHelper.SendDnsQueryPacket(dnsId2, dnsAddress2, 53, queryBytes2); + var queryBytes2 = DnsHelper.CreateDnsQueryPacket(currentUrl, recordType); + response2 = await DnsHelper.SendDnsQueryPacketAsync(dnsId2, dnsAddress2, 53, queryBytes2).ConfigureAwait(false); ProcessResponse(response2); } catch (SocketException socketException) { SetStatus( - Convert.ToString(socketException).IndexOf("time", StringComparison.Ordinal) > 0 + Convert.ToString(socketException)!.IndexOf("time", StringComparison.Ordinal) > 0 ? $"DNS1 Timeout - No response received for {Convert.ToString(DnsHelper.DnsReceiveTimeout / 1000)} seconds" : Convert.ToString(socketException)); } @@ -245,7 +246,7 @@ namespace EonaCat.DnsTester } } - private async Task ExecuteDns1(DnsRecordType recordType, string dnsAddress1, string currentUrl, string dnsId1, + private async Task ExecuteDns1Async(DnsRecordType recordType, string dnsAddress1, string currentUrl, string dnsId1, int i) { if (chkDns1.Checked) @@ -253,14 +254,14 @@ namespace EonaCat.DnsTester try { DnsResponse response1 = null; - byte[] queryBytes1 = DnsHelper.CreateDnsQueryPacket(currentUrl, recordType); - response1 = await DnsHelper.SendDnsQueryPacket(dnsId1, dnsAddress1, 53, queryBytes1); + var queryBytes1 = DnsHelper.CreateDnsQueryPacket(currentUrl, recordType); + response1 = await DnsHelper.SendDnsQueryPacketAsync(dnsId1, dnsAddress1, 53, queryBytes1).ConfigureAwait(false); ProcessResponse(response1); } catch (SocketException socketException) { SetStatus( - Convert.ToString(socketException).IndexOf("time", StringComparison.Ordinal) > 0 + Convert.ToString(socketException)!.IndexOf("time", StringComparison.Ordinal) > 0 ? $"DNS1 Timeout - No response received for {Convert.ToString(DnsHelper.DnsReceiveTimeout / 1000)} seconds" : Convert.ToString(socketException)); } @@ -279,7 +280,7 @@ namespace EonaCat.DnsTester private void ProcessResponse(DnsResponse dnsResponse) { - if (dnsResponse?.Answers == null || !dnsResponse.Answers.Any()) + if (dnsResponse == null || dnsResponse?.Answers == null || !dnsResponse.Answers.Any()) { return; } @@ -296,7 +297,7 @@ namespace EonaCat.DnsTester ResultView.Invoke(() => { - for (int i = 0; i < ResultView.Items.Count; i++) + for (var i = 0; i < ResultView.Items.Count; i++) { foreach (var answer in dnsResponse.Answers) { @@ -308,7 +309,7 @@ namespace EonaCat.DnsTester { case "Dns1": ResultView.Items[i].SubItems[1].Text = - Convert.ToString(answer.Data); + Convert.ToString(answer.Data) ?? string.Empty; sDeltaTime = Convert.ToString(deltaTime); ResultView.Items[i].SubItems[2].Text = sDeltaTime.Length > 5 ? sDeltaTime.Substring(0, 5) : sDeltaTime; @@ -321,7 +322,7 @@ namespace EonaCat.DnsTester case "Dns2": ResultView.Items[i].SubItems[3].Text = - Convert.ToString(answer.Data); + Convert.ToString(answer.Data) ?? string.Empty; sDeltaTime = Convert.ToString(deltaTime); ResultView.Items[i].SubItems[4].Text = sDeltaTime.Length > 5 ? sDeltaTime.Substring(0, 5) : sDeltaTime; @@ -345,13 +346,27 @@ namespace EonaCat.DnsTester return; } - if (!IPAddress.TryParse(txtResolveIP.Text, out IPAddress iPAddress)) + if (!IPAddress.TryParse(txtResolveIP.Text, out var iPAddress)) { MessageBox.Show("Please enter a valid IP address"); return; } - await ResolveIP().ConfigureAwait(false); + await Task.Run(() => + { + try + { + var dnsEntry = Dns.GetHostEntry(iPAddress); + txtResolveHost.Invoke(() => + { + txtResolveHost.Text = dnsEntry.HostName; + }); + } + catch (Exception) + { + MessageBox.Show($"Could not get hostname for IP address '{txtResolveIP.Text}'"); + } + }).ConfigureAwait(false); } private async void btnResolveHost_Click(object sender, EventArgs e) @@ -361,75 +376,38 @@ namespace EonaCat.DnsTester MessageBox.Show("Please enter a hostname to resolve"); return; } - await ResolveHost().ConfigureAwait(false); - } - private async Task ResolveIP() - { - if (string.IsNullOrWhiteSpace(txtResolveIP.Text)) - { - MessageBox.Show("Please enter an IP address to resolve"); - return; - } - if (!IPAddress.TryParse(txtResolveIP.Text, out IPAddress iPAddress)) + await Task.Run(() => { - MessageBox.Show("Please enter a valid IP address"); - return; - } - - try - { - var dnsEntry = await Dns.GetHostEntryAsync(iPAddress); - txtResolveHost.Text = dnsEntry.HostName; - } - catch (Exception) - { - MessageBox.Show($"Could not get hostname for IP address '{txtResolveIP.Text}'"); - } - } - - private async Task ResolveHost() - { - if (string.IsNullOrWhiteSpace(txtResolveHost.Text)) - { - MessageBox.Show("Please enter a hostname to resolve"); - return; - } - - try - { - var dnsEntry = await Dns.GetHostEntryAsync(txtResolveHost.Text); - var ipAddress = dnsEntry.AddressList.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork); - if (ipAddress != null) + try { - txtResolveIP.Text = ipAddress.ToString(); + var dnsEntry = Dns.GetHostEntry(txtResolveHost.Text); + + txtResolveHost.Invoke(() => + { + txtResolveIP.Text = dnsEntry.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).FirstOrDefault().Address.ToString(); + }); } - else + catch (Exception) { MessageBox.Show($"Could not get IP address for hostname '{txtResolveHost.Text}'"); } - } - catch (Exception) - { - MessageBox.Show($"Could not get IP address for hostname '{txtResolveHost.Text}'"); - } + }).ConfigureAwait(false); } private void SetStatus(string text) { - StringBuilder sb = new StringBuilder(StatusBox.Items.Count + 1); - sb.AppendLine($"{DateTime.Now} {text}"); - StatusBox.Invoke(() => { - StatusBox.Items.Add(sb.ToString()); + StatusBox.Items.Add($"{DateTime.Now} {text}"); StatusBox.TopIndex = StatusBox.Items.Count - 1; - if (StatusBox.Items.Count > STATUS_BAR_SIZE) { StatusBox.Items.RemoveAt(0); } + + StatusBox.Update(); }); } diff --git a/EonaCat.DnsTester/MainForm.resx b/EonaCat.DnsTester/MainForm.resx index 9a7c568..a6da628 100644 --- a/EonaCat.DnsTester/MainForm.resx +++ b/EonaCat.DnsTester/MainForm.resx @@ -1,4 +1,64 @@ - + + + diff --git a/Screenshots/1.png b/Screenshots/1.png index 4c1df74..fb8e984 100644 Binary files a/Screenshots/1.png and b/Screenshots/1.png differ