Updated UI

This commit is contained in:
EonaCat 2023-07-19 16:35:30 +02:00
parent 16c642dd20
commit 6ae0e0b779
7 changed files with 380 additions and 273 deletions

View File

@ -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<string> OnLog;
private static readonly Random random = new Random();
public static async Task<DnsResponse> SendDnsQueryPacket(string dnsId, string server, int port, byte[] queryBytes)
public static async Task<DnsResponse> 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<DnsQuestion>();
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<ResourceRecord>();
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<ResourceRecord>();
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<ResourceRecord>();
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
{
/// <summary>
/// The Internet.
/// </summary>
Internet = 1,
/// <summary>
/// The CSNET class (Obsolete - used only for examples insome obsolete RFCs).
/// </summary>
CS = 2,
/// <summary>
/// The CHAOS class.
/// </summary>
CH = 3,
/// <summary>
/// Hesiod[Dyer 87].
/// </summary>
HS = 4,
/// <summary>
/// Used in UPDATE message to signify no class.
/// </summary>
None = 254,
/// <summary>
/// Only used in QCLASS.
/// </summary>
/// <seealso cref="Question.Class"/>
ANY = 255
}
}

View File

@ -7,7 +7,7 @@ namespace EonaCat.DnsTester.Helpers
{
public static string ParseName(byte[] responseBytes, ref int offset)
{
List<string> labels = new List<string>();
var labels = new List<string>();
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<string> labels = new List<string>();
var labels = new List<string>();
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;
}

View File

@ -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<string> 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<string> GetRandomUrls(int totalUrls)
private static async Task<List<string>> GetRandomUrlsAsync(int totalUrls)
{
var letters = GetRandomLetters();
var searchEngineUrls = GetSearchEngines();
var rand = new Random();
var urls = new List<string>();
List<string> urls = new List<string>();
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<string>();
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<string> uniqueNames = new HashSet<string>();
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<string, string> GetSearchEngines()
{
Dictionary<string, string> searchEngineUrls = new Dictionary<string, string>();
var searchEngineUrls = new Dictionary<string, string>();
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<string> RetrieveUrls(int numThreads, int numUrlsPerThread)
public static async Task<List<string>> RetrieveUrlsAsync(int numThreads, int numUrlsPerThread)
{
List<string> urlList = new List<string>();
var tasks = new Task[numThreads];
Parallel.For(0, numThreads, _ =>
var urlList = new List<string>();
// 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);
}

View File

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

View File

@ -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<string> urls = new List<string>();
var urls = new List<string>();
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<string> 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();
});
}

View File

@ -1,4 +1,64 @@
<root>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing"">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 538 KiB