527 lines
16 KiB
C#
527 lines
16 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using System.Windows.Threading;
|
|
|
|
namespace EonaCat.DnsTester
|
|
{
|
|
public partial class MainForm : Form
|
|
{
|
|
private bool UseCustomDnsServers = false;
|
|
public string DNS1, DNS2;
|
|
|
|
public bool IsRunning { get; private set; }
|
|
|
|
public MainForm()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
private void RunTest_Click(object sender, EventArgs e)
|
|
{
|
|
int i;
|
|
List<string> CleanNames = new List<string>();
|
|
|
|
if (this.UseCustomDnsServers)
|
|
{
|
|
if (chkDns1.Checked)
|
|
{
|
|
DNS1 = CustomDns1.Text;
|
|
}
|
|
|
|
if (chkDns2.Checked)
|
|
{
|
|
DNS2 = CustomDns2.Text;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (chkDns1.Checked)
|
|
{
|
|
DNS1 = DnsList1.SelectedValue.ToString();
|
|
}
|
|
|
|
if (chkDns2.Checked)
|
|
{
|
|
DNS2 = DnsList2.SelectedValue.ToString();
|
|
}
|
|
|
|
}
|
|
|
|
ResultView.Items.Clear();
|
|
ResultView.Update();
|
|
Application.DoEvents();
|
|
|
|
GetWebAddresses(ref CleanNames, (int)numericUpDown1.Value);
|
|
|
|
for (i = 0; i < CleanNames.Count; i++)
|
|
{
|
|
ListViewItem listURL = new ListViewItem(CleanNames[i]);
|
|
listURL.SubItems.Add(" ");
|
|
listURL.SubItems.Add(" ");
|
|
listURL.SubItems.Add(" ");
|
|
listURL.SubItems.Add(" ");
|
|
|
|
ResultView.Items.Add(listURL);
|
|
}
|
|
if (ResultView.Items.Count > 1)
|
|
{
|
|
ResultView.EnsureVisible(ResultView.Items.Count - 1);
|
|
}
|
|
|
|
ResultView.Update();
|
|
Application.DoEvents();
|
|
|
|
if (IsRunning)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IsRunning = true;
|
|
CheckDNS(CleanNames.ToArray(), CleanNames.Count, DNS1, DNS2);
|
|
IsRunning = false;
|
|
}
|
|
|
|
|
|
private void TesterUI_Load(object sender, EventArgs e)
|
|
{
|
|
FillDnsList();
|
|
}
|
|
|
|
private void FillDnsList()
|
|
{
|
|
DnsList1.ValueMember = "ip";
|
|
DnsList1.DisplayMember = "name";
|
|
|
|
DnsList2.ValueMember = "ip";
|
|
DnsList2.DisplayMember = "name";
|
|
|
|
DataSet servers1 = new DataSet();
|
|
DataSet servers2 = new DataSet();
|
|
servers1.ReadXml(Path.Combine(Application.StartupPath, "Servers.xml"));
|
|
servers2.ReadXml(Path.Combine(Application.StartupPath, "Servers.xml"));
|
|
DataTable ns1 = servers1.Tables[0];
|
|
DataTable ns2 = servers2.Tables[0];
|
|
DnsList1.DataSource = ns1;
|
|
DnsList2.DataSource = ns2;
|
|
|
|
}
|
|
|
|
|
|
private void UseCustomServers_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
this.UseCustomDnsServers = UseCustomServers.Checked;
|
|
SetUI();
|
|
}
|
|
|
|
private void SetUI()
|
|
{
|
|
if (this.UseCustomDnsServers)
|
|
{
|
|
this.DnsList1.Enabled = false;
|
|
this.DnsList2.Enabled = false;
|
|
|
|
this.CustomDns1.Enabled = true;
|
|
this.CustomDns2.Enabled = true;
|
|
|
|
this.CustomDns1.Visible = true;
|
|
this.CustomDns2.Visible = true;
|
|
this.lblCustom1.Visible = true;
|
|
this.lblCustom2.Visible = true;
|
|
|
|
}
|
|
else
|
|
{
|
|
this.DnsList1.Enabled = true;
|
|
this.DnsList2.Enabled = true;
|
|
this.CustomDns1.Enabled = false; ;
|
|
this.CustomDns2.Enabled = false;
|
|
|
|
this.CustomDns1.Visible = false;
|
|
this.CustomDns2.Visible = false;
|
|
this.lblCustom1.Visible = false;
|
|
this.lblCustom2.Visible = false;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private void GetWebAddresses(ref List<string> CleanNames, int totalUrls)
|
|
{
|
|
int i, j;
|
|
byte[] random = new Byte[256];
|
|
string Letters = "";
|
|
// Make a 3 letter random string
|
|
|
|
//RNGCryptoServiceProvider is an implementation of a random number generator.
|
|
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
|
|
rng.GetBytes(random); // The array is now filled with cryptographically strong random bytes.
|
|
for (i = 1; i < 256; i++)
|
|
{
|
|
// Just take the first three letters
|
|
if (random[i] > 0x40 && random[i] < 0x5b) Letters = Letters + Convert.ToChar(random[i]);
|
|
if (Letters.Length == 3) break;
|
|
}
|
|
|
|
KeyValuePair<string, string> searchEngine = new KeyValuePair<string, string>();
|
|
Random rand = new Random();
|
|
|
|
// Create a list of search engine URLs to query
|
|
Dictionary<string, string> searchEngineUrls = new Dictionary<string, string>
|
|
{
|
|
{ "DuckDuckGo", "https://duckduckgo.com/html/?q=" },
|
|
{ "Yahoo", "https://search.yahoo.com/search?p=" },
|
|
{ "Bing", "https://www.bing.com/search?q=" },
|
|
{ "Google", "https://www.google.com/search?q=" },
|
|
{ "Ask", "https://www.ask.com/web?q=" },
|
|
{ "AOL", "https://www.aol.com/search?q=" },
|
|
{ "WolframAlpha", "https://www.wolframalpha.com/input/?i=" },
|
|
{ "StartPage", "https://www.startpage.com/do/dsearch?query=" },
|
|
{ "Yandex", "https://www.yandex.com/search/?text=" },
|
|
{ "Qwant", "https://www.qwant.com/?q=" }
|
|
};
|
|
|
|
try
|
|
{
|
|
while (CleanNames.Count < totalUrls)
|
|
{
|
|
int index = rand.Next(searchEngineUrls.Count);
|
|
searchEngine = searchEngineUrls.ElementAt(index);
|
|
|
|
WebRequest Request = WebRequest.Create(searchEngine.Value + Letters); //Letters);
|
|
WebResponse Response = Request.GetResponse();
|
|
Application.DoEvents();
|
|
|
|
// Get the response stream.
|
|
Stream Stream = Response.GetResponseStream();
|
|
StatusBoxPrint(searchEngine.Key + " response received");
|
|
|
|
StreamReader StreamReader = new StreamReader(Stream, Encoding.ASCII);
|
|
string ResponseString = StreamReader.ReadToEnd();
|
|
|
|
// Close the response stream and response to free resources.
|
|
Stream.Close();
|
|
Response.Close();
|
|
Application.DoEvents();
|
|
|
|
// find all .xxx.com addresses
|
|
MatchCollection HostNames;
|
|
string[] Names = new string[totalUrls + 1000];
|
|
|
|
// Create a new Regex object and define the regular expression.
|
|
Regex Dotcom = new Regex("[.]([A-Za-z]*)[.]com");
|
|
|
|
|
|
// Use the Matches method to find all matches in the input string.
|
|
HostNames = Dotcom.Matches(ResponseString);
|
|
|
|
Application.DoEvents();
|
|
|
|
// Loop through the match collection to retrieve all
|
|
// matches and delete the leading "."
|
|
for (i = 0; i < HostNames.Count; i++)
|
|
{
|
|
Names[i] = HostNames[i].Value.Remove(0, 1);
|
|
}
|
|
|
|
// eliminate its own search engine names and repeated entries
|
|
for (i = 0; i < HostNames.Count - 1; i++)
|
|
{
|
|
if (Names[i] == searchEngine.Key.ToLower() + ".com") Names[i] = "";
|
|
for (j = i + 1; j < HostNames.Count; j++)
|
|
{
|
|
if (Names[i] == Names[j]) Names[j] = "";
|
|
}
|
|
}
|
|
|
|
Shuffle(Names);
|
|
|
|
// Add the names to the list
|
|
for (i = 0; i < HostNames.Count; i++)
|
|
{
|
|
if (CleanNames.Count >= totalUrls)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(Names[i]) && !CleanNames.Contains(Names[i]))
|
|
{
|
|
CleanNames.Add(Names[i]);
|
|
}
|
|
}
|
|
}
|
|
StatusBoxPrint(CleanNames.Count + " Random URL's found");
|
|
}
|
|
catch(Exception exception)
|
|
{
|
|
StatusBoxPrint(searchEngine.Key + ": " ?? string.Empty + exception.Message);
|
|
}
|
|
}
|
|
|
|
public static void Shuffle<T>(T[] array)
|
|
{
|
|
Random rng = new Random();
|
|
int n = array.Length;
|
|
while (n > 1)
|
|
{
|
|
n--;
|
|
int k = rng.Next(n + 1);
|
|
T value = array[k];
|
|
array[k] = array[n];
|
|
array[n] = value;
|
|
}
|
|
}
|
|
|
|
private void CheckDNS(string[] URLNames, int URLNamescount, string DNSAddress1, string DNSAddress2)
|
|
{
|
|
int i;
|
|
const int IPPort = 53;
|
|
const string TransactionID1 = "Q1"; // Use transaction ID of Q1 and Q2 to identify our packet and DNS
|
|
const string TransactionID2 = "Q2";
|
|
const string TypeString = "\u0001" + "\u0000" + "\u0000" + "\u0001" + "\u0000" + "\u0000" + "\u0000" + "\u0000" + "\u0000" + "\u0000";
|
|
const string TrailerString = "\u0000" + "\u0000" + "\u0001" + "\u0000" + "\u0001";
|
|
const int DNSReceiveTimeout = 5000;
|
|
string URLNameStart, DomainName, QueryString, ReceiveString, IPResponse, sDeltaTime;
|
|
int URLNameStartLength, DomainNameLength, index, TransactionDNS;
|
|
byte[] Sendbytes = new byte[256];
|
|
long StartTime, StopTime;
|
|
string DeltaTime;
|
|
Socket DNSsocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
|
DNSsocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, DNSReceiveTimeout);
|
|
|
|
IPEndPoint dnsEP1 = null;
|
|
if (chkDns1.Checked && !string.IsNullOrWhiteSpace(DNSAddress1))
|
|
{
|
|
dnsEP1 = new IPEndPoint(IPAddress.Parse(DNSAddress1), IPPort);
|
|
}
|
|
|
|
IPEndPoint dnsEP2 = null;
|
|
if (chkDns2.Checked && !string.IsNullOrWhiteSpace(DNSAddress2))
|
|
{
|
|
dnsEP2 = new IPEndPoint(IPAddress.Parse(DNSAddress2), IPPort);
|
|
}
|
|
|
|
// Start the clock
|
|
StartTime = DateTime.Now.Ticks;
|
|
|
|
for (i = 0; i < URLNamescount; i++)
|
|
{
|
|
URLNameStart = URLNames[i].Substring(0, URLNames[i].IndexOf("."));
|
|
DomainName = URLNames[i].Substring(URLNames[i].IndexOf(".") + 1, URLNames[i].Length - URLNames[i].IndexOf(".") - 1);
|
|
|
|
if (chkDns1.Checked)
|
|
{
|
|
QueryString = TransactionID1 + TypeString + (char)URLNameStart.Length + URLNameStart + (char)DomainName.Length + DomainName + TrailerString;
|
|
Sendbytes = Encoding.ASCII.GetBytes(QueryString);
|
|
if (dnsEP1 != null)
|
|
{
|
|
DNSsocket.SendTo(Sendbytes, Sendbytes.Length, SocketFlags.None, dnsEP1);
|
|
}
|
|
}
|
|
|
|
if (chkDns2.Checked)
|
|
{
|
|
QueryString = TransactionID2 + TypeString + (char)URLNameStart.Length + URLNameStart + (char)DomainName.Length + DomainName + TrailerString;
|
|
Sendbytes = Encoding.ASCII.GetBytes(QueryString);
|
|
if (dnsEP2 != null)
|
|
{
|
|
DNSsocket.SendTo(Sendbytes, Sendbytes.Length, SocketFlags.None, dnsEP2);
|
|
}
|
|
}
|
|
}
|
|
|
|
byte[] Receivebytes = new byte[512];
|
|
int totalReceived = 0;
|
|
|
|
try
|
|
{
|
|
// wait for a response up to timeout
|
|
more:
|
|
DNSsocket.Receive(Receivebytes);
|
|
// make sure the message returned is ours
|
|
if (Receivebytes[0] == Sendbytes[0] && (Receivebytes[1] == 0x31) || (Receivebytes[1] == 0x32))
|
|
{
|
|
if (Receivebytes[2] == 0x81 && Receivebytes[3] == 0x80)
|
|
{
|
|
// Get the time now
|
|
StopTime = DateTime.Now.Ticks;
|
|
DeltaTime = Convert.ToString((double)(StopTime - StartTime) / 10000000);
|
|
|
|
// Decode the answers
|
|
// Find the URL that was returned
|
|
TransactionDNS = Receivebytes[1];
|
|
ReceiveString = Encoding.ASCII.GetString(Receivebytes);
|
|
index = 12;
|
|
URLNameStartLength = Receivebytes[index];
|
|
index++;
|
|
URLNameStart = ReceiveString.Substring(index, URLNameStartLength);
|
|
index = index + URLNameStartLength;
|
|
DomainNameLength = Receivebytes[index];
|
|
index++;
|
|
DomainName = ReceiveString.Substring(index, DomainNameLength);
|
|
index = index + DomainNameLength;
|
|
index = index + 8;
|
|
|
|
// Get the record type
|
|
int ResponseType = Receivebytes[index];
|
|
index = index + 9;
|
|
|
|
// Get the IP address if applicable
|
|
IPResponse = "";
|
|
switch (ResponseType)
|
|
{
|
|
case 1:
|
|
IPResponse = Convert.ToString(Receivebytes[index]) + "."
|
|
+ Convert.ToString(Receivebytes[index + 1]) + "."
|
|
+ Convert.ToString(Receivebytes[index + 2]) + "."
|
|
+ Convert.ToString(Receivebytes[index + 3]); break;
|
|
case 5: IPResponse = "CNAME"; break;
|
|
case 6: IPResponse = "SOA"; break;
|
|
}
|
|
StatusBoxPrint("DNS Answer: " + URLNameStart + "." + DomainName + " = " + IPResponse);
|
|
|
|
// Find the URL entry in the list
|
|
for (i = 0; i < ResultView.Items.Count; i++)
|
|
{
|
|
if (ResultView.Items[i].Text == URLNameStart + "." + DomainName)
|
|
{
|
|
|
|
switch (TransactionDNS)
|
|
{
|
|
case 0x31:
|
|
ResultView.Items[i].SubItems[1].Text = Convert.ToString(IPResponse);
|
|
sDeltaTime = Convert.ToString(DeltaTime);
|
|
if (sDeltaTime.Length > 5) ResultView.Items[i].SubItems[2].Text = sDeltaTime.Substring(0, 5);
|
|
else ResultView.Items[i].SubItems[2].Text = sDeltaTime;
|
|
ResultView.Items[i].ForeColor = System.Drawing.Color.Red;
|
|
ResultView.EnsureVisible(i);
|
|
ResultView.Update();
|
|
Application.DoEvents();
|
|
ResultView.Items[i].ForeColor = System.Drawing.Color.Black;
|
|
break;
|
|
|
|
case 0x32:
|
|
ResultView.Items[i].SubItems[3].Text = Convert.ToString(IPResponse);
|
|
sDeltaTime = Convert.ToString(DeltaTime);
|
|
if (sDeltaTime.Length > 5) ResultView.Items[i].SubItems[4].Text = sDeltaTime.Substring(0, 5);
|
|
else ResultView.Items[i].SubItems[4].Text = sDeltaTime;
|
|
ResultView.Items[i].ForeColor = System.Drawing.Color.Red;
|
|
ResultView.EnsureVisible(i);
|
|
ResultView.Update();
|
|
Application.DoEvents();
|
|
ResultView.Items[i].ForeColor = System.Drawing.Color.Black;
|
|
break;
|
|
}
|
|
|
|
totalReceived++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (totalReceived < URLNamescount)
|
|
{
|
|
goto more;
|
|
}
|
|
}
|
|
}
|
|
catch (SocketException e)
|
|
{
|
|
if (Convert.ToString(e).IndexOf("time") > 0)
|
|
{
|
|
StatusBoxPrint("Timeout - No response received for " + Convert.ToString(DNSReceiveTimeout / 1000) + " seconds");
|
|
}
|
|
else
|
|
{
|
|
StatusBoxPrint(Convert.ToString(e)); // for testing
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
// close the socket
|
|
DNSsocket.Close();
|
|
}
|
|
|
|
}
|
|
|
|
private async void btnResolveIP_Click(object sender, EventArgs e)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(txtResolveIP.Text))
|
|
{
|
|
MessageBox.Show("Please enter an IP address to resolve");
|
|
return;
|
|
}
|
|
|
|
if (!IPAddress.TryParse(txtResolveIP.Text , out IPAddress iPAddress))
|
|
{
|
|
MessageBox.Show("Please enter a valid IP address");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
await Task.Run(() =>
|
|
{
|
|
var dnsEntry = Dns.GetHostEntry(iPAddress);
|
|
Dispatcher.CurrentDispatcher.Invoke(() =>
|
|
{
|
|
txtResolveHost.Text = dnsEntry.HostName;
|
|
});
|
|
});
|
|
}
|
|
catch (Exception)
|
|
{
|
|
MessageBox.Show($"Could not get hostname for IP address '{txtResolveIP.Text}'");
|
|
}
|
|
}
|
|
|
|
private async void btnResolveHost_Click(object sender, EventArgs e)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(txtResolveHost.Text))
|
|
{
|
|
MessageBox.Show("Please enter an hostname to resolve");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
await Task.Run(() =>
|
|
{
|
|
var dnsEntry = Dns.GetHostEntry(txtResolveHost.Text);
|
|
|
|
Dispatcher.CurrentDispatcher.Invoke(() =>
|
|
{
|
|
txtResolveIP.Text = dnsEntry.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).FirstOrDefault().Address.ToString();
|
|
});
|
|
});
|
|
}
|
|
catch (Exception)
|
|
{
|
|
MessageBox.Show($"Could not get hostname for IP address '{txtResolveIP.Text}'");
|
|
}
|
|
}
|
|
|
|
private void StatusBoxPrint(string LogText)
|
|
{
|
|
StatusBox.Items.Add(DateTime.Now + " " + LogText);
|
|
StatusBox.TopIndex = StatusBox.Items.Count - 1;
|
|
if (StatusBox.Items.Count > 5000)
|
|
{
|
|
StatusBox.Items.RemoveAt(0);
|
|
}
|
|
StatusBox.Update();
|
|
}
|
|
|
|
|
|
}
|
|
}
|