diff --git a/ConsoleAppTest/ConsoleAppTest.csproj b/ConsoleAppTest/ConsoleAppTest.csproj
new file mode 100644
index 0000000..373f49d
--- /dev/null
+++ b/ConsoleAppTest/ConsoleAppTest.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ AnyCPU;x86;x64
+
+
+
+
+
+
+
diff --git a/ConsoleAppTest/ConsoleAppTest.csproj.user b/ConsoleAppTest/ConsoleAppTest.csproj.user
new file mode 100644
index 0000000..70b12d7
--- /dev/null
+++ b/ConsoleAppTest/ConsoleAppTest.csproj.user
@@ -0,0 +1,6 @@
+
+
+
+ <_LastSelectedProfileId>D:\PPWin\SecureDNSClient\ConsoleAppTest\Properties\PublishProfiles\FolderProfile.pubxml
+
+
\ No newline at end of file
diff --git a/ConsoleAppTest/Program.cs b/ConsoleAppTest/Program.cs
new file mode 100644
index 0000000..0a7c128
--- /dev/null
+++ b/ConsoleAppTest/Program.cs
@@ -0,0 +1,180 @@
+using MsmhToolsClass;
+using MsmhToolsClass.MsmhAgnosticServer;
+using System.Net;
+
+namespace ConsoleAppTest;
+
+internal class Program
+{
+ static async Task Main(string[] args)
+ {
+ MsmhAgnosticServer server1 = new();
+ MsmhAgnosticServer server2 = new();
+
+ server1.OnRequestReceived += Server_OnRequestReceived;
+ server2.OnRequestReceived += Server_OnRequestReceived;
+
+ string dohUrl = "https://dns.cloudflare.com/dns-query";
+ string dohHost = "dns.cloudflare.com";
+ string dohCleanIP = "104.16.132.229";
+ string cfClenIP = "172.66.192.140";
+
+ // Set DnsRules Content
+ string dnsRulesContent = $"{dohHost}|{dohCleanIP};";
+
+ // Set ProxyRules Content (Apply Fake DNS and White List Program)
+ string proxyRulesContent = $"{dohHost}|{dohCleanIP};";
+ proxyRulesContent += $"\n{dohCleanIP}|+;";
+ proxyRulesContent += $"\n*|-;"; // Block Other Requests
+
+ List dnsServers1 = new()
+ {
+ //"sdns://AQMAAAAAAAAAEjEwMy44Ny42OC4xOTQ6ODQ0MyAxXDKkdrOao8ZeLyu7vTnVrT0C7YlPNNf6trdMkje7QR8yLmRuc2NyeXB0LWNlcnQuZG5zLmJlYmFzaWQuY29t",
+ //"tls://free.shecan.ir",
+ //"https://free.shecan.ir/dns-query",
+ //"tls://dns.google",
+ //"https://notworking.com/dns-query",
+ //"tcp://8.8.8.8:53",
+ //"udp://8.8.8.8:53",
+ //"tcp://1.1.1.1:53",
+ //"https://every1dns.com/dns-query",
+ //"https://dns.cloudflare.com/dns-query",
+ //"https://dns.google/dns-query",
+ //"https://45.90.29.204:443/dns-query",
+ //"udp://208.67.222.222:5353",
+ //"9.9.9.9:9953",
+ dohUrl
+ };
+
+ AgnosticSettings settings1 = new()
+ {
+ Working_Mode = AgnosticSettings.WorkingMode.DnsAndProxy,
+ ListenerPort = 53,
+ DnsTimeoutSec = 10,
+ ProxyTimeoutSec = 40,
+ MaxRequests = 1000000,
+ KillOnCpuUsage = 40,
+ DNSs = dnsServers1,
+ BootstrapIpAddress = IPAddress.Loopback,
+ BootstrapPort = 53,
+ AllowInsecure = false,
+ BlockPort80 = true,
+ CloudflareCleanIP = cfClenIP,
+ UpstreamProxyScheme = $"socks5://{IPAddress.Loopback}:53",
+ //ApplyUpstreamOnlyToBlockedIps = true
+ };
+
+ AgnosticProgram.Fragment fragment = new();
+ fragment.Set(AgnosticProgram.Fragment.Mode.Program, 50, AgnosticProgram.Fragment.ChunkMode.SNI, 5, 2, 1);
+ server1.EnableFragment(fragment);
+
+ AgnosticProgram.DnsRules dnsRules1 = new();
+ dnsRules1.Set(AgnosticProgram.DnsRules.Mode.Text, dnsRulesContent);
+ server1.EnableDnsRules(dnsRules1);
+
+ AgnosticProgram.ProxyRules proxyRules1 = new();
+ proxyRules1.Set(AgnosticProgram.ProxyRules.Mode.Text, proxyRulesContent);
+ server1.EnableProxyRules(proxyRules1);
+
+
+ List dnsServers2 = new()
+ {
+ $"udp://{IPAddress.Loopback}:53"
+ };
+
+ AgnosticSettings settings2 = new()
+ {
+ Working_Mode = AgnosticSettings.WorkingMode.Dns,
+ ListenerPort = 443,
+ DnsTimeoutSec = 10,
+ DNSs = dnsServers2,
+ MaxRequests = 1000000,
+ BootstrapIpAddress = IPAddress.Loopback,
+ BootstrapPort = 53,
+ AllowInsecure = false,
+ //UpstreamProxyScheme = "socks5://192.168.1.120:10808",
+ //ApplyUpstreamOnlyToBlockedIps = true
+ };
+
+ AgnosticSettingsSSL settingsSSL = new(true)
+ {
+ EnableSSL = true,
+ //ChangeSni = true,
+ //DefaultSni = "speedtest.net",
+ };
+
+ await server2.EnableSSL(settingsSSL);
+
+
+
+ server1.Start(settings1);
+ server2.Start(settings2);
+
+
+
+ DnsMessage dmQ1 = DnsMessage.CreateQuery(DnsEnums.DnsProtocol.UDP, "youtube.com", DnsEnums.RRType.A, DnsEnums.CLASS.IN);
+ DnsMessage.TryWrite(dmQ1, out byte[] dmQBytes1);
+ DnsMessage dmQ2 = DnsMessage.CreateQuery(DnsEnums.DnsProtocol.DoH, "mail.yahoo.com", DnsEnums.RRType.A, DnsEnums.CLASS.IN);
+ DnsMessage.TryWrite(dmQ2, out byte[] dmQBytes2);
+ string dns = "udp://127.0.0.1:53";
+ string doh = "https://127.0.0.1:443/dns-query";
+ //doh = "https://dns-cloudflare.com/dns-query";
+
+ IPAddress bootIP = IPAddress.Parse("8.8.8.8");
+ int bootPort = 53;
+
+
+
+ HttpRequest httpRequest = new()
+ {
+ URI = new Uri("https://google.com"),
+ ProxyScheme = "socks5://127.0.0.1:443",
+ TimeoutMS = 5000
+ };
+
+
+ int n = 0;
+
+
+ tt1();
+ //tt1();
+ //tt1();
+ tt2();
+ //tt2();
+ //tt2();
+
+
+ async void tt1()
+ {
+ while (true)
+ {
+ //await Task.Delay(50);
+ n++;
+ await DnsClient.QueryAsync(dmQBytes1, DnsEnums.DnsProtocol.UDP, dns, true, bootIP, bootPort, 1000, CancellationToken.None);
+ //HttpRequest.SendAsync(httpRequest);
+ //if (n == 5000) break;
+ }
+ }
+
+ async void tt2()
+ {
+ while (true)
+ {
+ //await Task.Delay(50);
+ n++;
+ await DnsClient.QueryAsync(dmQBytes2, DnsEnums.DnsProtocol.DoH, doh, true, bootIP, bootPort, 1000, CancellationToken.None);
+ //HttpRequest.SendAsync(httpRequest);
+ if (n == 5000) break;
+ }
+ }
+
+
+ Console.ReadLine();
+ }
+
+ private static void Server_OnRequestReceived(object? sender, EventArgs e)
+ {
+ if (sender is not string msg) return;
+ Console.WriteLine(msg);
+ }
+}
\ No newline at end of file
diff --git a/ConsoleAppTest/Properties/PublishProfiles/FolderProfile.pubxml b/ConsoleAppTest/Properties/PublishProfiles/FolderProfile.pubxml
new file mode 100644
index 0000000..92a3de4
--- /dev/null
+++ b/ConsoleAppTest/Properties/PublishProfiles/FolderProfile.pubxml
@@ -0,0 +1,18 @@
+
+
+
+
+ Release
+ Any CPU
+ bin\Release\net6.0\publish\win-x86\
+ FileSystem
+ <_TargetId>Folder
+ net6.0
+ win-x86
+ false
+ true
+ true
+
+
\ No newline at end of file
diff --git a/ConsoleAppTest/Properties/PublishProfiles/FolderProfile.pubxml.user b/ConsoleAppTest/Properties/PublishProfiles/FolderProfile.pubxml.user
new file mode 100644
index 0000000..8a928bc
--- /dev/null
+++ b/ConsoleAppTest/Properties/PublishProfiles/FolderProfile.pubxml.user
@@ -0,0 +1,10 @@
+
+
+
+
+ True|2024-03-22T20:30:05.2407815Z;
+
+
+
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass.csproj b/MsmhToolsClass/MsmhToolsClass.csproj
index caa5dba..cf16fad 100644
--- a/MsmhToolsClass/MsmhToolsClass.csproj
+++ b/MsmhToolsClass/MsmhToolsClass.csproj
@@ -10,11 +10,193 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
diff --git a/MsmhToolsClass/MsmhToolsClass/ByteArrayTool.cs b/MsmhToolsClass/MsmhToolsClass/ByteArrayTool.cs
index 82a6c57..389d4c6 100644
--- a/MsmhToolsClass/MsmhToolsClass/ByteArrayTool.cs
+++ b/MsmhToolsClass/MsmhToolsClass/ByteArrayTool.cs
@@ -1,47 +1,264 @@
-using System;
+using System.Diagnostics;
namespace MsmhToolsClass;
-public class ByteArrayTool
+public static class ByteArrayTool
{
- public static int Search(byte[] src, byte[] pattern)
+ public static byte[] Append(this byte[] orig, byte[] append)
{
- int maxFirstCharSlot = src.Length - pattern.Length + 1;
- for (int i = 0; i < maxFirstCharSlot; i++)
+ if (append == null) return orig;
+ if (orig == null) return append;
+
+ byte[] bytes = new byte[orig.Length + append.Length];
+ Buffer.BlockCopy(orig, 0, bytes, 0, orig.Length);
+ Buffer.BlockCopy(append, 0, bytes, orig.Length, append.Length);
+ return bytes;
+ }
+
+ public static bool CanFitInBits(int number, int numberOfBits)
+ {
+ int maxValue = (1 << numberOfBits) - 1;
+ return number >= 0 && number <= maxValue;
+ }
+
+ public static byte[] GenerateRandom(int length)
+ {
+ byte[] bytes = new byte[length];
+ try
{
- if (src[i] != pattern[0]) // compare only first byte
- continue;
+ Random random = new(length);
+ random.NextBytes(bytes);
+ // OR: LibSodium.randombytes_buf(bytes, length);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("GenerateRandom2: " + ex.Message);
+ }
+ return bytes;
+ }
- // found a match on first byte, now try to match rest of the pattern
- for (int j = pattern.Length - 1; j >= 1; j--)
+ public static bool TryConvertBytesToUInt16(byte[] bytes, out ushort result)
+ {
+ try
+ {
+ int n = (bytes[0] << 8) + bytes[1];
+ if (n < 0)
{
- if (src[i + j] != pattern[j]) break;
- if (j == 1) return i;
+ result = 0;
+ return false;
}
+ result = Convert.ToUInt16(n);
+ // OR
+ //if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
+ //result = BitConverter.ToUInt16(bytes, 0);
+ return true;
+ }
+ catch (Exception)
+ {
+ result = 0;
+ return false;
}
- return -1;
}
- ///
- /// Fully read a stream into a byte array.
- ///
- /// The input stream.
- /// A byte array containing the data read from the stream.
- public static byte[] StreamToBytes(Stream input)
+ public static bool TryConvertBytesToUInt32(byte[] bytes, out uint result)
{
- if (input == null) throw new ArgumentNullException(nameof(input));
- if (!input.CanRead) throw new InvalidOperationException("Input stream is not readable");
+ try
+ {
+ int n = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
+ if (n < 0)
+ {
+ result = 0;
+ return false;
+ }
+ result = Convert.ToUInt32(n);
+ return true;
+ }
+ catch (Exception)
+ {
+ result = 0;
+ return false;
+ }
+ }
+
+ public static bool TryConvertUInt16ToBytes(ushort value, out byte[] result)
+ {
+ try
+ {
+ byte[] bytes = new byte[2];
+ bytes[0] = (byte)(value >> 8);
+ bytes[1] = (byte)value;
+ result = bytes;
+ // OR
+ //byte[] result0 = BitConverter.GetBytes(value);
+ //result = BitConverter.IsLittleEndian ? result0.Reverse().ToArray() : result0;
+ return true;
+ }
+ catch (Exception)
+ {
+ result = Array.Empty();
+ return false;
+ }
+ }
+
+ public static bool TryConvertUInt32ToBytes(uint value, out byte[] result)
+ {
+ try
+ {
+ byte[] bytes = new byte[4];
+ bytes[0] = (byte)(value >> 24);
+ bytes[1] = (byte)(value >> 16);
+ bytes[2] = (byte)(value >> 8);
+ bytes[3] = (byte)value;
+ result = bytes;
+ return true;
+ }
+ catch (Exception)
+ {
+ result = Array.Empty();
+ return false;
+ }
+ }
+
+ public static bool TryConvertToBinary(byte[] buffer, out string result)
+ {
+ try
+ {
+ result = string.Empty;
+ foreach (byte b in buffer) result += Convert.ToString(b, 2).PadLeft(8, '0');
+ return true;
+ }
+ catch (Exception)
+ {
+ result = string.Empty;
+ return false;
+ }
+ }
- byte[] buffer = new byte[16 * 1024];
+ public static bool TryConvertToBinary(byte oneByte, out string result)
+ {
+ try
+ {
+ result = Convert.ToString(oneByte, 2).PadLeft(8, '0');
+ return true;
+ }
+ catch (Exception)
+ {
+ result = string.Empty;
+ return false;
+ }
+ }
+
+ public static bool TryConvertToBinary(ushort value, out string result)
+ {
+ try
+ {
+ int binaryBase = 2;
+ result = Convert.ToString(value, binaryBase);
+ return true;
+ }
+ catch (Exception)
+ {
+ result = string.Empty;
+ return false;
+ }
+ }
+
+ public static bool TrySplitBinary(string bits, out bool[] result)
+ {
+ try
+ {
+ result = Array.ConvertAll(bits.ToCharArray(), x => x == '1');
+ //result = bits.Select(x => x == '1').ToArray(); // Using LINQ
+ return true;
+ }
+ catch (Exception)
+ {
+ result = Array.Empty();
+ return false;
+ }
+ }
+
+ public static bool TrySplitBinary(int bits, out bool[] result)
+ {
+ return TrySplitBinary(bits.ToString(), out result);
+ }
+
+ public static bool TryConvertSplittedBinaryToBytes(bool[] bits, out byte[] result)
+ {
+ try
+ {
+ if (bits.Length % 8 != 0)
+ {
+ result = Array.Empty();
+ return false;
+ }
+
+ // No need to worry about endianness
+ byte[] bytes = new byte[bits.Length / 8];
+ for (int i = 0; i < bits.Length; i += 8)
+ {
+ int value = 0;
+ for (int j = 0; j < 8; j++)
+ {
+ if (bits[i + j]) value += 1 << (7 - j);
+ }
+ bytes[i / 8] = (byte)value;
+ }
+
+ // OR / No need to worry about endianness
+ //int nBytes = bits.Length / 8;
+ //var bytesAsBools = Enumerable.Range(0, nBytes).Select(i => bits.Skip(8 * i).Take(8));
+ //byte[] bytes = bytesAsBools.Select(b => Convert.ToByte(string.Join("", b.Select(x => x ? "1" : "0")), 2)).ToArray();
+
+ // OR (Don't Use This One)
+ //byte[] bytes = new byte[bits.Length / 8]; // 1 Byte = 8 Bit
+ //BitArray bitArray = new(bits);
+ //bitArray.CopyTo(bytes, 0);
+
+ result = bytes;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("TryConvertSplittedBinaryToBytes: " + ex.Message);
+ result = Array.Empty();
+ return false;
+ }
+ }
+
+ public static async Task StreamToBytes(Stream stream)
+ {
+ if (!stream.CanRead) return Array.Empty();
using MemoryStream ms = new();
- int read;
- while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
+ try
+ {
+ await stream.CopyToAsync(ms);
+ }
+ catch (Exception ex)
{
- ms.Write(buffer, 0, read);
+ Debug.WriteLine("ByteArrayTool StreamToBytes: " + ex.Message);
}
return ms.ToArray();
}
+ public static int Search(byte[] src, byte[] pattern)
+ {
+ int maxFirstCharSlot = src.Length - pattern.Length + 1;
+ for (int i = 0; i < maxFirstCharSlot; i++)
+ {
+ if (src[i] != pattern[0]) // compare only first byte
+ continue;
+
+ // found a match on first byte, now try to match rest of the pattern
+ for (int j = pattern.Length - 1; j >= 1; j--)
+ {
+ if (src[i + j] != pattern[j]) break;
+ if (j == 1) return i;
+ }
+ }
+ return -1;
+ }
+
}
diff --git a/MsmhToolsClass/MsmhToolsClass/CertificateTool.cs b/MsmhToolsClass/MsmhToolsClass/CertificateTool.cs
index 1b5fd59..0b43ecd 100644
--- a/MsmhToolsClass/MsmhToolsClass/CertificateTool.cs
+++ b/MsmhToolsClass/MsmhToolsClass/CertificateTool.cs
@@ -1,5 +1,6 @@
using System.Diagnostics;
using System.Net;
+using System.Net.Sockets;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
@@ -127,39 +128,52 @@ private static SubjectAlternativeNameBuilder GetSanBuilderForGateway(IPAddress g
{
// Create SubjectAlternativeNameBuilder
SubjectAlternativeNameBuilder sanBuilder = new();
- sanBuilder.AddDnsName("localhost"); // Add Localhost
- sanBuilder.AddDnsName(Environment.UserName); // Add Current User
- if (OperatingSystem.IsWindows())
- sanBuilder.AddUserPrincipalName(System.Security.Principal.WindowsIdentity.GetCurrent().Name); // Add User Principal Name
- sanBuilder.AddIpAddress(IPAddress.Loopback);
- sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
- sanBuilder.AddIpAddress(IPAddress.Any);
- sanBuilder.AddIpAddress(IPAddress.IPv6Any);
-
- // Generate IP range for gateway
- if (NetworkTool.IsIPv4(gateway))
+
+ try
{
- string ipString = gateway.ToString();
- string[] ipSplit = ipString.Split('.');
- string ip1 = ipSplit[0] + "." + ipSplit[1] + "." + ipSplit[2] + ".";
- for (int n = 0; n <= 255; n++)
- {
- string ip2 = ip1 + n.ToString();
- sanBuilder.AddIpAddress(IPAddress.Parse(ip2));
- sanBuilder.AddUri(new Uri($"https://{ip2}"));
- }
- // Generate local IP range in case a VPN is active.
- if (!ip1.Equals("192.168.1."))
+ sanBuilder.AddDnsName("localhost"); // Add Localhost
+ sanBuilder.AddDnsName(Environment.UserName); // Add Current User
+ if (OperatingSystem.IsWindows())
+ sanBuilder.AddUserPrincipalName(System.Security.Principal.WindowsIdentity.GetCurrent().Name); // Add User Principal Name
+ sanBuilder.AddIpAddress(IPAddress.Loopback);
+ sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
+ sanBuilder.AddIpAddress(IPAddress.Any);
+ sanBuilder.AddIpAddress(IPAddress.IPv6Any);
+
+ // Add All Machine IPv4 And IPv6 Configuration To SAN Extension
+ foreach (var ipAddress in Dns.GetHostAddresses(Dns.GetHostName()))
+ if (ipAddress.AddressFamily == AddressFamily.InterNetwork || ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
+ sanBuilder.AddIpAddress(ipAddress);
+
+ // Generate IP range for gateway
+ if (NetworkTool.IsIPv4(gateway))
{
- string ipLocal1 = "192.168.1.";
+ string ipString = gateway.ToString();
+ string[] ipSplit = ipString.Split('.');
+ string ip1 = ipSplit[0] + "." + ipSplit[1] + "." + ipSplit[2] + ".";
for (int n = 0; n <= 255; n++)
{
- string ipLocal2 = ipLocal1 + n.ToString();
- sanBuilder.AddIpAddress(IPAddress.Parse(ipLocal2));
- sanBuilder.AddUri(new Uri($"https://{ipLocal2}"));
+ string ip2 = ip1 + n.ToString();
+ sanBuilder.AddIpAddress(IPAddress.Parse(ip2));
+ sanBuilder.AddUri(new Uri($"https://{ip2}"));
+ }
+ // Generate local IP range in case a VPN is active.
+ if (!ip1.Equals("192.168.1."))
+ {
+ string ipLocal1 = "192.168.1.";
+ for (int n = 0; n <= 255; n++)
+ {
+ string ipLocal2 = ipLocal1 + n.ToString();
+ sanBuilder.AddIpAddress(IPAddress.Parse(ipLocal2));
+ sanBuilder.AddUri(new Uri($"https://{ipLocal2}"));
+ }
}
}
}
+ catch (Exception ex)
+ {
+ Debug.WriteLine("CertificateTool GetSanBuilderForGateway: " + ex.Message);
+ }
return sanBuilder;
}
@@ -297,10 +311,15 @@ public static async Task GenerateCertificateAsync(string folderPath, IPAddress g
SubjectAlternativeNameBuilder sanBuilder = GetSanBuilderForGateway(gateway);
// Create Issuer Certificate
- using X509Certificate2 issuerCert = GenerateRootCertificate(sanBuilder, issuerSubjectName, out RSA issuerRsaKey);
+ X509Certificate2 issuerCert = GenerateRootCertificate(sanBuilder, issuerSubjectName, out RSA issuerRsaKey);
+ //if (!issuerCert.HasPrivateKey) issuerCert = issuerCert.CopyWithPrivateKey(issuerRsaKey);
+ //string pass = Guid.NewGuid().ToString();
+ //issuerCert = new(issuerCert.Export(X509ContentType.Pfx, pass), pass);
// Create Certificate
- using X509Certificate2 cert = GenerateCertificateByIssuer(issuerCert, sanBuilder, subjectName, out RSA rsaKey);
+ X509Certificate2 cert = GenerateCertificateByIssuer(issuerCert, sanBuilder, subjectName, out RSA rsaKey);
+ //if (!cert.HasPrivateKey) cert = cert.CopyWithPrivateKey(rsaKey);
+ //cert = new(cert.Export(X509ContentType.Pfx, pass), pass);
//== Export to Files
// Export Issuer Private Key
@@ -310,6 +329,7 @@ public static async Task GenerateCertificateAsync(string folderPath, IPAddress g
// Export Issuer Certificate
await issuerCert.SaveToFileAsCrt(issuerFilePathNoExt);
+ issuerCert.Dispose();
// Export Private Key
string filePathNoExt = Path.GetFullPath(Path.Combine(folderPath, "localhost"));
@@ -318,16 +338,24 @@ public static async Task GenerateCertificateAsync(string folderPath, IPAddress g
// Export Certificate
await cert.SaveToFileAsCrt(filePathNoExt);
+ cert.Dispose();
}
public static async void CreateP12(string certPath, string keyPath, string password = "")
{
- string? folderPath = Path.GetDirectoryName(certPath);
- string fileName = Path.GetFileNameWithoutExtension(certPath);
- using X509Certificate2 certWithKey = X509Certificate2.CreateFromPemFile(certPath, keyPath);
- byte[] certWithKeyExport = certWithKey.Export(X509ContentType.Pfx, password);
- if (!string.IsNullOrEmpty(folderPath))
- await File.WriteAllBytesAsync(Path.Combine(folderPath, fileName + ".p12"), certWithKeyExport);
+ try
+ {
+ string? folderPath = Path.GetDirectoryName(certPath);
+ string fileName = Path.GetFileNameWithoutExtension(certPath);
+ using X509Certificate2 certWithKey = X509Certificate2.CreateFromPemFile(certPath, keyPath);
+ byte[] certWithKeyExport = certWithKey.Export(X509ContentType.Pfx, password);
+ if (!string.IsNullOrEmpty(folderPath))
+ await File.WriteAllBytesAsync(Path.Combine(folderPath, fileName + ".p12"), certWithKeyExport);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("CreateP12: " + ex.Message);
+ }
}
///
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsReader.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsReader.cs
deleted file mode 100644
index 4f2c1d1..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsReader.cs
+++ /dev/null
@@ -1,208 +0,0 @@
-using System.Net;
-
-namespace MsmhToolsClass.DnsTool;
-
-public class DnsReader
-{
- public string? IP { get; private set; }
- public int Port { get; private set; }
- public string Dns { get; private set; }
- public string Host { get; private set; } = string.Empty;
- public string Path { get; private set; } = string.Empty;
- public string CompanyName { get; private set; } = string.Empty;
- private string CompanyNameDataFileContent { get; set; } = string.Empty;
- public DnsProtocol Protocol { get; private set; }
- public string ProtocolName { get; private set; }
- public bool IsDnsCryptStamp { get; private set; } = false;
- public DnsCryptStamp StampProperties = new();
-
- public class DnsCryptStamp
- {
- public bool IsDnsSec { get; set; } = false;
- public bool IsNoLog { get; set; } = false;
- public bool IsNoFilter { get; set; } = false;
- }
-
- ///
- /// Read any DNS
- ///
- /// DNS Address
- /// File content to get company name. each line e.g. 8.8.8.8|Google Inc.
- public DnsReader(string dns, string? companyNameDataFileContent)
- {
- Dns = dns;
-
- if (!string.IsNullOrEmpty(companyNameDataFileContent))
- CompanyNameDataFileContent = companyNameDataFileContent;
-
- Protocol = DnsProtocol.Unknown;
- ProtocolName = DnsProtocolName.Unknown;
-
- if (dns.ToLower().StartsWith("sdns://"))
- {
- IsDnsCryptStamp = true;
-
- // Decode Stamp
- DNSCryptStampReader stamp = new(dns);
- if (stamp != null && stamp.IsDecryptionSuccess)
- {
- IP = stamp.IP;
- Port = stamp.Port;
- Host = stamp.Host;
- Path = stamp.Path;
- Protocol = ParseProtocol(stamp.Protocol);
- ProtocolName = stamp.ProtocolName;
- StampProperties.IsDnsSec = stamp.IsDnsSec;
- StampProperties.IsNoLog = stamp.IsNoLog;
- StampProperties.IsNoFilter = stamp.IsNoFilter;
-
- // Get Company Name (SDNS)
- string stampHost = stamp.Host;
- if (string.IsNullOrEmpty(stampHost)) stampHost = stamp.IP;
- if (string.IsNullOrEmpty(stampHost)) stampHost = stamp.ProviderName;
- if (!string.IsNullOrEmpty(CompanyNameDataFileContent))
- CompanyName = GetCompanyName(stampHost, CompanyNameDataFileContent);
- }
- }
- else
- {
- if (dns.ToLower().StartsWith("https://"))
- {
- // DoH
- SetIpPortHostPath(dns, 443);
-
- Protocol = DnsProtocol.DoH;
- ProtocolName = DnsProtocolName.DoH;
- }
- else if (dns.ToLower().StartsWith("tls://"))
- {
- // TLS
- SetIpPortHostPath(dns, 853);
-
- Protocol = DnsProtocol.DoT;
- ProtocolName = DnsProtocolName.DoT;
- }
- else if (dns.ToLower().StartsWith("quic://"))
- {
- // DoQ
- SetIpPortHostPath(dns, 853);
-
- Protocol = DnsProtocol.DoQ;
- ProtocolName = DnsProtocolName.DoQ;
- }
- else if (dns.ToLower().StartsWith("tcp://") || dns.ToLower().StartsWith("udp://"))
- {
- // Plain DNS
- SetIpPortHostPath(dns, 53);
-
- Protocol = DnsProtocol.PlainDNS;
- ProtocolName = DnsProtocolName.PlainDNS;
- }
- else
- {
- // Plain DNS
- SetIpPortHost(dns, 53);
-
- Protocol = DnsProtocol.PlainDNS;
- ProtocolName = DnsProtocolName.PlainDNS;
- }
- }
- }
-
- private void SetIpPortHostPath(string dns, int defaultPort)
- {
- NetworkTool.GetUrlDetails(dns, defaultPort, out _, out string host, out _, out _, out int port, out string path, out bool isIPv6);
- Port = port;
- Path = path;
- bool isIPv4 = NetworkTool.IsIPv4Valid(host, out IPAddress? _);
- if (isIPv6 || isIPv4)
- {
- IP = host;
- }
- else
- {
- Host = host;
- IP = GetIP.GetIpFromSystem(host, false, false);
- }
- if (!string.IsNullOrEmpty(CompanyNameDataFileContent))
- {
- string? ipOrHost = Host;
- if (string.IsNullOrEmpty(ipOrHost)) ipOrHost = IP;
- if (string.IsNullOrEmpty(ipOrHost)) ipOrHost = host;
- CompanyName = GetCompanyName(ipOrHost, CompanyNameDataFileContent);
- }
- }
-
- private void SetIpPortHost(string hostIpPort, int defaultPort)
- {
- NetworkTool.GetHostDetails(hostIpPort, defaultPort, out string host, out _, out _, out int port, out string _, out bool isIPv6);
- Port = port;
- bool isIPv4 = NetworkTool.IsIPv4Valid(host, out IPAddress? _);
- if (isIPv6 || isIPv4)
- {
- IP = host;
- }
- else
- {
- Host = host;
- IP = GetIP.GetIpFromSystem(host, false, false);
- }
- if (!string.IsNullOrEmpty(CompanyNameDataFileContent))
- {
- string? ipOrHost = Host;
- if (string.IsNullOrEmpty(ipOrHost)) ipOrHost = IP;
- if (string.IsNullOrEmpty(ipOrHost)) ipOrHost = host;
- CompanyName = GetCompanyName(ipOrHost, CompanyNameDataFileContent);
- }
- }
-
- private static string GetCompanyName(string host, string fileContent)
- {
- return DnsTool.GetCompanyName.HostToCompanyOffline(host, fileContent);
- }
-
- private static DnsProtocol ParseProtocol(DNSCryptStampReader.StampProtocol stampProtocol)
- {
- var protocol = stampProtocol switch
- {
- DNSCryptStampReader.StampProtocol.PlainDNS => DnsProtocol.PlainDNS,
- DNSCryptStampReader.StampProtocol.DnsCrypt => DnsProtocol.DnsCrypt,
- DNSCryptStampReader.StampProtocol.DoH => DnsProtocol.DoH,
- DNSCryptStampReader.StampProtocol.DoT => DnsProtocol.DoT,
- DNSCryptStampReader.StampProtocol.DoQ => DnsProtocol.DoQ,
- DNSCryptStampReader.StampProtocol.ObliviousDohTarget => DnsProtocol.ObliviousDohTarget,
- DNSCryptStampReader.StampProtocol.AnonymizedDNSCryptRelay => DnsProtocol.AnonymizedDNSCryptRelay,
- DNSCryptStampReader.StampProtocol.ObliviousDohRelay => DnsProtocol.ObliviousDohRelay,
- DNSCryptStampReader.StampProtocol.Unknown => DnsProtocol.Unknown,
- _ => DnsProtocol.Unknown,
- };
- return protocol;
- }
-
- public enum DnsProtocol
- {
- PlainDNS,
- DnsCrypt,
- DoH,
- DoT,
- DoQ,
- ObliviousDohTarget,
- AnonymizedDNSCryptRelay,
- ObliviousDohRelay,
- Unknown
- }
-
- private struct DnsProtocolName
- {
- public static string PlainDNS = "Plain DNS";
- public static string DnsCrypt = "DNSCrypt";
- public static string DoH = "DNS-Over-HTTPS";
- public static string DoT = "DNS-Over-TLS";
- public static string DoQ = "DNS-Over-Quic";
- public static string ObliviousDohTarget = "Oblivious DoH Target";
- public static string AnonymizedDNSCryptRelay = "Anonymized DNSCrypt Relay";
- public static string ObliviousDohRelay = "Oblivious DoH Relay";
- public static string Unknown = "Unknown";
- }
-
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsByteExtensions.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsByteExtensions.cs
deleted file mode 100644
index a4d758b..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsByteExtensions.cs
+++ /dev/null
@@ -1,217 +0,0 @@
-using System;
-using System.Text;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-internal static class DnsByteExtensions
-{
- public static short ReadInt16(byte a, byte b)
- {
- return (short)(a << 0 | b << 8);
- }
-
- public static ushort ReadUInt16(byte a, byte b)
- {
- return (ushort)(a << 0 | b << 8);
- }
-
- public static short ReadInt16(ReadOnlyMemory bytes, ref int offset)
- {
- var value = ReadInt16(bytes.Span[offset + 1], bytes.Span[offset + 0]);
- offset += sizeof(short);
- return value;
- }
-
- public static ushort ReadUInt16(ReadOnlyMemory bytes, ref int offset)
- {
- var value = ReadUInt16(bytes.Span[offset + 1], bytes.Span[offset + 0]);
- offset += sizeof(ushort);
- return value;
- }
-
- public static int ReadInt32(ReadOnlyMemory bytes, ref int offset)
- {
- var value = bytes.Span[offset + 3] << 0 |
- bytes.Span[offset + 2] << 8 |
- bytes.Span[offset + 1] << 16 |
- bytes.Span[offset + 0] << 24;
- offset += sizeof(int);
- return value;
- }
-
- public static uint ReadUInt32(ReadOnlyMemory bytes, ref int offset)
- {
- var value = bytes.Span[offset + 3] << 0 |
- bytes.Span[offset + 2] << 8 |
- bytes.Span[offset + 1] << 16 |
- bytes.Span[offset + 0] << 24;
- offset += sizeof(uint);
- return (uint)value;
- }
-
- public static string ToDebugString(ReadOnlyMemory bytes)
- {
- if (bytes.IsEmpty)
- {
- return "";
- }
-
- return $"new byte [] {{{string.Join(",", bytes.ToArray())}}}";
- }
-
- public static ReadOnlyMemory ReadBytes(ReadOnlyMemory bytes, int length, ref int offset)
- {
- var data = bytes.Slice(offset, length);
- offset += length;
- return data;
- }
-
- public static string ReadStringSimple(ReadOnlyMemory bytes, ref int offset)
- {
- byte labelLength = bytes.Span[offset];
- offset += 1;
-#if NETSTANDARD2_0
- var str = Encoding.ASCII.GetString(bytes.Slice(offset, labelLength).ToArray());
-#else
- var str = Encoding.ASCII.GetString(bytes.Slice(offset, labelLength).Span);
-#endif
- offset += labelLength;
- return str;
- }
-
- public static string[] ReadString(ReadOnlyMemory bytes, ref int offset, bool compressionPermitted = true)
- {
- // Assume most labels consist of 3 parts
- var parts = new List(3);
-
- int? preCompressionOffset = null;
- while (offset < bytes.Length)
- {
- if (bytes.Span[offset] == 0)
- {
- offset++;
- break;
- }
-
- // Pointers are always 192 or more, because the first two bits are 1s
- if (bytes.Span[offset] >= 192 && compressionPermitted)
- {
- if (!preCompressionOffset.HasValue)
- {
- preCompressionOffset = offset + 2;
- }
-
- // Read the 14 bit pointer as our new offset
- offset = ReadUInt16(bytes.Span[offset + 1], (byte)(bytes.Span[offset] & (1 << 6) - 1));
- }
-
- parts.Add(ReadStringSimple(bytes, ref offset));
- }
-
- if (preCompressionOffset.HasValue)
- {
- offset = preCompressionOffset.Value;
- }
-
- return parts.ToArray();
- }
-
- public static ReadOnlyMemory AllocateAndWrite(IDnsByteArrayWriter writer)
- {
- var buffer = AllocatePinnedNetworkBuffer();
- var offset = 0;
- writer.WriteBytes(buffer, ref offset);
-#if NETSTANDARD2_0
- return buffer.AsMemory().Slice(0, offset);
-#else
- return buffer.Slice(0, offset);
-#endif
- }
-
-#if NETSTANDARD2_0
- public static ArraySegment Slice(this ArraySegment buffer, int start, int length)
- {
- return new ArraySegment(buffer.Array, start, length);
- }
-
- public static ArraySegment Slice(this ArraySegment buffer, int start)
- {
- return Slice(buffer, start, buffer.Count - start);
- }
-#endif
-
- private const int NetworkBufferSize = 65527;
-
-#if NETSTANDARD2_0 || NETSTANDARD2_1
- public static ArraySegment AllocatePinnedNetworkBuffer() => new ArraySegment(new byte[NetworkBufferSize]);
-#else
- // Allocate a buffer which will be used for the incoming query, and re-used to send the answer.
- // Also make it pinned, see https://enclave.io/high-performance-udp-sockets-net6/
- public static Memory AllocatePinnedNetworkBuffer() => GC.AllocateArray(NetworkBufferSize, true);
-#endif
-
- public static TReader FromBytes(ReadOnlyMemory bytes) where TReader : IDnsByteArrayReader, new()
- {
- var offset = 0;
-
- var reader = new TReader();
- reader.ReadBytes(bytes, ref offset);
-
- if (offset != bytes.Length)
- {
- throw new InvalidOperationException($"Should have read {bytes.Length} bytes, only read {offset} bytes");
- }
- return reader;
- }
-
- public static void ToBytes(ReadOnlySpan strings, Memory buffer, ref int offset)
- {
- for (int i = 0; i < strings.Length; i++)
- {
- // First write the value 1 byte from the offset to leave room for the length byte
-#if NETSTANDARD2_0
- var stringBytes = Encoding.ASCII.GetBytes(strings[i]);
- stringBytes.CopyTo(buffer.Slice(offset + 1, strings[i].Length));
- var length = stringBytes.Length;
-#else
- var length = Encoding.ASCII.GetBytes(strings[i], buffer.Slice(offset + 1, strings[i].Length).Span);
-#endif
-
- // Then write the length before the value
- buffer.Span[offset] = (byte)length;
-
- // Finally advance the offset past the length and value
- offset += 1 + length;
- }
-
- buffer.Span[offset++] = 0;
- }
-
- public static void ToBytes(int value, Memory buffer, ref int offset)
- {
- buffer.Span[offset++] = (byte)(value >> 24);
- buffer.Span[offset++] = (byte)(value >> 16);
- buffer.Span[offset++] = (byte)(value >> 8);
- buffer.Span[offset++] = (byte)(value >> 0);
- }
-
- public static void ToBytes(uint value, Memory buffer, ref int offset)
- {
- buffer.Span[offset++] = (byte)(value >> 24);
- buffer.Span[offset++] = (byte)(value >> 16);
- buffer.Span[offset++] = (byte)(value >> 8);
- buffer.Span[offset++] = (byte)(value >> 0);
- }
-
- public static void ToBytes(short value, Memory buffer, ref int offset)
- {
- buffer.Span[offset++] = (byte)(value >> 8);
- buffer.Span[offset++] = (byte)(value >> 0);
- }
-
- public static void ToBytes(ushort value, Memory buffer, ref int offset)
- {
- buffer.Span[offset++] = (byte)(value >> 8);
- buffer.Span[offset++] = (byte)(value >> 0);
- }
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsDomainResource.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsDomainResource.cs
deleted file mode 100644
index 6075a9a..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsDomainResource.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents a DNS text resource containing a domain name.
-///
-public sealed class DnsDomainResource : DnsStringResource
-{
- ///
- protected override bool CanUseCompression => true;
-
- ///
- /// Get the value of this entry as a domain name.
- ///
- public string Domain => string.Join(".", Entries);
-
- ///
- public override string ToString() => Domain;
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsHeader.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsHeader.cs
deleted file mode 100644
index 8807563..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsHeader.cs
+++ /dev/null
@@ -1,230 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents a DNS header, the first entry in a .
-/// See for methods to create DNS headers for specific purposes.
-///
-public sealed class DnsHeader : IEquatable, IDnsByteArrayReader, IDnsByteArrayWriter
-{
- ///
- /// Gets or sets the unique ID of this DNS query.
- ///
- ///
- /// A unique ID which allows the sender/receiver to
- /// identify this DNS message (for example if the transport protocol is stateless).
- ///
- public ushort Id { get; set; }
-
- internal ushort Flags { get; set; }
-
- ///
- /// The number of questions in this header.
- ///
- ///
- /// A count of the number of questions asked of the DNS server.
- ///
- public short QuestionCount { get; set; }
-
- ///
- /// The number of records in this header.
- ///
- ///
- /// A count of the number of answers contained within this DNS message.
- ///
- public short AnswerRecordCount { get; set; }
-
- ///
- /// The number of name server records in this header.
- ///
- ///
- /// A count of the number of DNS name server records in this header.
- ///
- public short NameServerRecordCount { get; set; }
-
- ///
- /// The number of additional records in this header.
- ///
- ///
- /// A count of the number of additional records in this header.
- ///
- public short AdditionalRecordCount { get; set; }
-
- internal string[] Labels { get; set; } = Array.Empty();
-
- ///
- /// The of this header.
- ///
- ///
- /// If the type of query is , it means the query is for IP address records.
- ///
- public DnsQueryType QueryType { get; set; }
-
- ///
- /// The of this header, normally (Internet).
- ///
- ///
- /// The class of query, most likely to represent Internet DNS queries.
- ///
- public DnsQueryClass QueryClass { get; set; }
-
- ///
- /// Determines whether this is a DNS query or answer.
- ///
- ///
- /// For DNS queries this is false, for DNS responses this is true.
- ///
- public bool IsQueryResponse
- {
- get => (Flags & 0x8000) == 0x8000;
- set => Flags = value ? (ushort)(Flags | 0x8000) : (ushort)(Flags & (~0x8000));
- }
-
- ///
- /// Determines the operation code for this DNS header.
- ///
- ///
- /// The DNS operation code for this DNS answer. For example .
- ///
- public DnsOperationCode OperationCode
- {
- get => (DnsOperationCode)((Flags & 0x7800) >> 11);
- set => Flags = (ushort)((Flags & ~0x7800) | ((int)value << 11));
- }
-
- ///
- /// Determines whether the answer was from an authoritative DNS server.
- ///
- ///
- /// If true, this DNS answer is authoritative.
- ///
- public bool AuthoritativeAnswer
- {
- get => (Flags & 0x0400) == 0x0400;
- set => Flags = value ? (ushort)(Flags | 0x0400) : (ushort)(Flags & (~0x0400));
- }
-
- ///
- /// Determines whether this DNS answer was truncated due to size.
- ///
- public bool Truncation
- {
- get => (Flags & 0x0200) == 0x0200;
- set => Flags = value ? (ushort)(Flags | 0x0200) : (ushort)(Flags & (~0x0200));
- }
-
- ///
- /// Indicates whether recursion is desired on this DNS query.
- /// If this is an answer, indicates whether the original query requested recursion.
- ///
- public bool RecursionDesired
- {
- get => (Flags & 0x0100) == 0x0100;
- set => Flags = value ? (ushort)(Flags | 0x0100) : (ushort)(Flags & (~0x0100));
- }
-
- ///
- /// Indicates whether recursion is available.
- ///
- public bool RecursionAvailable
- {
- get => (Flags & 0x0080) == 0x0080;
- set => Flags = value ? (ushort)(Flags | 0x0080) : (ushort)(Flags & (~0x0080));
- }
-
- ///
- /// The for this result.
- ///
- public DnsResponseCode ResponseCode
- {
- get => (DnsResponseCode)(Flags & 0x000F);
- set => Flags = (ushort)((Flags & ~0x000F) | (byte)value);
- }
-
- ///
- /// The DNS host to use, for example "example.com".
- ///
- public string Host
- {
- get => string.Join(".", Labels);
- set => Labels = value.Split('.');
- }
-
- ///
- public override string ToString() => $"{(IsQueryResponse ? "RES" : "QRY")}: {Id} Domain: {Host} Type: {QueryType} Class: {QueryClass}";
-
- ///
- public bool Equals(DnsHeader? other)
- {
- return Id == other?.Id &&
- Flags == other.Flags &&
- QuestionCount == other.QuestionCount &&
- AnswerRecordCount == other.AnswerRecordCount &&
- NameServerRecordCount == other.NameServerRecordCount &&
- AdditionalRecordCount == other.AdditionalRecordCount &&
- Host == other.Host &&
- QueryType == other.QueryType &&
- QueryClass == other.QueryClass;
- }
-
- ///
- public override int GetHashCode()
- {
- HashCode hash = new();
- hash.Add(Id);
- hash.Add(Flags);
- hash.Add(QuestionCount);
- hash.Add(AnswerRecordCount);
- hash.Add(NameServerRecordCount);
- hash.Add(AdditionalRecordCount);
- hash.Add(QueryType);
- hash.Add(QueryClass);
- hash.Add(Host);
- return hash.ToHashCode();
- }
-
- ///
- public void ReadBytes(ReadOnlyMemory bytes, ref int offset)
- {
- Id = DnsByteExtensions.ReadUInt16(bytes, ref offset);
- Flags = DnsByteExtensions.ReadUInt16(bytes, ref offset);
- QuestionCount = DnsByteExtensions.ReadInt16(bytes, ref offset);
- AnswerRecordCount = DnsByteExtensions.ReadInt16(bytes, ref offset);
- NameServerRecordCount = DnsByteExtensions.ReadInt16(bytes, ref offset);
- AdditionalRecordCount = DnsByteExtensions.ReadInt16(bytes, ref offset);
- Labels = DnsByteExtensions.ReadString(bytes, ref offset);
- QueryType = (DnsQueryType)DnsByteExtensions.ReadUInt16(bytes, ref offset);
- QueryClass = (DnsQueryClass)DnsByteExtensions.ReadUInt16(bytes, ref offset);
-
- if (!IsQueryResponse && (AnswerRecordCount > 0 || NameServerRecordCount > 0))
- {
- throw new InvalidOperationException($"Header states that this message is a query, yet there are answer and nameserver records.");
- }
- }
-
- ///
- public void WriteBytes(Memory bytes, ref int offset)
- {
- DnsByteExtensions.ToBytes(Id, bytes, ref offset);
- DnsByteExtensions.ToBytes(Flags, bytes, ref offset);
- DnsByteExtensions.ToBytes(QuestionCount, bytes, ref offset);
- DnsByteExtensions.ToBytes(AnswerRecordCount, bytes, ref offset);
- DnsByteExtensions.ToBytes(NameServerRecordCount, bytes, ref offset);
- DnsByteExtensions.ToBytes(AdditionalRecordCount, bytes, ref offset);
- DnsByteExtensions.ToBytes(Labels, bytes, ref offset);
- DnsByteExtensions.ToBytes((ushort)QueryType, bytes, ref offset);
- DnsByteExtensions.ToBytes((ushort)QueryClass, bytes, ref offset);
- }
-
- ///
- /// A generic bag of tags associated with this object.
- /// Will not be serialised and/or passed over the wire.
- ///
- public IDictionary Tags { get; } = new Dictionary();
-
- public override bool Equals(object? obj)
- {
- return Equals(obj as DnsHeader);
- }
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsIpAddressResource.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsIpAddressResource.cs
deleted file mode 100644
index 8677374..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsIpAddressResource.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System;
-using System.Net;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents a DNS resource record which contains an Internet Protocol address.
-/// See and .
-///
-public sealed class DnsIpAddressResource : IDnsResource, IEquatable
-{
- ///
- /// The Internet Protocol address.
- ///
- ///
- /// May be an IPv4 or IPv6 address, depending on the record type.
- ///
- public IPAddress IPAddress { get; set; } = IPAddress.Loopback;
-
- ///
- public bool Equals(DnsIpAddressResource? other) => IPAddress.Equals(other?.IPAddress);
-
- ///
- public override bool Equals(object? obj) => obj is DnsIpAddressResource record ? Equals(record) : base.Equals(obj);
-
- ///
- public override int GetHashCode() => HashCode.Combine(IPAddress);
-
- ///
- public void ReadBytes(ReadOnlyMemory bytes, ref int offset, int length)
- {
- var raw = DnsByteExtensions.ReadBytes(bytes, length, ref offset);
- IPAddress = new IPAddress(raw.ToArray());
- }
-
- ///
- public IEnumerable> WriteBytes()
- {
- yield return IPAddress.GetAddressBytes();
- }
-
- ///
- public override string ToString() => IPAddress.ToString();
-
- ///
- public void WriteBytes(Memory bytes, ref int offset)
- {
- Span address = IPAddress.GetAddressBytes();
-
- address.CopyTo(bytes[offset..].Span);
- offset += address.Length;
- }
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsMessage.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsMessage.cs
deleted file mode 100644
index 1dc82ca..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsMessage.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-using System;
-using System.Text;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents an answer to a DNS query, generated by a DNS server.
-///
-public sealed class DnsMessage : IEquatable, IDnsByteArrayReader, IDnsByteArrayWriter
-{
- ///
- /// The section of this answer.
- ///
- /// Gets or sets the , which describes the original DNS query.
- public DnsHeader Header { get; set; } = new DnsHeader();
-
- ///
- /// The list of DNS resources returned by the server.
- ///
- /// Gets or sets the list representing values returned by the DNS server.
- public IList Answers { get; set; } = Array.Empty();
-
- ///
- /// The list of name server DNS resources returned by the server.
- ///
- /// Gets or sets the list representing values returned by the DNS server.
- public IList Nameservers { get; set; } = Array.Empty();
-
- ///
- /// The list of additional DNS resources returned by the server.
- ///
- /// Gets or sets the list representing values returned by the DNS server.
- public IList Additional { get; set; } = Array.Empty();
-
- ///
- public bool Equals(DnsMessage? other) => Header.Equals(other?.Header) &&
- Answers.SequenceEqual(other.Answers) &&
- Nameservers.SequenceEqual(other.Nameservers) &&
- Additional.SequenceEqual(other.Additional);
-
- ///
- public override bool Equals(object? obj) => obj is DnsMessage record ? Equals(record) : base.Equals(obj);
-
- ///
- public override int GetHashCode() => HashCode.Combine(Header, Answers, Nameservers, Additional);
-
- private IList ReadRecords(int count, ReadOnlyMemory bytes, ref int offset)
- {
- var records = new DnsResourceRecord[count];
- for (var i = 0; i < count; i++)
- {
- var record = new DnsResourceRecord();
- record.ReadBytes(bytes, ref offset);
- records[i] = record;
- }
-
- return records;
- }
-
- private void EnsureCorrectCounts()
- {
- if (Header.AnswerRecordCount != Answers.Count)
- {
- throw new InvalidOperationException($"Header states there are {Header.AnswerRecordCount} answer records, there are {Answers.Count}");
- }
-
- if (Header.AdditionalRecordCount != Additional.Count)
- {
- throw new InvalidOperationException($"Header states there are {Header.AdditionalRecordCount} additional records, there are {Additional.Count}");
- }
-
- if (Header.NameServerRecordCount != Nameservers.Count)
- {
- throw new InvalidOperationException($"Header states there are {Header.NameServerRecordCount} nameserver records, there are {Nameservers.Count}");
- }
- }
-
- ///
- public void ReadBytes(ReadOnlyMemory bytes, ref int offset)
- {
- EnsureCorrectCounts();
- Header.ReadBytes(bytes, ref offset);
- Answers = ReadRecords(Header.AnswerRecordCount, bytes, ref offset);
- Nameservers = ReadRecords(Header.NameServerRecordCount, bytes, ref offset);
- Additional = ReadRecords(Header.AdditionalRecordCount, bytes, ref offset);
- }
-
- ///
- public override string ToString()
- {
- var sb = new StringBuilder();
- sb.Append($"{Header} Response: {Header.ResponseCode}");
-
- if (Answers.Count > 0)
- {
- sb.Append($"{Environment.NewLine}Answers: {Answers.Count}{string.Concat(Answers.Select(x => $"{Environment.NewLine} * {x}"))}");
- }
-
- if (Additional.Count > 0)
- {
- sb.Append($"{Environment.NewLine}Additional: {Additional.Count}{string.Concat(Additional.Select(x => $"{Environment.NewLine} * {x}"))}");
- }
-
- return sb.ToString();
- }
-
- ///
- public void WriteBytes(Memory bytes, ref int offset)
- {
- EnsureCorrectCounts();
- Header.WriteBytes(bytes, ref offset);
-
- foreach (var answer in Answers)
- {
- answer.WriteBytes(bytes, ref offset);
- }
-
- foreach (var nameserver in Nameservers)
- {
- nameserver.WriteBytes(bytes, ref offset);
- }
-
- foreach (var additional in Additional)
- {
- additional.WriteBytes(bytes, ref offset);
- }
- }
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsMxResource.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsMxResource.cs
deleted file mode 100644
index 63da6f6..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsMxResource.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents a mail server DNS record. See .
-///
-public sealed class DnsMxResource : DnsStringResource, IEquatable
-{
- ///
- /// The priority or preference field identifies which mailserver should be preferred.
- ///
- ///
- /// The priority field identifies which mailserver should be preferred - if multiple
- /// values are the same, mail would be expected to flow evenly to all hosts.
- ///
- public ushort Preference { get; set; }
-
- ///
- /// The domain name of a mailserver.
- ///
- ///
- /// The host name must map directly to one or more address records (A, or AAAA) in the DNS, and must not point to any CNAME records.
- ///
- public string Exchange => string.Join(".", Entries);
-
- ///
- protected override bool CanUseCompression => true;
-
- ///
- public bool Equals(DnsMxResource? other) => Preference == other?.Preference && Exchange == other.Exchange;
-
- ///
- public override bool Equals(object? obj) => obj is DnsMxResource record ? Equals(record) : base.Equals(obj);
-
- ///
- public override int GetHashCode() => HashCode.Combine(Preference, Exchange);
-
- ///
- public override void ReadBytes(ReadOnlyMemory bytes, ref int offset, int length)
- {
- Preference = DnsByteExtensions.ReadUInt16(bytes, ref offset);
- base.ReadBytes(bytes, ref offset, length - sizeof(ushort));
- }
-
- ///
- public override string ToString() => Exchange;
-
- ///
- public override void WriteBytes(Memory bytes, ref int offset)
- {
- DnsByteExtensions.ToBytes(Preference, bytes, ref offset);
- base.WriteBytes(bytes, ref offset);
- }
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsOperationCode.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsOperationCode.cs
deleted file mode 100644
index be23e08..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsOperationCode.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// DNS operation codes. See https://www.iana.org/assignments/dns-parameters/dns-parameters.xml#dns-parameters-5
-///
-public enum DnsOperationCode : byte
-{
- ///
- /// Query
- ///
- QUERY = 0,
- ///
- /// IQuery (Inverse Query, OBSOLETE)
- ///
- IQUERY = 1,
- ///
- /// Status
- ///
- STATUS = 2,
- ///
- /// Notify
- ///
- NOTIFY = 4,
- ///
- /// Update
- ///
- UPDATE = 5,
- ///
- /// DNS Stateful Operations (DSO)
- ///
- DSO = 6,
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsOptResource.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsOptResource.cs
deleted file mode 100644
index 7cc1d53..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsOptResource.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Describes an EDNS0 psuedo-resource.
-///
-public sealed class DnsOptResource : IDnsResource, IEquatable
-{
- ///
- /// The raw bytes recieved for this DNS resource.
- ///
- ///
- /// The raw set of bytes representing the value fo this DNS resource.
- /// For string values, due to compression the whole packet may be needed.
- ///
- public ReadOnlyMemory Raw { get; set; }
-
- ///
- public bool Equals(DnsOptResource? other)
- {
- return other != null && Raw.Span.SequenceEqual(other.Raw.Span);
- }
-
- ///
- public override bool Equals(object? obj) => obj is DnsOptResource record ? Equals(record) : base.Equals(obj);
-
- ///
- public override int GetHashCode() => HashCode.Combine(Raw);
-
- ///
- public void ReadBytes(ReadOnlyMemory bytes, ref int offset, int length)
- {
- Raw = DnsByteExtensions.ReadBytes(bytes, length, ref offset);
- }
-
- ///
- public void WriteBytes(Memory bytes, ref int offset)
- {
- // Payloadsize / flags is serialised at a higher level
- Raw.CopyTo(bytes[offset..]);
- offset += Raw.Length;
- }
-
- ///
- public override string ToString() => $"Raw: {Raw.Length} bytes";
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsQueryClass.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsQueryClass.cs
deleted file mode 100644
index 07c70f7..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsQueryClass.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// DNS class. See https://www.iana.org/assignments/dns-parameters/dns-parameters.xml#dns-parameters-2
-///
-public enum DnsQueryClass : ushort
-{
- ///
- /// Internet (IN)
- ///
- IN = 1,
- ///
- /// Chaos (CH)
- ///
- CH = 3,
- ///
- /// Hesiod (HS)
- ///
- HS = 4,
- ///
- /// QCLASS NONE
- ///
- QCLASS_NONE = 254,
- ///
- /// QCLASS * (ANY)
- ///
- QCLASS_ANY = 255,
- ///
- /// Standards Action
- ///
- StandardsAction = 65535
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsQueryFactory.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsQueryFactory.cs
deleted file mode 100644
index d508831..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsQueryFactory.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using System;
-using System.Net;
-using System.Net.Sockets;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Provides extension methods around .
-///
-public static class DnsQueryFactory
-{
- ///
- /// Generate a unique ID to identify this DNS message.
- ///
- /// A random value.
- public static ushort GenerateId()
- {
- var offset = 0;
- return DnsByteExtensions.ReadUInt16(Guid.NewGuid().ToByteArray(), ref offset);
- }
-
- ///
- /// Create a DNS query using the specified host name and DNS query type.
- ///
- /// The DNS host to request in the query.
- /// The type of DNS query to request.
- /// The complete DNS query.
- public static DnsMessage CreateQuery(string host, DnsQueryType type = DnsQueryType.A)
- {
- return new DnsMessage
- {
- Header = new DnsHeader
- {
- Id = GenerateId(),
- Host = host,
- QueryType = type,
- QueryClass = DnsQueryClass.IN,
- OperationCode = DnsOperationCode.QUERY,
- QuestionCount = 1,
- RecursionDesired = true
- }
- };
- }
-
- public static DnsHeader Clone(DnsHeader header)
- {
- return new DnsHeader
- {
- Id = header.Id,
- Host = header.Host,
- QueryType = header.QueryType,
- QueryClass = header.QueryClass,
- OperationCode = header.OperationCode,
- QuestionCount = header.QuestionCount,
- RecursionDesired = header.RecursionDesired,
- AdditionalRecordCount = header.AdditionalRecordCount,
- AnswerRecordCount = header.AnswerRecordCount,
- AuthoritativeAnswer = header.AuthoritativeAnswer,
- IsQueryResponse = header.IsQueryResponse,
- NameServerRecordCount = header.NameServerRecordCount,
- RecursionAvailable = header.RecursionAvailable,
- ResponseCode = header.ResponseCode,
- Truncation = header.Truncation
- };
- }
-
- public static DnsMessage TruncateAnswer(DnsMessage query)
- {
- var header = Clone(query.Header);
-
- header.AnswerRecordCount = 0;
- header.NameServerRecordCount = 0;
- header.AdditionalRecordCount = 0;
- header.IsQueryResponse = true;
- header.OperationCode = DnsOperationCode.QUERY;
- header.Truncation = true;
-
- return new DnsMessage { Header = header };
- }
-
- ///
- /// Create a reverse DNS query which resolves an IP address to a host name.
- ///
- /// The IPv4 or IPv6 address to resolve.
- /// The correctly formatted DNS query.
- public static DnsMessage CreateReverseQuery(IPAddress ipAddress)
- {
- return CreateQuery(GetReverseLookupHostForIpAddress(ipAddress), DnsQueryType.PTR);
- }
-
- private static string GetReverseLookupHostForIpAddress(IPAddress ipAddress)
- {
- if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
- {
- return string.Join(".", string.Concat(ipAddress.GetAddressBytes().Select(x => x.ToString("x2"))).Reverse()) + ".ip6.arpa";
- }
- else if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
- {
- return string.Join(".", ipAddress.ToString().Split('.').Reverse()) + ".in-addr.arpa";
- }
- else
- {
- throw new InvalidOperationException($"Invalid address type: {ipAddress.AddressFamily}");
- }
- }
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsQueryType.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsQueryType.cs
deleted file mode 100644
index 9943641..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsQueryType.cs
+++ /dev/null
@@ -1,291 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// DNS resource record type. See https://www.iana.org/assignments/dns-parameters/dns-parameters.xml#dns-parameters-4
-///
-public enum DnsQueryType : ushort
-{
- ///
- /// A host address
- ///
- A = 1,
- ///
- /// An authoritative name server
- ///
- NS = 2,
- ///
- /// A mail destination (OBSOLETE - use MX)
- ///
- MD = 3,
- ///
- /// A mail forwarder (OBSOLETE - use MX)
- ///
- MF = 4,
- ///
- /// The canonical name for an alias
- ///
- CNAME = 5,
- ///
- /// Marks the start of a zone of authority
- ///
- SOA = 6,
- ///
- /// A mailbox domain name (EXPERIMENTAL)
- ///
- MB = 7,
- ///
- /// A mail group member (EXPERIMENTAL)
- ///
- MG = 8,
- ///
- /// A mail rename domain name (EXPERIMENTAL)
- ///
- MR = 9,
- ///
- /// A null RR (EXPERIMENTAL)
- ///
- NULL = 10,
- ///
- /// A well known service description
- ///
- WKS = 11,
- ///
- /// A domain name pointer
- ///
- PTR = 12,
- ///
- /// Host information
- ///
- HINFO = 13,
- ///
- /// Mailbox or mail list information
- ///
- MINFO = 14,
- ///
- /// Mail exchange
- ///
- MX = 15,
- ///
- /// Text strings
- ///
- TEXT = 16,
- ///
- /// For Responsible Person
- ///
- RP = 17,
- ///
- /// For AFS Data Base location
- ///
- AFSDB = 18,
- ///
- /// For X.25 PSDN address
- ///
- X25 = 19,
- ///
- /// For ISDN address
- ///
- ISDN = 20,
- ///
- /// For Route Through
- ///
- RT = 21,
- ///
- /// For NSAP address, NSAP style A record
- ///
- NSAP = 22,
- ///
- /// For domain name pointer, NSAP style
- ///
- NSAPPTR = 23,
- ///
- /// For security signature
- ///
- SIG = 24,
- ///
- /// For security key
- ///
- KEY = 25,
- ///
- /// X.400 mail mapping information
- ///
- PX = 26,
- ///
- /// Geographical Position
- ///
- GPOS = 27,
- ///
- /// IP6 Address
- ///
- AAAA = 28,
- ///
- /// Location Information
- ///
- LOC = 29,
- ///
- /// Next Domain (OBSOLETE)
- ///
- NXT = 30,
- ///
- /// Endpoint Identifier
- ///
- EID = 31,
- ///
- /// Nimrod Locator
- ///
- NIMLOC = 32,
- ///
- /// Server Selection
- ///
- SRV = 33,
- ///
- /// ATM Address
- ///
- ATMA = 34,
- ///
- /// Naming Authority Pointer
- ///
- NAPTR = 35,
- ///
- /// Key Exchanger
- ///
- KX = 36,
- CERT = 37,
- ///
- /// A6 (OBSOLETE - use AAAA)
- ///
- A6 = 38,
- DNAME = 39,
- SINK = 40,
- OPT = 41,
- APL = 42,
- ///
- /// Delegation Signer
- ///
- DS = 43,
- ///
- /// SSH Key Fingerprint
- ///
- SSHFP = 44,
- IPSECKEY = 45,
- RRSIG = 46,
- NSEC = 47,
- DNSKEY = 48,
- DHCID = 49,
- NSEC3 = 50,
- NSEC3PARAM = 51,
- TLSA = 52,
- ///
- /// S/MIME cert association
- ///
- SMIMEA = 53,
- ///
- /// Host Identity Protocol
- ///
- HIP = 55,
- NINFO = 56,
- RKEY = 57,
- ///
- /// Trust Anchor LINK
- ///
- TALINK = 58,
- ///
- /// Child DS
- ///
- CDS = 59,
- ///
- /// DNSKEY(s) the Child wants reflected in DS
- ///
- CDNSKEY = 60,
- ///
- /// OpenPGP Key
- ///
- OPENPGPKEY = 61,
- ///
- /// Child-To-Parent Synchronization
- ///
- CSYNC = 62,
- ///
- /// Message digest for DNS zone
- ///
- ZONEMD = 63,
- ///
- /// Service Binding
- ///
- SVCB = 64,
- ///
- /// HTTPS Binding
- ///
- HTTPS = 65,
- SPF = 99,
- UINFO = 100,
- UID = 101,
- GID = 102,
- UNSPEC = 103,
- NID = 104,
- L32 = 105,
- L64 = 106,
- LP = 107,
- ///
- /// An EUI-48 address
- ///
- EUI48 = 108,
- ///
- /// An EUI-64 address
- ///
- EUI64 = 109,
- ///
- /// Transaction Key
- ///
- TKEY = 249,
- ///
- /// Transaction Signature
- ///
- TSIG = 250,
- ///
- /// Incremental transfer
- ///
- IXFR = 251,
- ///
- /// Transfer of an entire zone
- ///
- AXFR = 252,
- ///
- /// Mailbox-related RRs (MB, MG or MR)
- ///
- MAILB = 253,
- ///
- /// Mail agent RRs (OBSOLETE - see MX)
- ///
- MAILA = 254,
- ///
- /// A request for some or all records the server has available
- ///
- ANY = 255,
- URI = 256,
- ///
- /// Certification Authority Restriction
- ///
- CAA = 257,
- ///
- /// Application Visibility and Control
- ///
- AVC = 258,
- ///
- /// Digital Object Architecture
- ///
- DOA = 259,
- ///
- /// Automatic Multicast Tunneling Relay
- ///
- AMTRELAY = 260,
- ///
- /// DNSSEC Trust Authorities
- ///
- TA = 32768,
- ///
- /// DNSSEC Lookaside Validation (OBSOLETE)
- ///
- DLV = 32769
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsResourceRecord.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsResourceRecord.cs
deleted file mode 100644
index 4c46519..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsResourceRecord.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents metadata around a DNS resource record returned by a DNS server.
-///
-public sealed class DnsResourceRecord : IEquatable, IDnsByteArrayReader, IDnsByteArrayWriter
-{
- ///
- /// The type of DNS query.
- ///
- public DnsQueryType Type { get; set; }
- ///
- /// The class of DNS query.
- ///
- public DnsQueryClass Class { get; set; }
- ///
- /// The time to live entry for this record, in seconds.
- ///
- public uint TimeToLive { get; set; }
-
- ///
- /// The host name associated with this record.
- ///
- public string Host
- {
- get => string.Join(".", Name);
- set => Name = value.Split('.');
- }
-
- private string[] Name { get; set; } = Array.Empty();
-
- ///
- /// The value of this DNS record, which should be
- /// cast to the appropriate resource record type
- /// class depending on the .
- ///
- public IDnsResource? Resource { get; set; }
-
- private IDnsResource CreateResourceRecord(DnsQueryType recordType)
- {
- return recordType switch
- {
- DnsQueryType.A => new DnsIpAddressResource(),
- DnsQueryType.AAAA => new DnsIpAddressResource(),
- DnsQueryType.TEXT => new DnsTextResource(),
- DnsQueryType.CNAME => new DnsDomainResource(),
- DnsQueryType.NS => new DnsDomainResource(),
- DnsQueryType.PTR => new DnsDomainResource(),
- DnsQueryType.SPF => new DnsTextResource(),
- DnsQueryType.SOA => new DnsSoaResource(),
- DnsQueryType.MX => new DnsMxResource(),
- DnsQueryType.OPT => new DnsOptResource(),
- _ => new DnsUnknownResource(),
- };
- }
-
- ///
- public override string ToString() => $"Name: {Host} Type: {Type} Class: {Class} TTL: {TimeToLive} Resource: {Resource}";
-
- ///
- public bool Equals(DnsResourceRecord? other) => Host == other?.Host &&
- Type == other.Type &&
- Class == other.Class &&
- TimeToLive == other.TimeToLive &&
- Resource != null &&
- Resource.Equals(other.Resource);
-
- ///
- public override bool Equals(object? obj) => obj is DnsResourceRecord record ? Equals(record) : base.Equals(obj);
-
- ///
- public override int GetHashCode() => HashCode.Combine(Name, Type, Class, TimeToLive, Host, Resource);
-
- ///
- public void ReadBytes(ReadOnlyMemory bytes, ref int offset)
- {
- Name = DnsByteExtensions.ReadString(bytes, ref offset);
- Type = (DnsQueryType)DnsByteExtensions.ReadUInt16(bytes, ref offset);
- Class = (DnsQueryClass)DnsByteExtensions.ReadUInt16(bytes, ref offset);
- TimeToLive = DnsByteExtensions.ReadUInt32(bytes, ref offset);
- Resource = CreateResourceRecord(Type);
- var dataLength = DnsByteExtensions.ReadUInt16(bytes, ref offset);
- FromBytesKnownLength(Resource, bytes, ref offset, dataLength);
- }
-
- private static void FromBytesKnownLength(IDnsResource resource, ReadOnlyMemory bytes, ref int offset, int length)
- {
- var expectedOffset = offset + length;
- resource.ReadBytes(bytes, ref offset, length);
- if (offset != expectedOffset)
- {
- throw new InvalidOperationException($"{resource.GetType().Name}.{nameof(IDnsResource.ReadBytes)} did not read to offset {expectedOffset} (read to {offset})");
- }
- }
-
- ///
- public void WriteBytes(Memory bytes, ref int offset)
- {
- DnsByteExtensions.ToBytes(Name, bytes, ref offset);
- DnsByteExtensions.ToBytes((ushort)Type, bytes, ref offset);
- DnsByteExtensions.ToBytes((ushort)Class, bytes, ref offset);
- DnsByteExtensions.ToBytes(TimeToLive, bytes, ref offset);
-
- // First, write the resource, but save two bytes for the size (and do not advance the offset)
- var resourceSize = 0;
- Resource?.WriteBytes(bytes[(offset + sizeof(ushort))..], ref resourceSize);
-
- // Write the size of the resource in the two bytes preceeding (current offset)
- DnsByteExtensions.ToBytes((ushort)resourceSize, bytes, ref offset);
-
- // Advance the offset with the size of the resource
- offset += resourceSize;
- }
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsResponseCode.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsResponseCode.cs
deleted file mode 100644
index 3522bb8..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsResponseCode.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// DNS response codes. See https://www.iana.org/assignments/dns-parameters/dns-parameters.xml#dns-parameters-6
-///
-public enum DnsResponseCode : byte
-{
- ///
- /// No Error
- ///
- NoError = 0,
- ///
- /// Format Error
- ///
- FormErr = 1,
- ///
- /// Server Failure
- ///
- ServFail = 2,
- ///
- /// Non-Existent Domain
- ///
- NXDomain = 3,
- ///
- /// Not Implemented
- ///
- NotImp = 4,
- ///
- /// Query Refused
- ///
- Refused = 5,
- ///
- /// Name Exists when it should not
- ///
- YXDomain = 6,
- ///
- /// RR Set Exists when it should not
- ///
- YXRRSet = 7,
- ///
- /// RR Set that should exist does not
- ///
- NXRRSet = 8,
- ///
- /// Server Not Authoritative for zone or Not Authorized
- ///
- NotAuth = 9,
- ///
- /// Name not contained in zone
- ///
- NotZone = 10,
- ///
- /// DSO-TYPE Not Implemented
- ///
- DSOTYPENI = 11,
- ///
- /// BADVERS or TSIG Signature Failure
- ///
- BADVERS = 16,
- ///
- /// Key not recognized
- ///
- BADKEY = 17,
- ///
- /// Signature out of time window
- ///
- BADTIME = 18,
- ///
- /// Bad TKEY Mode
- ///
- BADMODE = 19,
- ///
- /// Duplicate key name
- ///
- BADNAME = 20,
- ///
- /// Algorithm not supported
- ///
- BADALG = 21,
- ///
- /// Bad Truncation
- ///
- BADTRUNC = 22,
- ///
- /// Bad/missing Server Cookie
- ///
- BADCOOKIE = 23
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsSoaResource.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsSoaResource.cs
deleted file mode 100644
index cef22d4..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsSoaResource.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// A Start of Authority record (abbreviated as SOA record) is a type of
-/// resource record in the Domain Name System (DNS) containing administrative
-/// information about the zone, especially regarding zone transfers.
-/// The SOA record format is specified in RFC 1035.
-///
-public sealed class DnsSoaResource : IDnsResource, IEquatable
-{
- ///
- /// Primary master name server for this zone
- ///
- public string MName { get; set; } = string.Empty;
- ///
- /// Email address of the administrator responsible for this zone.
- /// (As usual, the email address is encoded as a name. The part of the
- /// email address before the @ becomes the first label of the name; the
- /// domain name after the @ becomes the rest of the name. In zone-file
- /// format, dots in labels are escaped with backslashes; thus the email
- /// address john.doe@example.com would be represented in a zone file
- /// as john\.doe.example.com.)
- ///
- public string RName { get; set; } = string.Empty;
- ///
- /// Serial number for this zone. If a secondary name server slaved to
- /// this one observes an increase in this number, the slave will assume
- /// that the zone has been updated and initiate a zone transfer.
- ///
- public uint Serial { get; set; }
- ///
- /// Number of seconds after which secondary name servers should query
- /// the master for the SOA record, to detect zone changes. Recommendation
- /// for small and stable zones:[4] 86400 seconds (24 hours).
- ///
- public TimeSpan Refresh { get; set; }
- ///
- /// Number of seconds after which secondary name servers should retry to
- /// request the serial number from the master if the master does not respond.
- /// It must be less than Refresh. Recommendation for small and stable
- /// zones: 7200 seconds (2 hours).
- ///
- public TimeSpan Retry { get; set; }
- ///
- /// Number of seconds after which secondary name servers should stop answering
- /// request for this zone if the master does not respond. This value must be
- /// bigger than the sum of Refresh and Retry. Recommendation for small and
- /// stable zones: 3600000 seconds (1000 hours).
- ///
- public TimeSpan Expire { get; set; }
- ///
- /// Time to live for purposes of negative caching. Recommendation for
- /// small and stable zones: 3600 seconds (1 hour). Originally this
- /// field had the meaning of a minimum TTL value for resource records
- /// in this zone; it was changed to its current meaning by RFC 2308.
- ///
- public TimeSpan Minimum { get; set; }
-
- ///
- public bool Equals(DnsSoaResource? other) => MName == other?.MName && RName == other.RName && Serial == other.Serial && Refresh == other.Refresh && Retry == other.Retry && Expire == other.Expire && Minimum == other.Minimum;
-
- ///
- public override bool Equals(object? obj) => obj is DnsSoaResource record ? Equals(record) : base.Equals(obj);
-
- ///
- public override int GetHashCode() => HashCode.Combine(MName, RName, Serial, Refresh, Retry, Expire, Minimum);
-
- ///
- public void ReadBytes(ReadOnlyMemory bytes, ref int offset, int length)
- {
- MName = string.Join(".", DnsByteExtensions.ReadString(bytes, ref offset));
- RName = string.Join(".", DnsByteExtensions.ReadString(bytes, ref offset));
- Serial = DnsByteExtensions.ReadUInt32(bytes, ref offset);
- Refresh = TimeSpan.FromSeconds(DnsByteExtensions.ReadInt32(bytes, ref offset));
- Retry = TimeSpan.FromSeconds(DnsByteExtensions.ReadInt32(bytes, ref offset));
- Expire = TimeSpan.FromSeconds(DnsByteExtensions.ReadInt32(bytes, ref offset));
- Minimum = TimeSpan.FromSeconds(DnsByteExtensions.ReadUInt32(bytes, ref offset));
- }
-
- ///
- public override string ToString() => MName;
-
- ///
- public void WriteBytes(Memory bytes, ref int offset)
- {
- DnsByteExtensions.ToBytes(MName.Split('.'), bytes, ref offset);
- DnsByteExtensions.ToBytes(RName.Split('.'), bytes, ref offset);
- DnsByteExtensions.ToBytes(Serial, bytes, ref offset);
- DnsByteExtensions.ToBytes((int)Refresh.TotalSeconds, bytes, ref offset);
- DnsByteExtensions.ToBytes((int)Retry.TotalSeconds, bytes, ref offset);
- DnsByteExtensions.ToBytes((int)Expire.TotalSeconds, bytes, ref offset);
- DnsByteExtensions.ToBytes((uint)Minimum.TotalSeconds, bytes, ref offset);
- }
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsStringResource.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsStringResource.cs
deleted file mode 100644
index 1f35e0d..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsStringResource.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents a DNS resource containing a string.
-///
-public abstract class DnsStringResource : IDnsResource, IEquatable
-{
- ///
- /// The text entry contained within this resource.
- ///
- ///
- /// The text values of this resource as an array of strings.
- ///
- public string[] Entries { get; set; } = Array.Empty();
-
- ///
- public bool Equals(DnsStringResource? other) => Entries.SequenceEqual(other?.Entries ?? Array.Empty());
-
- ///
- public override bool Equals(object? obj) => obj is DnsStringResource record ? Equals(record) : base.Equals(obj);
-
- ///
- public override int GetHashCode() => HashCode.Combine(Entries);
-
- ///
- /// Describes whether this string resource can use string compression.
- ///
- protected abstract bool CanUseCompression { get; }
-
- ///
- public virtual void ReadBytes(ReadOnlyMemory bytes, ref int offset, int length)
- {
- // Provide the ReadString method with a sliced buffer, which ends when this resource ends
- // It must start where the packet starts, since there are often pointers back to the beginning
- Entries = DnsByteExtensions.ReadString(bytes[..(offset + length)], ref offset, CanUseCompression);
- }
-
- ///
- public virtual void WriteBytes(Memory bytes, ref int offset)
- {
- DnsByteExtensions.ToBytes(Entries, bytes, ref offset);
- }
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsTextResource.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsTextResource.cs
deleted file mode 100644
index 47e3b54..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsTextResource.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents a DNS text resource containing a string.
-///
-public sealed class DnsTextResource : DnsStringResource
-{
- ///
- protected override bool CanUseCompression => false;
-
- ///
- public override string ToString() => '"' + string.Join("\", \"", Entries) + '"';
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsUnknownResource.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsUnknownResource.cs
deleted file mode 100644
index eed7c56..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/DnsUnknownResource.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents a DNS resource that this library cannot yet understand.
-///
-public sealed class DnsUnknownResource : IDnsResource, IEquatable
-{
- ///
- /// The raw bytes recieved for this DNS resource.
- ///
- ///
- /// The raw set of bytes representing the value fo this DNS resource.
- /// For string values, due to compression the whole packet may be needed.
- ///
- public ReadOnlyMemory Raw { get; set; }
-
- ///
- public bool Equals(DnsUnknownResource? other)
- {
- return other != null && Raw.Span.SequenceEqual(other.Raw.Span);
- }
-
- ///
- public override bool Equals(object? obj) => obj is DnsUnknownResource record ? Equals(record) : base.Equals(obj);
-
- ///
- public override int GetHashCode() => HashCode.Combine(Raw);
-
- ///
- public void ReadBytes(ReadOnlyMemory bytes, ref int offset, int length)
- {
- Raw = DnsByteExtensions.ReadBytes(bytes, length, ref offset);
- }
-
- ///
- public override string ToString() => $"Raw {Raw.Length} bytes";
-
- ///
- public void WriteBytes(Memory bytes, ref int offset)
- {
- Raw.CopyTo(bytes[offset..]);
- offset += Raw.Length;
- }
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/IDnsByteArrayReader.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/IDnsByteArrayReader.cs
deleted file mode 100644
index c41b714..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/IDnsByteArrayReader.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents a class which can read from the specified byte array.
-///
-public interface IDnsByteArrayReader
-{
- ///
- /// Read from the specified byte array, starting at the specified offset.
- ///
- /// The byte array to read from.
- /// The offset to start at.
- void ReadBytes(ReadOnlyMemory bytes, ref int offset);
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/IDnsByteArrayWriter.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/IDnsByteArrayWriter.cs
deleted file mode 100644
index 73414af..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/IDnsByteArrayWriter.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents a class which can write its contents to an enumerable of bytes.
-///
-public interface IDnsByteArrayWriter
-{
- ///
- /// Write to the the specified byte array.
- ///
- /// The byte array to read from.
- /// The offset to start at.
- void WriteBytes(Memory bytes, ref int offset);
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/IDnsResource.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/IDnsResource.cs
deleted file mode 100644
index b0f6796..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsWireformatTools/IDnsResource.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-
-namespace MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-///
-/// Represents a type of DNS resource.
-///
-public interface IDnsResource : IDnsByteArrayWriter
-{
- ///
- /// Read from the specified byte array, starting at the specified offset.
- ///
- /// The byte array to read from.
- /// The offset to start at.
- /// The maximum number of bytes to read.
- void ReadBytes(ReadOnlyMemory bytes, ref int offset, int length);
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/GetIP.cs b/MsmhToolsClass/MsmhToolsClass/DnsTool/GetIP.cs
deleted file mode 100644
index 7919986..0000000
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/GetIP.cs
+++ /dev/null
@@ -1,557 +0,0 @@
-using System.Diagnostics;
-using System.Net;
-using System.Net.Http.Headers;
-using System.Net.NetworkInformation;
-using System.Net.Sockets;
-using System.Text.Json;
-using MsmhToolsClass.DnsTool.DnsWireformatTools;
-
-namespace MsmhToolsClass.DnsTool;
-
-public static class GetIP
-{
- //================================================= Get From System
-
- ///
- /// Get First IP in Answer Section
- ///
- /// Host
- /// Look for IPv6
- /// Returns string.Empty if fail
- public static string GetIpFromSystem(string host, bool getIPv6 = false, bool alsoUsePing = true)
- {
- string result = string.Empty;
- List ips = GetIpsFromSystem(host, getIPv6);
- if (ips.Any()) result = ips[0];
- else
- {
- if (!alsoUsePing) return result;
-
- try
- {
- using Ping ping = new();
- PingReply reply = ping.Send(host, 3000);
- string ipStr = reply.Address.ToString();
- bool isIpv6 = NetworkTool.IsIPv6(reply.Address);
-
- if (getIPv6 && isIpv6) result = ipStr;
- if (!getIPv6 && !isIpv6) result = ipStr;
- }
- catch (Exception)
- {
- // do nothing
- }
- }
-
- return result;
- }
-
- ///
- /// Get a List of IPs
- ///
- /// Host
- /// Look for IPv6
- /// Returns Empty List if fail
- public static List GetIpsFromSystem(string host, bool getIPv6 = false)
- {
- List ips = new();
-
- try
- {
- //IPAddress[] ipAddresses = System.Net.Dns.GetHostAddresses(host);
- IPAddress[] ipAddresses = System.Net.Dns.GetHostEntry(host).AddressList;
-
- if (ipAddresses == null || ipAddresses.Length == 0) return ips;
-
- if (!getIPv6)
- {
- for (int n = 0; n < ipAddresses.Length; n++)
- {
- AddressFamily addressFamily = ipAddresses[n].AddressFamily;
- string ip = ipAddresses[n].ToString();
- if (addressFamily != AddressFamily.InterNetworkV6)
- if (!string.IsNullOrEmpty(ip) && !NetworkTool.IsLocalIP(ip))
- ips.Add(ip);
- }
- }
- else
- {
- for (int n = 0; n < ipAddresses.Length; n++)
- {
- AddressFamily addressFamily = ipAddresses[n].AddressFamily;
- string ip = ipAddresses[n].ToString();
- if (addressFamily == AddressFamily.InterNetworkV6)
- if (!string.IsNullOrEmpty(ip) && !NetworkTool.IsLocalIP(ip))
- ips.Add(ip);
- }
- }
- }
- catch (Exception ex)
- {
- Debug.WriteLine(ex.Message);
- }
- return ips;
- }
-
- //================================================= Get From DoH Using Wire Format
-
- ///
- /// Get First IP in Answer Section (Using Wire Format)
- ///
- /// Host
- /// DoH Server
- /// Timeout (Sec)
- /// Use Proxy to Get IP
- /// Returns string.Empty if fail
- public static async Task GetIpFromDohUsingWireFormat(string host, string doh, int timeoutSec, string? proxyScheme = null, string? proxyUsername = null, string? proxyPassword = null)
- {
- List ips = await GetIpsFromDohUsingWireFormat(host, doh, timeoutSec, proxyScheme, proxyUsername, proxyPassword);
- if (ips.Any()) return ips[0];
- return string.Empty;
- }
-
- ///
- /// Get A List of IPs (Using Wire Format)
- ///
- /// Host
- /// DoH Server
- /// Timeout (Sec)
- /// Use Proxy to Get IPs
- /// Returns an Empty List if fail
- public static async Task> GetIpsFromDohUsingWireFormat(string host, string doh, int timeoutSec, string? proxyScheme = null, string? proxyUsername = null, string? proxyPassword = null)
- {
- if (string.IsNullOrEmpty(proxyScheme))
- return await GetIpsFromDohUsingWireFormatNoProxy(host, doh, timeoutSec);
- else
- return await GetIpsFromDohUsingWireFormatProxy(host, doh, timeoutSec, proxyScheme, proxyUsername, proxyPassword);
- }
-
- private static async Task> GetIpsFromDohUsingWireFormatNoProxy(string host, string doh, int timeoutSec)
- {
- List ips = new();
-
- try
- {
- DnsMessage dnsMessage = DnsQueryFactory.CreateQuery(host);
- var queryBuffer = DnsByteExtensions.AllocatePinnedNetworkBuffer();
- int queryBufferLength = 0;
- dnsMessage.WriteBytes(queryBuffer, ref queryBufferLength);
-
- Uri uri = new(doh);
-
- using HttpClient httpClient = new();
- httpClient.Timeout = new TimeSpan(0, 0, timeoutSec);
-
- HttpContent content = new ReadOnlyMemoryContent(queryBuffer[..queryBufferLength]);
- content.Headers.ContentType = new MediaTypeHeaderValue("application/dns-message");
-
- HttpRequestMessage message = new(HttpMethod.Post, "/dns-query");
- message.RequestUri = uri;
- message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/dns-message"));
- message.Content = content;
-
- using HttpResponseMessage r = await httpClient.SendAsync(message);
-
- if (r.IsSuccessStatusCode)
- {
- byte[] buffer = await r.Content.ReadAsByteArrayAsync();
-
- DnsMessage answer = DnsByteExtensions.FromBytes(buffer);
- if (httpClient.BaseAddress != null)
- answer.Header.Tags.Add("Resolver", httpClient.BaseAddress.ToString());
-
- for (int n = 0; n < answer.Answers.Count; n++)
- {
- DnsResourceRecord drr = answer.Answers[n];
- string? ip = drr.Resource?.ToString();
- if (!string.IsNullOrEmpty(ip) && !NetworkTool.IsLocalIP(ip) && IPAddress.TryParse(ip, out IPAddress? _))
- ips.Add(ip);
- }
- }
- }
- catch (Exception) { }
-
- return ips;
- }
-
- private static async Task> GetIpsFromDohUsingWireFormatProxy(string host, string doh, int timeoutSec, string? proxyScheme = null, string? proxyUsername = null, string? proxyPassword = null)
- {
- List ips = new();
-
- try
- {
- DnsMessage dnsMessage = DnsQueryFactory.CreateQuery(host);
- var queryBuffer = DnsByteExtensions.AllocatePinnedNetworkBuffer();
- int queryBufferLength = 0;
- dnsMessage.WriteBytes(queryBuffer, ref queryBufferLength);
-
- Uri uri = new(doh);
-
- using SocketsHttpHandler socketsHttpHandler = new();
- socketsHttpHandler.Proxy = new WebProxy(proxyScheme, true, null, new NetworkCredential(proxyUsername, proxyPassword));
-
- using HttpClient httpClient = new(socketsHttpHandler);
- httpClient.Timeout = new TimeSpan(0, 0, timeoutSec);
-
- HttpContent content = new ReadOnlyMemoryContent(queryBuffer[..queryBufferLength]);
- content.Headers.ContentType = new MediaTypeHeaderValue("application/dns-message");
-
- HttpRequestMessage message = new(HttpMethod.Post, "/dns-query");
- message.RequestUri = uri;
- message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/dns-message"));
- message.Content = content;
-
- using HttpResponseMessage r = await httpClient.SendAsync(message);
-
- if (r.IsSuccessStatusCode)
- {
- byte[] buffer = await r.Content.ReadAsByteArrayAsync();
-
- DnsMessage answer = DnsByteExtensions.FromBytes(buffer);
- if (httpClient.BaseAddress != null)
- answer.Header.Tags.Add("Resolver", httpClient.BaseAddress.ToString());
-
- for (int n = 0; n < answer.Answers.Count; n++)
- {
- DnsResourceRecord drr = answer.Answers[n];
- string? ip = drr.Resource?.ToString();
- if (!string.IsNullOrEmpty(ip) && !NetworkTool.IsLocalIP(ip) && IPAddress.TryParse(ip, out IPAddress? _))
- ips.Add(ip);
- }
- }
- }
- catch (Exception) { }
-
- return ips;
- }
-
- //================================================= Get From DoH Using JSON Format
-
- ///
- /// Get first IP Using JSON Format
- ///
- /// Host
- /// DoH Address
- /// Timeout (Sec)
- /// Use Proxy to Get IP
- /// Returns Empty List if fail
- public static async Task GetIpFromDohUsingJsonFormat(string host, string doh, int timeoutSec, string? proxyScheme = null, string? proxyUsername = null, string? proxyPassword = null)
- {
- List ips = await GetIpsFromDohUsingJsonFormat(host, doh, timeoutSec, proxyScheme, proxyUsername, proxyPassword);
- if (ips.Any()) return ips[0];
- return string.Empty;
- }
-
- ///
- /// Get IPs Using JSON Format
- ///
- /// Host
- /// DoH Address
- /// Timeout (Sec)
- /// Use Proxy to Get IP
- /// Returns Empty List if fail
- public static async Task> GetIpsFromDohUsingJsonFormat(string host, string doh, int timeoutSec, string? proxyScheme = null, string? proxyUsername = null, string? proxyPassword = null)
- {
- if (string.IsNullOrEmpty(proxyScheme))
- return await GetIpFromDohUsingJsonFormatNoProxy(host, doh, timeoutSec);
- else
- return await GetIpFromDohUsingJsonFormatProxy(host, doh, timeoutSec, proxyScheme, proxyUsername, proxyPassword);
- }
-
- private static async Task> GetIpFromDohUsingJsonFormatNoProxy(string host, string doh, int timeoutSec)
- {
- List ips = new();
-
- try
- {
- doh = doh.Trim();
- if (doh.EndsWith('/')) doh = doh.TrimEnd('/');
-
- host = Uri.EscapeDataString(host);
- string path = $"?name={host}&type=A";
- Uri uri = new(doh + path);
-
- HttpClient httpClient = new();
- httpClient.Timeout = new TimeSpan(0, 0, timeoutSec);
-
- HttpRequestMessage message = new();
- message.Method = HttpMethod.Get;
- message.RequestUri = uri;
- message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/dns-json"));
-
- using HttpResponseMessage r = await httpClient.SendAsync(message);
-
- if (r.IsSuccessStatusCode)
- {
- string contents = await r.Content.ReadAsStringAsync();
- httpClient.Dispose();
- ips = GetIpFromJson(contents);
- }
- httpClient.Dispose();
- }
- catch (Exception ex)
- {
- Debug.WriteLine(ex.Message);
- }
-
- return ips;
- }
-
- private static async Task> GetIpFromDohUsingJsonFormatProxy(string host, string doh, int timeoutSec, string? proxyScheme = null, string? proxyUsername = null, string? proxyPassword = null)
- {
- List ips = new();
-
- try
- {
- doh = doh.Trim();
- if (doh.EndsWith('/')) doh = doh.TrimEnd('/');
-
- host = Uri.EscapeDataString(host);
- string path = $"?name={host}&type=A";
- Uri uri = new(doh + path);
-
- using SocketsHttpHandler socketsHttpHandler = new();
- socketsHttpHandler.Proxy = new WebProxy(proxyScheme, true, null, new NetworkCredential(proxyUsername, proxyPassword));
-
- using HttpClient httpClient = new(socketsHttpHandler);
- httpClient.Timeout = new TimeSpan(0, 0, timeoutSec);
-
- HttpRequestMessage message = new();
- message.Method = HttpMethod.Get;
- message.RequestUri = uri;
- message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/dns-json"));
-
- using HttpResponseMessage r = await httpClient.SendAsync(message);
-
- if (r.IsSuccessStatusCode)
- {
- string contents = await r.Content.ReadAsStringAsync();
- httpClient.Dispose();
- ips = GetIpFromJson(contents);
- }
- httpClient.Dispose();
- }
- catch (Exception ex)
- {
- Debug.WriteLine(ex.Message);
- }
-
- return ips;
- }
-
- private static List GetIpFromJson(string jsonContent)
- {
- List ips = new();
- try
- {
- using JsonDocument jd = JsonDocument.Parse(jsonContent);
- JsonElement rootElement = jd.RootElement;
- if (rootElement.ValueKind == JsonValueKind.Object)
- {
- JsonProperty[] properties = rootElement.EnumerateObject().ToArray();
- for (int n = 0; n < properties.Length; n++)
- {
- JsonProperty property = properties[n];
- if (property.Name == "Answer")
- {
- JsonElement[] elementsProperties = property.Value.EnumerateArray().ToArray();
- for (int n2 = 0; n2 < elementsProperties.Length; n2++)
- {
- JsonElement elementProperty = elementsProperties[n2];
- if (elementProperty.ValueKind == JsonValueKind.Object)
- {
- JsonProperty[] answerProperties = elementProperty.EnumerateObject().ToArray();
- for (int n3 = 0; n3 < answerProperties.Length; n3++)
- {
- JsonProperty answerProperty = answerProperties[n3];
- if (answerProperty.Name == "data")
- {
- string ip = answerProperty.Value.ToString();
- if (!string.IsNullOrEmpty(ip) && !NetworkTool.IsLocalIP(ip) && IPAddress.TryParse(ip, out IPAddress? _))
- ips.Add(ip);
- }
- }
- }
- }
- }
- }
- }
- }
- catch (Exception ex)
- {
- Debug.WriteLine(ex.Message);
- }
- return ips;
- }
-
- //================================================= Get From Plain DNS
-
- public enum PlainDnsProtocol
- {
- UDP, TCP, Both
- }
-
- ///
- /// Get First IP in Answer Section
- ///
- /// Host
- /// Plain DNS IP
- /// Plain DNS Port
- /// Timeout (ms)
- /// Returns string.Empty if fail
- public static async Task GetIpFromPlainDNS(string host, string dnsIP, int dnsPort, int timeoutSec, PlainDnsProtocol protocol = PlainDnsProtocol.Both)
- {
- List ips = await GetIpsFromPlainDNS(host, dnsIP, dnsPort, timeoutSec, protocol);
- if (ips.Any()) return ips[0];
- return string.Empty;
- }
-
- ///
- /// Get a List of IPs
- ///
- /// Host
- /// Plain DNS IP
- /// Plain DNS Port
- /// Timeout (ms)
- /// Returns an Empty List if fail
- public async static Task> GetIpsFromPlainDNS(string host, string dnsIP, int dnsPort, int timeoutSec, PlainDnsProtocol protocol = PlainDnsProtocol.Both)
- {
- List ips;
-
- if (protocol == PlainDnsProtocol.UDP)
- ips = await GetIpsFromPlainDnsUdp(host, dnsIP, dnsPort, timeoutSec);
- else if (protocol == PlainDnsProtocol.TCP)
- ips = await GetIpsFromPlainDnsTcp(host, dnsIP, dnsPort, timeoutSec);
- else
- {
- ips = await GetIpsFromPlainDnsUdp(host, dnsIP, dnsPort, timeoutSec);
- if (!ips.Any())
- ips = await GetIpsFromPlainDnsTcp(host, dnsIP, dnsPort, timeoutSec);
- }
-
- return ips;
- }
-
- ///
- /// Get a List of IPs
- ///
- /// Host
- /// Plain DNS IP
- /// Plain DNS Port
- /// Timeout (ms)
- /// Returns an Empty List if fail
- private async static Task> GetIpsFromPlainDnsUdp(string host, string dnsIP, int dnsPort, int timeoutSec)
- {
- List ips = new();
-
- Task task = Task.Run(async () =>
- {
- try
- {
- DnsMessage dnsMessage = DnsQueryFactory.CreateQuery(host);
- IPEndPoint ep = new(IPAddress.Parse(dnsIP), dnsPort);
-
- Memory queryBuffer = DnsByteExtensions.AllocatePinnedNetworkBuffer();
- int queryBufferLength = 0;
- dnsMessage.WriteBytes(queryBuffer, ref queryBufferLength);
- byte[] raw = queryBuffer[..queryBufferLength].ToArray();
-
- using Socket socket = new(ep.AddressFamily, SocketType.Dgram, ProtocolType.Udp | ProtocolType.Tcp);
- await socket.SendToAsync(raw, SocketFlags.None, ep);
- byte[] buffer = new byte[65536];
- int receivedLength = await socket.ReceiveAsync(buffer, SocketFlags.None);
- buffer = buffer.Take(receivedLength).ToArray();
-
- socket.Close();
-
- DnsMessage answer = DnsByteExtensions.FromBytes(buffer);
- answer.Header.Id = dnsMessage.Header.Id;
- answer.Header.Tags.Add("Resolver", $"udp://{ep}/");
- Debug.WriteLine("UDP Answers: " + answer.Answers.Count);
-
- for (int n = 0; n < answer.Answers.Count; n++)
- {
- DnsResourceRecord drr = answer.Answers[n];
- string? ip = drr.Resource?.ToString();
- if (!string.IsNullOrEmpty(ip) && !NetworkTool.IsLocalIP(ip) && IPAddress.TryParse(ip, out IPAddress? _))
- ips.Add(ip);
- }
- }
- catch (Exception)
- {
- // do nothing
- }
- });
- try { await task.WaitAsync(TimeSpan.FromSeconds(timeoutSec)); } catch (Exception) { }
-
- return ips;
- }
-
- ///
- /// Get a List of IPs
- ///
- /// Host
- /// Plain DNS IP
- /// Plain DNS Port
- /// Timeout (ms)
- /// Returns an Empty List if fail
- private async static Task> GetIpsFromPlainDnsTcp(string host, string dnsIP, int dnsPort, int timeoutSec)
- {
- List ips = new();
-
- Task task = Task.Run(async () =>
- {
- try
- {
- DnsMessage dnsMessage = DnsQueryFactory.CreateQuery(host);
- IPEndPoint ep = new(IPAddress.Parse(dnsIP), dnsPort);
-
- using Socket socket = new(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
- await socket.ConnectAsync(ep);
-
- Memory buffer = DnsByteExtensions.AllocatePinnedNetworkBuffer();
- int sendOffset = sizeof(ushort);
- dnsMessage.WriteBytes(buffer, ref sendOffset);
-
- int fakeOffset = 0;
- DnsByteExtensions.ToBytes((ushort)(sendOffset - sizeof(ushort)), buffer, ref fakeOffset);
-
- Memory sendBuffer = buffer[..sendOffset];
- await socket.SendAsync(sendBuffer, SocketFlags.None);
- int received = await socket.ReceiveAsync(buffer, SocketFlags.None);
-
- int offset = 0;
- ushort answerLength = DnsByteExtensions.ReadUInt16(buffer, ref offset);
-
- while (received < answerLength)
- {
- received += await socket.ReceiveAsync(buffer[received..], SocketFlags.None);
- }
-
- socket.Close();
-
- Memory answerBuffer = buffer.Slice(offset, answerLength);
-
- DnsMessage answer = DnsByteExtensions.FromBytes(answerBuffer);
- answer.Header.Tags.Add("Resolver", $"tcp://{ep}/");
- Debug.WriteLine("TCP Answers: " + answer.Answers.Count);
-
- for (int n = 0; n < answer.Answers.Count; n++)
- {
- DnsResourceRecord drr = answer.Answers[n];
- string? ip = drr.Resource?.ToString();
- if (!string.IsNullOrEmpty(ip) && !NetworkTool.IsLocalIP(ip) && IPAddress.TryParse(ip, out IPAddress? _))
- ips.Add(ip);
- }
- }
- catch (Exception)
- {
- // do nothing
- }
- });
- try { await task.WaitAsync(TimeSpan.FromSeconds(timeoutSec)); } catch (Exception) { }
-
- return ips;
- }
-
-}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DrawingTool.cs b/MsmhToolsClass/MsmhToolsClass/DrawingTool.cs
index ff444af..93ced45 100644
--- a/MsmhToolsClass/MsmhToolsClass/DrawingTool.cs
+++ b/MsmhToolsClass/MsmhToolsClass/DrawingTool.cs
@@ -17,16 +17,15 @@ public static class DrawingTool
public static Icon? ExtractIcon(string dllPath, int index, bool largeIcon)
{
Icon? icon = null;
- if (!OperatingSystem.IsWindows()) return icon;
+ if (!OperatingSystem.IsWindowsVersionAtLeast(6, 1)) return icon;
_ = ExtractIconEx(dllPath, index, out IntPtr large, out IntPtr small, 1);
+
try
{
icon = Icon.FromHandle(largeIcon ? large : small);
}
- catch
- {
- // do nothing
- }
+ catch { }
+
return icon;
}
//-----------------------------------------------------------------------------------
@@ -35,7 +34,7 @@ public static class DrawingTool
///
public static GraphicsPath? RoundedRectangle(Rectangle bounds, int radiusTopLeft, int radiusTopRight, int radiusBottomRight, int radiusBottomLeft)
{
- if (!OperatingSystem.IsWindows()) return null;
+ if (!OperatingSystem.IsWindowsVersionAtLeast(6, 1)) return null;
int diameterTopLeft = radiusTopLeft * 2;
int diameterTopRight = radiusTopRight * 2;
int diameterBottomRight = radiusBottomRight * 2;
@@ -98,7 +97,7 @@ public static class DrawingTool
///
public static Bitmap? Invert(Bitmap source)
{
- if (!OperatingSystem.IsWindows()) return null;
+ if (!OperatingSystem.IsWindowsVersionAtLeast(6, 1)) return null;
//create a blank bitmap the same size as original
Bitmap newBitmap = new(source.Width, source.Height);
//get a graphics object from the new image
diff --git a/MsmhToolsClass/MsmhToolsClass/EncodingTool.cs b/MsmhToolsClass/MsmhToolsClass/EncodingTool.cs
index 79075dc..5177792 100644
--- a/MsmhToolsClass/MsmhToolsClass/EncodingTool.cs
+++ b/MsmhToolsClass/MsmhToolsClass/EncodingTool.cs
@@ -92,12 +92,11 @@ public static string Base64Decode(string encodedString)
}
}
- public static string UrlEncode(byte[] arg)
+ public static string Base64ToBase64Url(string base64)
{
try
{
- string s = Convert.ToBase64String(arg);
- return s.Replace("=", "").Replace("/", "_").Replace("+", "-");
+ return base64.Replace("=", "").Replace("/", "_").Replace("+", "-");
}
catch (Exception)
{
@@ -105,11 +104,11 @@ public static string UrlEncode(byte[] arg)
}
}
- public static string UrlToBase64(string arg)
+ public static string Base64UrlToBase64(string base64Url)
{
try
{
- return arg.PadRight(arg.Length + (4 - arg.Length % 4) % 4, '=').Replace("_", "/").Replace("-", "+");
+ return base64Url.PadRight(base64Url.Length + (4 - base64Url.Length % 4) % 4, '=').Replace("_", "/").Replace("-", "+");
}
catch (Exception)
{
@@ -117,36 +116,35 @@ public static string UrlToBase64(string arg)
}
}
- public static byte[] UrlDecode(string arg)
+ public static string UrlEncode(byte[] buffer)
{
try
{
- string decrypted = UrlToBase64(arg);
- return Convert.FromBase64String(decrypted);
+ string base64 = Convert.ToBase64String(buffer);
+ return Base64ToBase64Url(base64);
}
- catch (Exception ex)
+ catch (Exception)
{
- Debug.WriteLine("UrlDecode: " + ex.Message);
- return Array.Empty();
+ return string.Empty;
}
}
- public static T[] SubArray(T[] arr, int start, int length)
+ public static byte[] UrlDecode(string base64Url)
{
- T[] result = new T[length];
+ string base64 = Base64UrlToBase64(base64Url);
+
try
{
- Buffer.BlockCopy(arr, start, result, 0, length);
+
+ return Convert.FromBase64String(base64);
}
catch (Exception ex)
{
- Debug.WriteLine("SubArray: " + ex.Message);
+ Debug.WriteLine("UrlDecode: " + ex.Message);
+ Debug.WriteLine("UrlDecode Base64Url: " + base64Url);
+ Debug.WriteLine("UrlDecode Base64: " + base64);
+ return Array.Empty();
}
- return result;
}
- public static T[] SubArray(T[] arr, int start)
- {
- return SubArray(arr, start, arr.Length - start);
- }
}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/ExtensionsMethods.cs b/MsmhToolsClass/MsmhToolsClass/ExtensionsMethods.cs
index 4b6a60b..98cf9cf 100644
--- a/MsmhToolsClass/MsmhToolsClass/ExtensionsMethods.cs
+++ b/MsmhToolsClass/MsmhToolsClass/ExtensionsMethods.cs
@@ -13,6 +13,8 @@
using System.Diagnostics;
using System.Drawing;
using System.Net.Sockets;
+using System.Collections.Specialized;
+using System.Collections.Concurrent;
namespace MsmhToolsClass;
@@ -23,6 +25,63 @@ public static class Methods
}
public static class ExtensionsMethods
{
+ //-----------------------------------------------------------------------------------
+ public static bool TryUpdate(this ConcurrentDictionary ccDic, K key, V newValue) where K : notnull
+ {
+ try
+ {
+ if (key == null) return false;
+ bool isKeyExist = ccDic.TryGetValue(key, out V? oldValue);
+ if (isKeyExist && oldValue != null)
+ return ccDic.TryUpdate(key, newValue, oldValue);
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("ExtensionsMethods TryUpdate: " + ex.Message);
+ return false;
+ }
+ }
+ //-----------------------------------------------------------------------------------
+ public static V? AddOrUpdate(this ConcurrentDictionary ccDic, K key, V newValue) where K : notnull
+ {
+ try
+ {
+ if (key == null) return default;
+ return ccDic.AddOrUpdate(key, newValue, (oldkey, oldvalue) => newValue);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("ExtensionsMethods TryUpdate: " + ex.Message);
+ return default;
+ }
+ }
+ //-----------------------------------------------------------------------------------
+ ///
+ /// If Key Exist Adds The Value (Comma-Separated)
+ ///
+ public static void AddAndUpdate(this NameValueCollection nvc, string? key, string? value)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(key)) return;
+ if (string.IsNullOrEmpty(value)) return;
+
+ string? theKey = nvc[key];
+ if (!string.IsNullOrEmpty(theKey)) // Key Exist
+ {
+ string tempVal = theKey;
+ tempVal += "," + value;
+ nvc.Remove(key);
+ nvc.Add(key, tempVal);
+ }
+ else
+ {
+ nvc.Add(key, value);
+ }
+ }
+ catch (Exception) { }
+ }
//-----------------------------------------------------------------------------------
public static string GetInnerExceptions(this Exception ex)
{
@@ -84,12 +143,14 @@ public static async Task SaveAsync(this XDocument xDocument, string xmlFilePath)
{
try
{
- XmlWriterSettings xmlWriterSettings = new();
- xmlWriterSettings.WriteEndDocumentOnClose = true;
- xmlWriterSettings.Async = true;
- xmlWriterSettings.Indent = true;
- xmlWriterSettings.OmitXmlDeclaration = true;
- xmlWriterSettings.Encoding = new UTF8Encoding(false);
+ XmlWriterSettings xmlWriterSettings = new()
+ {
+ WriteEndDocumentOnClose = true,
+ Async = true,
+ Indent = true,
+ OmitXmlDeclaration = true,
+ Encoding = new UTF8Encoding(false)
+ };
using XmlWriter xmlWriter = XmlWriter.Create(xmlFilePath, xmlWriterSettings);
await xDocument.SaveAsync(xmlWriter, CancellationToken.None);
}
@@ -104,10 +165,26 @@ public static TimeSpan Round(this TimeSpan timeSpan, int precision)
return TimeSpan.FromSeconds(Math.Round(timeSpan.TotalSeconds, precision));
}
//-----------------------------------------------------------------------------------
- public static bool IsContain(this List list, T t)
+ public static string ToString(this List list, string separator)
{
+ string result = string.Empty;
for (int n = 0; n < list.Count; n++)
- if (t != null && t.Equals(list[n])) return true;
+ {
+ T t = list[n];
+ result += $"{t}{separator}";
+ }
+ if (result.EndsWith(separator)) result = result.TrimEnd(separator);
+ return result;
+ }
+ //-----------------------------------------------------------------------------------
+ public static bool IsContain(this List list, T t)
+ {
+ try
+ {
+ for (int n = 0; n < list.Count; n++)
+ if (t != null && t.Equals(list[n])) return true;
+ }
+ catch (Exception) { }
return false;
}
//-----------------------------------------------------------------------------------
@@ -282,7 +359,7 @@ public static T IsNotNull([NotNull] this T? value, [CallerArgumentExpression(
///
public static GraphicsPath? Shrink(this GraphicsPath path, float width)
{
- if (!OperatingSystem.IsWindows()) return null;
+ if (!OperatingSystem.IsWindowsVersionAtLeast(6, 1)) return null;
using GraphicsPath gp = new();
gp.AddPath(path, false);
gp.CloseAllFigures();
@@ -314,7 +391,7 @@ public static T IsNotNull([NotNull] this T? value, [CallerArgumentExpression(
private static int CountNextFigure(PathData data, int position)
{
int count = 0;
- if (!OperatingSystem.IsWindows()) return count;
+ if (!OperatingSystem.IsWindowsVersionAtLeast(6, 1)) return count;
for (int i = position; i < data?.Types?.Length; i++)
{
count++;
@@ -329,7 +406,7 @@ private static int CountNextFigure(PathData data, int position)
///
public static void DrawRoundedRectangle(this Graphics graphics, Pen pen, Rectangle bounds, int radiusTopLeft, int radiusTopRight, int radiusBottomRight, int radiusBottomLeft)
{
- if (!OperatingSystem.IsWindows()) return;
+ if (!OperatingSystem.IsWindowsVersionAtLeast(6, 1)) return;
GraphicsPath? path = DrawingTool.RoundedRectangle(bounds, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
if (path != null)
@@ -342,7 +419,7 @@ public static void DrawRoundedRectangle(this Graphics graphics, Pen pen, Rectang
///
public static void FillRoundedRectangle(this Graphics graphics, Brush brush, Rectangle bounds, int radiusTopLeft, int radiusTopRight, int radiusBottomRight, int radiusBottomLeft)
{
- if (!OperatingSystem.IsWindows()) return;
+ if (!OperatingSystem.IsWindowsVersionAtLeast(6, 1)) return;
GraphicsPath? path = DrawingTool.RoundedRectangle(bounds, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
if (path != null)
@@ -355,7 +432,7 @@ public static void FillRoundedRectangle(this Graphics graphics, Brush brush, Rec
///
public static void DrawCircle(this Graphics g, Pen pen, float centerX, float centerY, float radius)
{
- if (!OperatingSystem.IsWindows()) return;
+ if (!OperatingSystem.IsWindowsVersionAtLeast(6, 1)) return;
g.DrawEllipse(pen, centerX - radius, centerY - radius, radius + radius, radius + radius);
}
//-----------------------------------------------------------------------------------
@@ -364,7 +441,7 @@ public static void DrawCircle(this Graphics g, Pen pen, float centerX, float cen
///
public static void FillCircle(this Graphics g, Brush brush, float centerX, float centerY, float radius)
{
- if (!OperatingSystem.IsWindows()) return;
+ if (!OperatingSystem.IsWindowsVersionAtLeast(6, 1)) return;
g.FillEllipse(brush, centerX - radius, centerY - radius, radius + radius, radius + radius);
}
//-----------------------------------------------------------------------------------
@@ -475,11 +552,13 @@ public static void SaveToFile(this List list, string filePath)
{
try
{
- FileStreamOptions streamOptions = new();
- streamOptions.Access = FileAccess.ReadWrite;
- streamOptions.Share = FileShare.ReadWrite;
- streamOptions.Mode = FileMode.Create;
- streamOptions.Options = FileOptions.RandomAccess;
+ FileStreamOptions streamOptions = new()
+ {
+ Access = FileAccess.ReadWrite,
+ Share = FileShare.ReadWrite,
+ Mode = FileMode.Create,
+ Options = FileOptions.RandomAccess
+ };
using StreamWriter file = new(filePath, streamOptions);
for (int n = 0; n < list.Count; n++)
if (list[n] != null)
@@ -544,7 +623,7 @@ public static int GetIndex(this List list, T value)
{
try
{
- return list.FindIndex(a => a.Equals(value));
+ return list.FindIndex(a => a != null && a.Equals(value));
// If the item is not found, it will return -1
}
catch (Exception)
diff --git a/MsmhToolsClass/MsmhToolsClass/ExternLibs/LibSodium.cs b/MsmhToolsClass/MsmhToolsClass/ExternLibs/LibSodium.cs
new file mode 100644
index 0000000..1d646a3
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/ExternLibs/LibSodium.cs
@@ -0,0 +1,351 @@
+using System.Runtime.InteropServices;
+
+namespace MsmhToolsClass.ExternLibs;
+
+#pragma warning disable CA1401 // P/Invokes should not be visible
+internal class LibSodium
+{
+ private const string DllName = "libsodium";
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_core_hchacha20(byte[] Hash, byte[] Data, byte[] Key, byte[] Nonce);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_curve25519xchacha20poly1305_beforenm(byte[] SharedKey, byte[] PublicKey, byte[] SecretKey);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_curve25519xchacha20poly1305_easy_afternm(byte[] CipherText, byte[] Message, long MessageLength, byte[] Nonce, byte[] Key);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_curve25519xchacha20poly1305_easy(byte[] CipherText, byte[] Message, long MessageLength, byte[] Nonce, byte[] PublicKey, byte[] SecretKey);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_curve25519xchacha20poly1305_open_easy_afternm(byte[] PlainText, byte[] CipherText, long CipherTextLength, byte[] Nonce, byte[] Key);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_curve25519xchacha20poly1305_open_easy(byte[] PlainText, byte[] CipherText, long CipherTextLength, byte[] Nonce, byte[] PublicKey, byte[] SecretKey);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_easy_afternm(byte[] CipherText, byte[] Message, long MessageLength, byte[] Nonce, byte[] Key);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_easy(byte[] CipherText, byte[] Message, long MessageLength, byte[] Nonce, byte[] PublicKey, byte[] SecretKey);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_open_easy_afternm(byte[] PlainText, byte[] CipherText, long CipherTextLength, byte[] Nonce, byte[] Key);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_open_easy(byte[] PlainText, byte[] CipherText, long CipherTextLength, byte[] Nonce, byte[] PublicKey, byte[] SecretKey);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_beforenm(byte[] SharedKey, byte[] PublicKey, byte[] SecretKey);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void sodium_init();
+
+ //randombytes_buf
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void randombytes_buf(byte[] Buffer, int Size);
+
+ //randombytes_uniform
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int randombytes_uniform(int UpperBound);
+
+ //sodium_increment
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void sodium_increment(byte[] Buffer, long Length);
+
+ //sodium_compare
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int sodium_compare(byte[] A, byte[] B, long Length);
+
+ //sodium_version_string
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr sodium_version_string();
+
+ //crypto_hash
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_hash(byte[] Buffer, byte[] Message, long Length);
+
+ //crypto_hash_sha512
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_hash_sha512(byte[] Buffer, byte[] Message, long Length);
+
+ //crypto_hash_sha256
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_hash_sha256(byte[] Buffer, byte[] Message, long Length);
+
+ //crypto_generichash
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_generichash(byte[] Buffer, int BufferLength, byte[] Message, long MessageLength, byte[] Key, int KeyLength);
+
+ //crypto_generichash_blake2b_salt_personal
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_generichash_blake2b_salt_personal(byte[] Buffer, int BufferLength, byte[] Message, long MessageLength, byte[] Key, int KeyLength, byte[] Salt, byte[] Personal);
+
+ //crypto_onetimeauth
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_onetimeauth(byte[] Buffer, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_onetimeauth_verify
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_onetimeauth_verify(byte[] Signature, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_pwhash_str
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_pwhash_str(byte[] Buffer, byte[] Password, long PasswordLen, long OpsLimit, int MemLimit);
+
+ //crypto_pwhash_str_verify
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_pwhash_str_verify(byte[] Buffer, byte[] Password, long PassLength);
+
+ //crypto_pwhash
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_pwhash(byte[] Buffer, long BufferLen, byte[] Password, long PasswordLen, byte[] Salt, long OpsLimit, int MemLimit, int Alg);
+
+ //crypto_pwhash_scryptsalsa208sha256_str
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_pwhash_scryptsalsa208sha256_str(byte[] Buffer, byte[] Password, long PasswordLen, long OpsLimit, int MemLimit);
+
+ //crypto_pwhash_scryptsalsa208sha256
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_pwhash_scryptsalsa208sha256(byte[] Buffer, long BufferLen, byte[] Password, long PasswordLen, byte[] Salt, long OpsLimit, int MemLimit);
+
+ //crypto_pwhash_scryptsalsa208sha256_str_verify
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_pwhash_scryptsalsa208sha256_str_verify(byte[] Buffer, byte[] Password, long PassLength);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_pwhash_str_needs_rehash(byte[] Buffer, long OpsLimit, int MemLimit);
+
+ //crypto_sign_keypair
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_sign_keypair(byte[] PublicKey, byte[] SecretKey);
+
+ //crypto_sign_seed_keypair
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_sign_seed_keypair(byte[] PublicKey, byte[] SecretKey, byte[] Seed);
+
+ //crypto_sign
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_sign(byte[] Buffer, ref long BufferLength, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_sign_open
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_sign_open(byte[] Buffer, ref long BufferLength, byte[] SignedMessage, long SignedMessageLength, byte[] Key);
+
+ //crypto_sign_detached
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_sign_detached(byte[] Signature, ref long SignatureLength, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_sign_verify_detached
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_sign_verify_detached(byte[] Signature, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_sign_ed25519_sk_to_seed
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_sign_ed25519_sk_to_seed(byte[] Seed, byte[] SecretKey);
+
+ //crypto_sign_ed25519_sk_to_pk
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_sign_ed25519_sk_to_pk(byte[] PublicKey, byte[] SecretKey);
+
+ //crypto_sign_ed25519_pk_to_curve25519
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_sign_ed25519_pk_to_curve25519(byte[] Curve25519Pk, byte[] Ed25519Pk);
+
+ //crypto_sign_ed25519_sk_to_curve25519
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_sign_ed25519_sk_to_curve25519(byte[] Curve25519Sk, byte[] Ed25519Sk);
+
+ //crypto_box_keypair
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_keypair(byte[] PublicKey, byte[] SecretKey);
+
+ //crypto_box_seed_keypair
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_seed_keypair(byte[] PublicKey, byte[] SecretKey, byte[] Seed);
+
+ //crypto_box_open_easy
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_detached(byte[] Cipher, byte[] Mac, byte[] Message, long MessageLength, byte[] Nonce, byte[] Pk, byte[] Sk);
+
+ //crypto_box_open_detached
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_open_detached(byte[] Buffer, byte[] CipherText, byte[] Mac, long CipherTextLength, byte[] Nonce, byte[] Pk, byte[] Sk);
+
+ //crypto_box_seedbytes
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_seedbytes();
+
+ //crypto_scalarmult_bytes
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_scalarmult_bytes();
+
+ //crypto_scalarmult_scalarbytes
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_scalarmult_scalarbytes();
+
+ //crypto_scalarmult_primitive
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern byte crypto_scalarmult_primitive();
+
+ //crypto_scalarmult_base
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_scalarmult_base(byte[] PublicKey, byte[] SecretKey);
+
+ //crypto_scalarmult
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_scalarmult(byte[] SharedKey, byte[] SecretKey, byte[] PublicKey);
+
+ //crypto_box_seal
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_seal(byte[] Buffer, byte[] Message, long MessageLength, byte[] Pk);
+
+ //crypto_box_seal_open
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_box_seal_open(byte[] Buffer, byte[] CipherText, long CipherTextLength, byte[] Pk, byte[] Sk);
+
+ //crypto_secretbox_easy
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_secretbox_easy(byte[] Buffer, byte[] Message, long MessageLength, byte[] Nonce, byte[] Key);
+
+ //crypto_secretbox_open_easy
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_secretbox_open_easy(byte[] Buffer, byte[] CipherText, long CipherTextLength, byte[] Nonce, byte[] Key);
+
+ //crypto_secretbox_detached
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_secretbox_detached(byte[] Cipher, byte[] Mac, byte[] Message, long MessageLength, byte[] Nonce, byte[] Key);
+
+ //crypto_secretbox_open_detached
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_secretbox_open_detached(byte[] Buffer, byte[] CipherText, byte[] Mac, long CipherTextLength, byte[] Nonce, byte[] Key);
+
+ //crypto_auth
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_auth(byte[] Buffer, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_auth_verify
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_auth_verify(byte[] Signature, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_auth_hmacsha256
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_auth_hmacsha256(byte[] Buffer, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_auth_hmacsha256_verify
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_auth_hmacsha256_verify(byte[] Signature, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_auth_hmacsha512
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_auth_hmacsha512(byte[] Signature, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_auth_hmacsha512_verify
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_auth_hmacsha512_verify(byte[] Signature, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_shorthash
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_shorthash(byte[] Buffer, byte[] Message, long MessageLength, byte[] Key);
+
+ //crypto_stream_xor
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_stream_xor(byte[] Buffer, byte[] Message, long MessageLength, byte[] Nonce, byte[] Key);
+
+ //crypto_stream_chacha20_xor
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_stream_chacha20_xor(byte[] Buffer, byte[] Message, long MessageLength, byte[] Nonce, byte[] Key);
+
+ //sodium_bin2hex
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr sodium_bin2hex(byte[] Hex, int HexMaxlen, byte[] Bin, int BinLen);
+
+ //sodium_hex2bin
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+ internal static extern int sodium_hex2bin(IntPtr Bin, int BinMaxlen, string Hex, int HexLen, string Ignore, out int BinLen, string HexEnd);
+
+ //sodium_bin2base64
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr sodium_bin2base64(byte[] B64, int B64Maxlen, byte[] Bin, int BinLen, int Variant);
+
+ //sodium_base642bin
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+ internal static extern int sodium_base642bin(IntPtr Bin, int BinMaxlen, string B64, int B64Len, string Ignore, out int BinLen, out char B64End, int Variant);
+
+ //sodium_base64_encoded_len
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int sodium_base64_encoded_len(int BinLen, int Variant);
+
+ //crypto_aead_chacha20poly1305_ietf_encrypt
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_aead_chacha20poly1305_ietf_encrypt(IntPtr Cipher, out long CipherLength, byte[] Message, long MessageLength, byte[] AdditionalData, long AdditionalDataLength, byte[] NSEC, byte[] Nonce, byte[] Key);
+
+ //crypto_aead_chacha20poly1305_ietf_decrypt
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_aead_chacha20poly1305_ietf_decrypt(IntPtr Message, out long MessageLength, byte[] NSEC, byte[] Cipher, long CipherLength, byte[] AdditionalData, long AdditionalDataLength, byte[] Nonce, byte[] Key);
+
+ //crypto_aead_chacha20poly1305_encrypt
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_aead_chacha20poly1305_encrypt(IntPtr Cipher, out long CipherLength, byte[] Message, long MessageLength, byte[] AdditionalData, long AdditionalDataLength, byte[] NSEC, byte[] Nonce, byte[] Key);
+
+ //crypto_aead_chacha20poly1305_decrypt
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_aead_chacha20poly1305_decrypt(IntPtr Message, out long MessageLength, byte[] NSEC, byte[] Cipher, long CipherLength, byte[] AdditionalData, long AdditionalDataLength, byte[] Nonce, byte[] Key);
+
+ //crypto_aead_xchacha20poly1305_ietf_encrypt
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_aead_xchacha20poly1305_ietf_encrypt(IntPtr Cipher, out long CipherLength, byte[] Message, long MessageLength, byte[] AdditionalData, long AdditionalDataLength, byte[] NSEC, byte[] Nonce, byte[] Key);
+
+ //crypto_aead_xchacha20poly1305_ietf_decrypt
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_aead_xchacha20poly1305_ietf_decrypt(IntPtr Message, out long MessageLength, byte[] NSEC, byte[] Cipher, long CipherLength, byte[] AdditionalData, long AdditionalDataLength, byte[] Nonce, byte[] Key);
+
+ //crypto_aead_aes256gcm_is_available
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_aead_aes256gcm_is_available();
+
+ //crypto_aead_aes256gcm_encrypt
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_aead_aes256gcm_encrypt(IntPtr Cipher, out long CipherLength, byte[] Message, long MessageLength, byte[] AdditionalData, long AdditionalDataLength, byte[] NSEC, byte[] Nonce, byte[] Key);
+
+ //crypto_aead_aes256gcm_decrypt
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_aead_aes256gcm_decrypt(IntPtr Message, out long MessageLength, byte[] NSEC, byte[] Cipher, long CipherLength, byte[] AdditionalData, long AdditionalDataLength, byte[] Nonce, byte[] Key);
+
+ //crypto_generichash_init
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_generichash_init(IntPtr State, byte[] Key, int KeySize, int HashSize);
+
+ //crypto_generichash_update
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_generichash_update(IntPtr State, byte[] Message, long MessageLength);
+
+ //crypto_generichash_final
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_generichash_final(IntPtr State, byte[] Buffer, int BufferLength);
+
+ //crypto_generichash_state
+ [StructLayout(LayoutKind.Sequential, Size = 384)]
+ internal struct HashState
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+ public ulong[] h;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
+ public ulong[] t;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
+ public ulong[] f;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
+ public byte[] buf;
+ public uint buflen;
+ public byte last_node;
+ }
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_stream_xchacha20(byte[] Buffer, int BufferLength, byte[] Nonce, byte[] Key);
+
+ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int crypto_stream_xchacha20_xor(byte[] Buffer, byte[] Message, long MessageLength, byte[] Nonce, byte[] Key);
+}
+#pragma warning restore CA1401 // P/Invokes should not be visible
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/FileDirectory.cs b/MsmhToolsClass/MsmhToolsClass/FileDirectory.cs
index a204cf9..8ae439c 100644
--- a/MsmhToolsClass/MsmhToolsClass/FileDirectory.cs
+++ b/MsmhToolsClass/MsmhToolsClass/FileDirectory.cs
@@ -6,7 +6,43 @@ namespace MsmhToolsClass;
public class FileDirectory
{
//-----------------------------------------------------------------------------------
- public static async Task MoveDirectory(string sourceDir, string destDir, bool overWrite, CancellationToken token)
+ public static async Task> GetAllFilesAsync(string dirPath, CancellationToken ct = default)
+ {
+ return await Task.Run(async () =>
+ {
+ List paths = new();
+
+ try
+ {
+ if (!Directory.Exists(dirPath)) return paths;
+ DirectoryInfo rootDirInfo = new(dirPath);
+
+ FileInfo[] fileInfos = rootDirInfo.GetFiles();
+ for (int n = 0; n < fileInfos.Length; n++)
+ {
+ if (ct.IsCancellationRequested) break;
+ FileInfo fileInfo = fileInfos[n];
+ paths.Add(fileInfo.FullName);
+ }
+
+ DirectoryInfo[] dirInfos = rootDirInfo.GetDirectories();
+ for (int n = 0; n < dirInfos.Length; n++)
+ {
+ if (ct.IsCancellationRequested) break;
+ DirectoryInfo dirInfo = dirInfos[n];
+ paths.AddRange(await GetAllFilesAsync(dirInfo.FullName, ct));
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("FileDirectory GetAllFiles: " + ex.Message);
+ }
+
+ return paths;
+ }, ct);
+ }
+ //-----------------------------------------------------------------------------------
+ public static async Task MoveDirectoryAsync(string sourceDir, string destDir, bool overWrite, CancellationToken ct)
{
await Task.Run(async () =>
{
@@ -19,24 +55,26 @@ await Task.Run(async () =>
FileInfo[] fileInfos = rootDirInfo.GetFiles();
for (int n = 0; n < fileInfos.Length; n++)
{
+ if (ct.IsCancellationRequested) break;
FileInfo fileInfo = fileInfos[n];
fileInfo.MoveTo(Path.GetFullPath(Path.Combine(destDir, fileInfo.Name)), overWrite);
}
DirectoryInfo[] dirInfos = rootDirInfo.GetDirectories();
- for (int n2 = 0; n2 < dirInfos.Length; n2++)
+ for (int n = 0; n < dirInfos.Length; n++)
{
- DirectoryInfo dirInfo = dirInfos[n2];
- await MoveDirectory(dirInfo.FullName, Path.GetFullPath(Path.Combine(destDir, dirInfo.Name)), overWrite, token);
+ if (ct.IsCancellationRequested) break;
+ DirectoryInfo dirInfo = dirInfos[n];
+ await MoveDirectoryAsync(dirInfo.FullName, Path.GetFullPath(Path.Combine(destDir, dirInfo.Name)), overWrite, ct);
}
if (Directory.Exists(sourceDir)) Directory.Delete(sourceDir, false);
}
catch (Exception ex)
{
- Debug.WriteLine("MoveDirectory: " + ex.Message);
+ Debug.WriteLine("FileDirectory MoveDirectory: " + ex.Message);
}
- }, token);
+ }, ct);
}
//-----------------------------------------------------------------------------------
public static bool IsPathTooLong(string path)
@@ -107,8 +145,14 @@ public static bool IsDirectoryEmpty(string dirPath)
///
public static void CreateEmptyFile(string filePath)
{
- if (!File.Exists(filePath))
- File.Create(filePath).Dispose();
+ try
+ {
+ if (!File.Exists(filePath)) File.Create(filePath).Dispose();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("FileDirectory CreateEmptyFile: " + ex.Message);
+ }
}
//-----------------------------------------------------------------------------------
///
@@ -116,8 +160,14 @@ public static void CreateEmptyFile(string filePath)
///
public static void CreateEmptyDirectory(string directoryPath)
{
- if (!Directory.Exists(directoryPath))
- Directory.CreateDirectory(directoryPath);
+ try
+ {
+ if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("FileDirectory CreateEmptyDirectory: " + ex.Message);
+ }
}
//-----------------------------------------------------------------------------------
public static bool CompareByLength(string path1, string path2)
@@ -193,7 +243,7 @@ public static void AppendTextLine(string filePath, string textToAppend, Encoding
}
catch (Exception ex)
{
- Debug.WriteLine(ex.Message);
+ Debug.WriteLine("FileDirectory AppendTextLine: " + ex.Message);
}
}
//-----------------------------------------------------------------------------------
@@ -209,7 +259,7 @@ public static async Task AppendTextLineAsync(string filePath, string textToAppen
}
catch (Exception ex)
{
- Debug.WriteLine("AppendTextLineAsync: " + ex.Message);
+ Debug.WriteLine("FileDirectory AppendTextLineAsync: " + ex.Message);
}
}
//-----------------------------------------------------------------------------------
@@ -223,7 +273,7 @@ public static void AppendText(string filePath, string textToAppend, Encoding enc
}
catch (Exception ex)
{
- Debug.WriteLine("AppendText: " + ex.Message);
+ Debug.WriteLine("FileDirectory AppendText: " + ex.Message);
}
}
//-----------------------------------------------------------------------------------
@@ -237,7 +287,7 @@ public static async Task AppendTextAsync(string filePath, string textToAppend, E
}
catch (Exception ex)
{
- Debug.WriteLine("AppendTextAsync: " + ex.Message);
+ Debug.WriteLine("FileDirectory AppendTextAsync: " + ex.Message);
}
}
//-----------------------------------------------------------------------------------
@@ -253,7 +303,7 @@ public static void WriteAllText(string filePath, string fileContent, Encoding en
}
catch (Exception ex)
{
- Debug.WriteLine("WriteAllText: " + ex.Message);
+ Debug.WriteLine("FileDirectory WriteAllText: " + ex.Message);
}
}
//-----------------------------------------------------------------------------------
@@ -269,7 +319,7 @@ public static async Task WriteAllTextAsync(string filePath, string fileContent,
}
catch (Exception ex)
{
- Debug.WriteLine("WriteAllTextAsync: " + ex.Message);
+ Debug.WriteLine("FileDirectory WriteAllTextAsync: " + ex.Message);
}
}
//-----------------------------------------------------------------------------------
@@ -306,119 +356,35 @@ public static bool IsFileLocked(string fileNameOrPath)
}
catch (Exception ex)
{
- Debug.WriteLine("IsFileLocked: " + ex.Message);
+ Debug.WriteLine("FileDirectory IsFileLocked: " + ex.Message);
return true;
}
}
//-----------------------------------------------------------------------------------
public static List? FindFilesByPartialName(string partialName, string dirPath)
- {
- if (Directory.Exists(dirPath))
- {
- DirectoryInfo hdDirectoryInWhichToSearch = new(dirPath);
- FileInfo[] filesInDir = hdDirectoryInWhichToSearch.GetFiles("*" + partialName + "*.*");
- List list = new();
- foreach (FileInfo foundFile in filesInDir)
- {
- string fullName = foundFile.FullName;
- list.Add(fullName);
- }
- return list;
- }
- Console.WriteLine("Directory Not Exist: " + dirPath);
- return null;
- }
- //-----------------------------------------------------------------------------------
- // ===== Old Methods ================================================================
- public static byte[] ReadAllBytes(MemoryStream memoryStream)
- {
- return memoryStream.ToArray();
- }
- //-----------------------------------------------------------------------------------
- public static byte[]? ReadAllBytes(string filePath)
{
try
{
- using FileStream fsSource = new(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
- // Read the source file into a byte array.
- byte[] bytes = new byte[fsSource.Length];
- int numBytesToRead = (int)fsSource.Length;
- int numBytesRead = 0;
- while (numBytesToRead > 0)
+ if (Directory.Exists(dirPath))
{
- // Read may return anything from 0 to numBytesToRead.
- int n = fsSource.Read(bytes, numBytesRead, numBytesToRead);
- // Break when the end of the file is reached.
- if (n == 0) break;
- numBytesRead += n;
- numBytesToRead -= n;
- }
- numBytesToRead = bytes.Length;
- return bytes;
- }
- catch (FileNotFoundException ioEx)
- {
- Console.WriteLine(ioEx.Message);
- return null;
- }
- }
- //-----------------------------------------------------------------------------------
- public static async Task ReadAllBytesAsync(string filePath)
- {
- try
- {
- using FileStream fsSource = new(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
- // Read the source file into a byte array.
- byte[] bytes = new byte[fsSource.Length];
- int numBytesToRead = (int)fsSource.Length;
- int numBytesRead = 0;
- while (numBytesToRead > 0)
- {
- // Read may return anything from 0 to numBytesToRead.
- int n = await fsSource.ReadAsync(bytes.AsMemory(numBytesRead, numBytesToRead));
- // Break when the end of the file is reached.
- if (n == 0) break;
- numBytesRead += n;
- numBytesToRead -= n;
+ DirectoryInfo hdDirectoryInWhichToSearch = new(dirPath);
+ FileInfo[] filesInDir = hdDirectoryInWhichToSearch.GetFiles("*" + partialName + "*.*");
+ List list = new();
+ foreach (FileInfo foundFile in filesInDir)
+ {
+ string fullName = foundFile.FullName;
+ list.Add(fullName);
+ }
+ return list;
}
- numBytesToRead = bytes.Length;
- return bytes;
+ Console.WriteLine("FileDirectory Directory Not Exist: " + dirPath);
}
- catch (FileNotFoundException ioEx)
- {
- Console.WriteLine(ioEx.Message);
- return null;
- }
- }
- //-----------------------------------------------------------------------------------
- public static void WriteAllBytes(string filePath, byte[] bytes)
- {
- try
- {
- int numBytesToRead = bytes.Length;
- // Write the byte array to the other FileStream.
- using FileStream fsNew = new(filePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
- fsNew.Write(bytes, 0, numBytesToRead);
- }
- catch (FileNotFoundException ioEx)
- {
- Console.WriteLine(ioEx.Message);
- }
- }
- //-----------------------------------------------------------------------------------
- public static async Task WriteAllBytesAsync(string filePath, byte[] bytes)
- {
- try
- {
- int numBytesToRead = bytes.Length;
- // Write the byte array to the other FileStream.
- using FileStream fsNew = new(filePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
- await fsNew.WriteAsync(bytes.AsMemory(0, numBytesToRead));
- }
- catch (FileNotFoundException ioEx)
+ catch (Exception ex)
{
- Console.WriteLine(ioEx.Message);
+ Debug.WriteLine("FileDirectory FindFilesByPartialName: " + ex.Message);
}
+
+ return null;
}
//-----------------------------------------------------------------------------------
}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/HttpRequest.cs b/MsmhToolsClass/MsmhToolsClass/HttpRequest.cs
new file mode 100644
index 0000000..ea5907d
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/HttpRequest.cs
@@ -0,0 +1,586 @@
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Net;
+using System.Net.Http.Headers;
+using System.Net.Security;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+
+namespace MsmhToolsClass;
+
+public class HttpRequestResult
+{
+ public bool IsSuccess { get; internal set; } = false;
+ public HttpMethod Method { get; internal set; } = HttpMethod.Get;
+ public string RawURL { get; internal set; } = string.Empty;
+ public Uri? URI { get; internal set; }
+ public string ProtocolVersion { get; internal set; } = string.Empty;
+ public string ContentType { get; internal set; } = string.Empty;
+ public string UserAgent { get; internal set; } = string.Empty;
+ public NameValueCollection Headers { get; internal set; } = new(StringComparer.InvariantCultureIgnoreCase);
+ public byte[] PayLoad { get; internal set; } = Array.Empty();
+
+ public override string ToString()
+ {
+ string result = string.Empty;
+
+ try
+ {
+ result += $"{nameof(IsSuccess)}: {IsSuccess}\r\n";
+ result += $"{nameof(Method)}: {Method}\r\n";
+ result += $"{nameof(RawURL)}: {RawURL}\r\n";
+ if (URI != null) result += $"{nameof(URI)}: {URI}\r\n";
+ result += $"{nameof(ProtocolVersion)}: {ProtocolVersion}\r\n";
+ result += $"{nameof(ContentType)}: {ContentType}\r\n";
+ result += $"{nameof(UserAgent)}: {UserAgent}\r\n";
+ if (Headers.Count > 0) result += $"{nameof(Headers)}:\r\n";
+ for (int n = 0; n < Headers.Count; n++)
+ {
+ string? key = Headers.GetKey(n);
+ string? val = Headers.Get(n);
+ if (string.IsNullOrEmpty(key)) continue;
+ if (string.IsNullOrEmpty(val)) continue;
+ result += $"{key}: {val}\r\n";
+ }
+ if (PayLoad.Length > 0) result += $"{nameof(PayLoad)}: {BitConverter.ToString(PayLoad)}";
+ if (result.EndsWith("\r\n")) result = result.TrimEnd("\r\n");
+ }
+ catch (Exception) { }
+
+ return result;
+ }
+}
+
+
+public class HttpRequestResponse
+{
+ public bool IsSuccess { get; internal set; } = false;
+ public string ContentEncoding { get; internal set; } = string.Empty;
+ public string ContentType { get; internal set; } = string.Empty;
+ public long ContentLength { get; internal set; } = 0;
+ public string ProtocolVersion { get; internal set; } = string.Empty;
+ public int StatusCode { get; internal set; } = (int)HttpStatusCode.NoContent;
+ public string StatusDescription { get; internal set; } = HttpStatusCode.NoContent.ToString();
+ public NameValueCollection Headers { get; internal set; } = new(StringComparer.InvariantCultureIgnoreCase);
+ public byte[] Data { get; internal set; } = Array.Empty();
+
+ public override string ToString()
+ {
+ string result = string.Empty;
+
+ try
+ {
+ result += $"{nameof(IsSuccess)}: {IsSuccess}\r\n";
+ result += $"{nameof(ContentEncoding)}: {ContentEncoding}\r\n";
+ result += $"{nameof(ContentType)}: {ContentType}\r\n";
+ result += $"{nameof(ContentLength)}: {ContentLength}\r\n";
+ result += $"{nameof(ProtocolVersion)}: {ProtocolVersion}\r\n";
+ result += $"{nameof(StatusCode)}: {StatusCode}\r\n";
+ result += $"{nameof(StatusDescription)}: {StatusDescription}\r\n";
+ if (Headers.Count > 0)
+ result += $"{nameof(Headers)}:\r\n";
+ for (int n = 0; n < Headers.Count; n++)
+ {
+ string? key = Headers.GetKey(n);
+ string? val = Headers.Get(n);
+ if (string.IsNullOrEmpty(key)) continue;
+ if (string.IsNullOrEmpty(val)) continue;
+ result += $"{key}: {val}\r\n";
+ }
+ result += $"{nameof(Data)}: {Convert.ToHexString(Data)}";
+ }
+ catch (Exception) { }
+
+ return result;
+ }
+}
+
+///
+/// Authorization Header Options
+///
+public class HttpRequestAuthorizationHeader
+{
+ ///
+ /// The username to use in the authorization header, if any
+ ///
+ public string User { get; set; } = string.Empty;
+
+ ///
+ /// The password to use in the authorization header, if any
+ ///
+ public string Password { get; set; } = string.Empty;
+
+ ///
+ /// The bearer token to use in the authorization header, if any
+ ///
+ public string BearerToken { get; set; } = string.Empty;
+
+ ///
+ /// Enable to encode credentials in the authorization header
+ ///
+ public bool EncodeCredentials { get; set; } = true;
+
+ public override string ToString()
+ {
+ string result = string.Empty;
+
+ try
+ {
+ result += $"{nameof(User)}: {User}\r\n";
+ result += $"{nameof(Password)}: {Password}\r\n";
+ result += $"{nameof(BearerToken)}: {BearerToken}\r\n";
+ result += $"{nameof(EncodeCredentials)}: {EncodeCredentials}";
+ }
+ catch (Exception) { }
+
+ return result;
+ }
+}
+
+public class HttpRequest
+{
+ public CancellationToken CT { get; set; } = default;
+ public bool IsForHttpProxy { get; set; } = false;
+ public Uri? URI { get; set; }
+ public byte[] DataToSend { get; set; } = Array.Empty();
+ public HttpMethod Method { get; set; } = HttpMethod.Get;
+ public string ContentType { get; set; } = string.Empty;
+ ///
+ /// Default is "Other"
+ ///
+ public string UserAgent { get; set; } = "Other";
+ ///
+ /// User-Agent Will Be Add Automatically (Don't Add To Header)
+ ///
+ public NameValueCollection Headers { get; set; } = new(StringComparer.InvariantCultureIgnoreCase);
+ public bool AllowAutoRedirect { get; set; } = true;
+ public bool AllowInsecure { get; set; } = true;
+ public int TimeoutMS { get; set; } = 30000;
+ public string? ProxyScheme { get; set; }
+ public string? ProxyUser { get; set; }
+ public string? ProxyPass { get; set; }
+ public HttpRequestAuthorizationHeader Authorization { get; set; } = new();
+ public X509Certificate2? Certificate { get; set; }
+
+ public override string ToString()
+ {
+ string result = string.Empty;
+
+ try
+ {
+ result += $"{nameof(IsForHttpProxy)}: {IsForHttpProxy}\r\n";
+ if (URI != null) result += $"{nameof(URI)}: {URI}\r\n";
+ if (DataToSend.Length > 0) result += $"{nameof(DataToSend)}: {Convert.ToHexString(DataToSend)}\r\n";
+ result += $"{nameof(Method)}: {Method}\r\n";
+ result += $"{nameof(ContentType)}: {ContentType}\r\n";
+ result += $"{nameof(UserAgent)}: {UserAgent}\r\n";
+ if (Headers.Count > 0) result += $"{nameof(Headers)}:\r\n";
+ for (int n = 0; n < Headers.Count; n++)
+ {
+ string? key = Headers.GetKey(n);
+ string? val = Headers.Get(n);
+ if (string.IsNullOrEmpty(key)) continue;
+ if (string.IsNullOrEmpty(val)) continue;
+ result += $"{key}: {val}\r\n";
+ }
+ result += $"{nameof(AllowAutoRedirect)}: {AllowAutoRedirect}\r\n";
+ result += $"{nameof(AllowInsecure)}: {AllowInsecure}\r\n";
+ result += $"{nameof(TimeoutMS)}: {TimeoutMS}\r\n";
+ if (!string.IsNullOrEmpty(ProxyScheme))
+ result += $"{nameof(ProxyScheme)}: {ProxyScheme}\r\n";
+ if (!string.IsNullOrEmpty(ProxyUser))
+ result += $"{nameof(ProxyUser)}: {ProxyUser}\r\n";
+ if (!string.IsNullOrEmpty(ProxyPass))
+ result += $"{nameof(ProxyPass)}: {ProxyPass}\r\n";
+ result += $"{nameof(Authorization)}:\r\n";
+ result += Authorization.ToString() + "\r\n";
+ if (Certificate != null)
+ result += $"{nameof(Certificate)}: {Certificate.SubjectName}\r\n";
+ if (result.EndsWith("\r\n")) result = result.TrimEnd("\r\n");
+ }
+ catch (Exception) { }
+
+ return result;
+ }
+
+ public static HttpRequestResult Read(byte[] buffer)
+ {
+ HttpRequestResult hrr = new();
+
+ try
+ {
+ string str = Encoding.UTF8.GetString(buffer);
+ str.ReplaceLineEndings();
+
+ string[] headers = str.Split(Environment.NewLine); // new string[] { "\r\n", "\n" }
+
+ string scheme = string.Empty, host = string.Empty, path = string.Empty;
+ int port = -1;
+ string contentType = string.Empty;
+ int contentLength = 0;
+
+ for (int n = 0; n < headers.Length; n++)
+ {
+ if (n == 0)
+ {
+ // First Line (Method, raw URL, ListenerProtocol/Version)
+ string[] requestLine = headers[n].Trim().Trim('\0').Split(' ', StringSplitOptions.RemoveEmptyEntries);
+
+ if (requestLine.Length >= 3)
+ {
+ // 3 Parts Means Success
+ hrr.IsSuccess = true;
+
+ // Get Method
+ hrr.Method = NetworkTool.ParseHttpMethod(requestLine[0]);
+
+ // Get Raw URL
+ string rawUrl = requestLine[1];
+ hrr.RawURL = rawUrl;
+
+ // Get URI (For HTTP & HTTPS Proxies)
+ // For HTTP: Scheme: Yes, Port: No
+ // For HTTPS: Scheme: No, Port: Yes
+ NetworkTool.GetUrlDetails(rawUrl, -1, out scheme, out host, out _, out _, out port, out path, out _);
+ if (scheme.ToLower().Equals("http://") && port == -1) port = 80;
+ if (string.IsNullOrEmpty(scheme) && port != -1) scheme = "https://";
+ if (string.IsNullOrEmpty(scheme) && port == -1) path = rawUrl; // e.g. DoH Request
+
+ // If Host Is IPv6
+ if (host.StartsWith('[')) host = host.TrimStart('[');
+ if (host.EndsWith(']')) host = host.TrimEnd(']');
+
+ // Get ProtocolVersion
+ hrr.ProtocolVersion = requestLine[2];
+ }
+ }
+ else
+ {
+ if (headers[n].Contains(':'))
+ {
+ string[] headerLine = headers[n].Split(':'); // Shouldn't RemoveEmptyEntries [::] IPv6
+ string key = string.Empty;
+ string val = string.Empty;
+ for (int i = 0; i < headerLine.Length; i++)
+ {
+ string kvs = headerLine[i];
+ if (i == 0) key = kvs;
+ else val += $"{kvs}:";
+ }
+ if (val.EndsWith(':')) val = val.TrimEnd(':');
+
+ key = key.Trim();
+ val = val.Trim();
+
+ if (string.IsNullOrEmpty(key)) continue;
+ if (string.IsNullOrEmpty(val)) continue;
+ string keyEval = key.ToLower();
+
+ // Get Host
+ if (keyEval.Equals("host") && string.IsNullOrEmpty(host))
+ {
+ host = val;
+ if (host.Contains(':'))
+ {
+ NetworkTool.GetUrlDetails(host, -1, out _, out string host2, out _, out _, out int port2, out _, out _);
+
+ // If Host Is IPv6
+ if (host2.StartsWith('[')) host2 = host2.TrimStart('[');
+ if (host2.EndsWith(']')) host2 = host2.TrimEnd(']');
+
+ host = host2;
+ if (port == -1) port = port2;
+ }
+ }
+
+ // Get ContentType
+ if (keyEval.Equals("accept"))
+ {
+ if (string.IsNullOrEmpty(contentType)) contentType = val;
+ }
+
+ if (keyEval.Equals("content-type"))
+ {
+ contentType = val;
+ }
+
+ // Get ContentLength
+ if (keyEval.Equals("content-length"))
+ {
+ bool isIntSuccess = int.TryParse(val, out int cl);
+ if (isIntSuccess) contentLength = cl;
+ }
+
+ // Get UserAgent
+ if (keyEval.Equals("user-agent"))
+ hrr.UserAgent = val;
+ else hrr.Headers.AddAndUpdate(key, val); // Get Other Headers
+ }
+ }
+ }
+
+ // Set ContentType
+ hrr.ContentType = contentType;
+
+ // Get PayLoad
+ if (contentLength > 0 && buffer.Length > contentLength && hrr.Method != HttpMethod.Get && hrr.Method != HttpMethod.Head)
+ {
+ int startIndex = buffer.Length - contentLength;
+ hrr.PayLoad = buffer[startIndex..];
+ }
+
+ // Generate URI
+ if (string.IsNullOrEmpty(scheme)) scheme = "https://";
+ if (!string.IsNullOrEmpty(host) && port != -1)
+ {
+ UriBuilder uriBuilder = new()
+ {
+ Scheme = scheme.ToLower(),
+ Host = host.ToLower(),
+ Port = port,
+ Path = path
+ };
+ hrr.URI = uriBuilder.Uri;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("HttpRequest Read: " + ex.Message);
+ }
+
+ return hrr;
+ }
+
+ public static async Task SendAsync(HttpRequest hr)
+ {
+ HttpRequestResponse hrr = new();
+
+ Task task = Task.Run(async () =>
+ {
+ try
+ {
+ static bool callback(object sender, X509Certificate? cert, X509Chain? chain, SslPolicyErrors sslPolicyErrors) => true;
+ SslProtocols protocols = SslProtocols.None | SslProtocols.Tls12 | SslProtocols.Tls13;
+
+ HttpClientHandler handler = new()
+ {
+ SslProtocols = protocols,
+ AllowAutoRedirect = hr.AllowAutoRedirect
+ };
+
+ // Ignore Cert Check
+ if (hr.AllowInsecure)
+ {
+ handler.ClientCertificateOptions = ClientCertificateOption.Manual;
+ handler.ServerCertificateCustomValidationCallback = callback;
+ }
+
+ if (!string.IsNullOrEmpty(hr.ProxyScheme))
+ {
+ NetworkCredential credential = new(hr.ProxyUser, hr.ProxyPass);
+ handler.Proxy = new WebProxy(hr.ProxyScheme, true, null, credential);
+ handler.Credentials = credential;
+ handler.UseProxy = true;
+ }
+ else handler.UseProxy = false;
+
+ if (hr.Certificate != null)
+ {
+ handler.ClientCertificateOptions = ClientCertificateOption.Manual;
+ handler.ClientCertificates.Add(hr.Certificate);
+ }
+
+ HttpContent? content = null;
+ if (hr.DataToSend.Length > 0 && hr.Method != HttpMethod.Get && hr.Method != HttpMethod.Head)
+ {
+ content = new ReadOnlyMemoryContent(hr.DataToSend);
+ if (!string.IsNullOrEmpty(hr.ContentType))
+ content.Headers.ContentType = new MediaTypeHeaderValue(hr.ContentType);
+ }
+
+ HttpClient httpClient = new(handler);
+ httpClient.Timeout = TimeSpan.FromMilliseconds(hr.TimeoutMS);
+ httpClient.DefaultRequestHeaders.ExpectContinue = false;
+ httpClient.DefaultRequestHeaders.ConnectionClose = true;
+
+ HttpRequestMessage message = new(hr.Method, hr.URI);
+ message.Content = content;
+ if (!string.IsNullOrEmpty(hr.ContentType))
+ message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(hr.ContentType));
+ message.Headers.TryAddWithoutValidation("User-Agent", hr.UserAgent);
+
+ for (int n = 0; n < hr.Headers.Count; n++)
+ {
+ string? key = hr.Headers.GetKey(n);
+ string? value = hr.Headers.Get(n);
+ if (string.IsNullOrEmpty(key)) continue;
+ if (string.IsNullOrEmpty(value)) continue;
+
+ if (key.ToLower().Trim().Equals("close"))
+ {
+ if (!hr.IsForHttpProxy) message.Headers.TryAddWithoutValidation(key, value);
+ }
+ else if (key.ToLower().Trim().Equals("connection"))
+ {
+ if (!hr.IsForHttpProxy) message.Headers.TryAddWithoutValidation(key, value);
+ }
+ else if (key.ToLower().Trim().Equals("content-length"))
+ {
+ if (!hr.IsForHttpProxy) message.Headers.TryAddWithoutValidation(key, value);
+ }
+ else if (key.ToLower().Trim().Equals("accept"))
+ {
+ if (!hr.IsForHttpProxy) message.Headers.TryAddWithoutValidation(key, value);
+ }
+ else if (key.ToLower().Trim().Equals("accept-encoding"))
+ {
+ if (!hr.IsForHttpProxy) message.Headers.TryAddWithoutValidation(key, value);
+ }
+ else if (key.ToLower().Trim().Equals("content-type"))
+ {
+ if (message.Content != null && hr.Method != HttpMethod.Get && hr.Method != HttpMethod.Head)
+ message.Content.Headers.ContentType = new MediaTypeHeaderValue(value);
+ }
+ else
+ {
+ message.Headers.TryAddWithoutValidation(key, value);
+ //Debug.WriteLine("==========> " + key + ": " + value);
+ }
+ }
+
+ // Add Auth Info
+ if (!string.IsNullOrEmpty(hr.Authorization.User))
+ {
+ if (hr.Authorization.EncodeCredentials)
+ {
+ string authInfo = $"{hr.Authorization.User}:{hr.Authorization.Password}";
+ authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
+ httpClient.DefaultRequestHeaders.Add("Authorization", "Basic " + authInfo);
+ }
+ else
+ {
+ httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {hr.Authorization.User}:{hr.Authorization.Password}");
+ }
+ }
+ else if (!string.IsNullOrEmpty(hr.Authorization.BearerToken))
+ {
+ httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {hr.Authorization.BearerToken}");
+ }
+
+ HttpResponseMessage response = await httpClient.SendAsync(message, hr.CT).ConfigureAwait(false);
+
+ hrr.IsSuccess = response.IsSuccessStatusCode;
+ hrr.ProtocolVersion = $"HTTP/{response.Version}";
+ hrr.StatusCode = (int)response.StatusCode;
+ hrr.StatusDescription = response.StatusCode.ToString();
+
+ hrr.ContentEncoding = string.Join(",", response.Content.Headers.ContentEncoding);
+ if (response.Content.Headers.ContentType != null)
+ hrr.ContentType = response.Content.Headers.ContentType.ToString();
+
+ if (response.Content.Headers.ContentLength != null)
+ hrr.ContentLength = response.Content.Headers.ContentLength.Value;
+
+ foreach (var header in response.Headers)
+ {
+ string key = header.Key;
+ string value = string.Join(",", header.Value);
+ hrr.Headers.AddAndUpdate(key, value);
+ }
+
+ if (hrr.IsSuccess)
+ {
+ hrr.Data = await response.Content.ReadAsByteArrayAsync(hr.CT).ConfigureAwait(false);
+ }
+
+ try
+ {
+ _ = Task.Run(() =>
+ {
+ handler.Dispose();
+ content?.Dispose();
+ message.Dispose();
+ httpClient.Dispose();
+ response.Dispose();
+ });
+ }
+ catch (Exception) { }
+ }
+ catch (WebException we)
+ {
+ if (we.Response is HttpWebResponse exceptionResponse)
+ {
+ hrr.IsSuccess = false;
+ hrr.ProtocolVersion = $"HTTP/{exceptionResponse.ProtocolVersion}";
+ hrr.StatusCode = (int)exceptionResponse.StatusCode;
+ hrr.StatusDescription = exceptionResponse.StatusDescription;
+ hrr.ContentEncoding = exceptionResponse.ContentEncoding;
+ hrr.ContentType = exceptionResponse.ContentType;
+ hrr.ContentLength = exceptionResponse.ContentLength;
+
+ if (exceptionResponse.Headers != null && exceptionResponse.Headers.Count > 0)
+ {
+ for (int n = 0; n < exceptionResponse.Headers.Count; n++)
+ {
+ string key = exceptionResponse.Headers.GetKey(n);
+ string val = string.Empty;
+ int valCount = 0;
+
+ string[]? getValues = exceptionResponse.Headers.GetValues(n);
+ if (getValues != null)
+ {
+ foreach (string value in getValues)
+ {
+ if (valCount == 0)
+ {
+ val += value;
+ valCount++;
+ }
+ else
+ {
+ val += $",{value}";
+ valCount++;
+ }
+ }
+ }
+
+ hrr.Headers.AddAndUpdate(key, val);
+ }
+ }
+
+ Stream? stream = null;
+ if (exceptionResponse.ContentLength > 0)
+ {
+ hrr.ContentLength = exceptionResponse.ContentLength;
+ stream = exceptionResponse.GetResponseStream();
+ byte[] data = await ByteArrayTool.StreamToBytes(stream).ConfigureAwait(false);
+ if (data.Length > 0)
+ {
+ hrr.Data = data;
+ hrr.IsSuccess = true;
+ }
+ }
+
+ try
+ {
+ _ = Task.Run(() =>
+ {
+ exceptionResponse.Dispose();
+ stream?.Dispose();
+ });
+ }
+ catch (Exception) { }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("HttpRequest SendAsync: " + ex.GetInnerExceptions());
+ hrr.IsSuccess = false;
+ }
+ });
+ try { await task.WaitAsync(TimeSpan.FromMilliseconds(hr.TimeoutMS), hr.CT); } catch (Exception) { }
+
+ return hrr;
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/AgnosticRequest.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/AgnosticRequest.cs
new file mode 100644
index 0000000..0de1938
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/AgnosticRequest.cs
@@ -0,0 +1,246 @@
+using System.Net.Sockets;
+using System.Net;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public enum RequestProtocol
+{
+ UDP,
+ TCP,
+ DoH,
+ HTTP_S,
+ SOCKS4_4A,
+ SOCKS5,
+ SniProxy,
+ Unknown
+}
+
+public enum SslKind
+{
+ NonSSL,
+ SSL
+}
+
+public class AgnosticResult
+{
+ public IPEndPoint Local_EndPoint { get; set; } = new(IPAddress.None, 0);
+ public IPEndPoint Remote_EndPoint { get; set; } = new(IPAddress.None, 0);
+ public Socket? Socket { get; internal set; }
+ public SslStream? Ssl_Stream { get; set; }
+ public SslKind Ssl_Kind { get; set; } = SslKind.NonSSL;
+ public byte[] FirstBuffer { get; internal set; } = Array.Empty();
+ public RequestProtocol Protocol { get; internal set; } = RequestProtocol.Unknown;
+ public string SNI { get; internal set; } = string.Empty; // If RequestProtocol Is SniProxy
+}
+
+public class AgnosticRequest
+{
+ public enum ListenerProtocol
+ {
+ UDP,
+ TCP
+ }
+
+ public Socket? Udp_Socket { get; set; }
+ public TcpClient? Tcp_Client { get; set; }
+ public SslStream? Ssl_Stream { get; set; }
+ public IPEndPoint? Local_EndPoint { get; set; }
+ public IPEndPoint? Remote_EndPoint { get; set; }
+ public byte[] Peeked_Buffer { get; set; } = Array.Empty();
+ public ListenerProtocol Protocol { get; set; }
+
+ public async Task GetResultAsync(AgnosticSettingsSSL settingsSSL)
+ {
+ AgnosticResult ar = new();
+ if (Local_EndPoint == null) return ar;
+ if (Remote_EndPoint == null) return ar;
+ if (Peeked_Buffer.Length < 2) return ar;
+
+ try
+ {
+ ar.Local_EndPoint = Local_EndPoint;
+ ar.Remote_EndPoint = Remote_EndPoint;
+
+ if (Protocol == ListenerProtocol.UDP && Udp_Socket != null)
+ {
+ ar.Socket = Udp_Socket;
+ ar.FirstBuffer = Peeked_Buffer;
+ ar.Protocol = RequestProtocol.UDP;
+ }
+ else if (Protocol == ListenerProtocol.TCP && Tcp_Client != null)
+ {
+ ByteArrayTool.TryConvertBytesToUInt16(Peeked_Buffer[..2], out ushort tcpLength);
+ if (tcpLength == Peeked_Buffer.Length - 2)
+ {
+ ar.Socket = Tcp_Client.Client;
+ ar.FirstBuffer = await HandleNonSslAsync().ConfigureAwait(false);
+ ar.Protocol = RequestProtocol.TCP;
+ }
+ else if (Peeked_Buffer[0] == 0x04)
+ {
+ ar.Socket = Tcp_Client.Client;
+ ar.FirstBuffer = await HandleNonSslAsync().ConfigureAwait(false);
+ ar.Protocol = RequestProtocol.SOCKS4_4A;
+ }
+ else if (Peeked_Buffer[0] == 0x05)
+ {
+ ar.Socket = Tcp_Client.Client;
+ ar.FirstBuffer = await HandleNonSslAsync().ConfigureAwait(false);
+ ar.Protocol = RequestProtocol.SOCKS5;
+ }
+ else
+ {
+ byte[] firstBuffer = Array.Empty();
+ if (Peeked_Buffer[0] == 0x16) // SSL
+ {
+ SniReader sniReader = new(Peeked_Buffer);
+ if (sniReader.HasSni)
+ {
+ if (sniReader.SniList.Count != 0)
+ {
+ string sni = sniReader.SniList[0].ServerName;
+ ar.SNI = sni;
+ ar.Socket = Tcp_Client.Client;
+ ar.FirstBuffer = Peeked_Buffer;
+ ar.Protocol = RequestProtocol.SniProxy;
+ }
+ }
+ else
+ {
+ firstBuffer = await HandleSslAsync(settingsSSL).ConfigureAwait(false);
+ ar.Ssl_Kind = SslKind.SSL;
+ }
+ }
+ else
+ {
+ firstBuffer = await HandleNonSslAsync().ConfigureAwait(false);
+ }
+
+ if (firstBuffer.Length > 0)
+ {
+ HttpRequestResult hrResult = HttpRequest.Read(firstBuffer);
+ if (hrResult.IsSuccess)
+ {
+ if (hrResult.ContentType.Equals(MsmhAgnosticServer.DnsMessageContentType))
+ {
+ if (hrResult.Method == HttpMethod.Get && hrResult.RawURL.Contains("dns=", StringComparison.InvariantCultureIgnoreCase))
+ {
+ // DoH Get Method
+ string[] split = hrResult.RawURL.Split("dns=", StringSplitOptions.RemoveEmptyEntries);
+ if (split.Length >= 2)
+ {
+ string base64UrlQuery = split[1];
+ byte[] base64QueryBuffer = EncodingTool.UrlDecode(base64UrlQuery);
+ if (base64QueryBuffer.Length > 0)
+ {
+ ar.Socket = Tcp_Client.Client;
+ ar.Ssl_Stream = Ssl_Stream;
+ ar.FirstBuffer = base64QueryBuffer;
+ ar.Protocol = RequestProtocol.DoH;
+ }
+ }
+ }
+ else if (hrResult.Method == HttpMethod.Post && hrResult.PayLoad.Length > 0)
+ {
+ // DoH Post Method
+ ar.Socket = Tcp_Client.Client;
+ ar.Ssl_Stream = Ssl_Stream;
+ ar.FirstBuffer = hrResult.PayLoad;
+ ar.Protocol = RequestProtocol.DoH;
+ }
+ }
+ else
+ {
+ ar.Socket = Tcp_Client.Client;
+ ar.FirstBuffer = firstBuffer;
+ ar.Protocol = RequestProtocol.HTTP_S;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("AgnosticRequest GetResultAsync: " + ex.Message);
+ }
+
+ return ar;
+ }
+
+ private async Task HandleNonSslAsync()
+ {
+ if (Tcp_Client != null)
+ {
+ try
+ {
+ byte[] buffer = new byte[MsmhAgnosticServer.MaxDataSize];
+ int received = await Tcp_Client.Client.ReceiveAsync(buffer, SocketFlags.None).ConfigureAwait(false);
+ if (received > 0)
+ {
+ buffer = buffer[..received];
+ return buffer;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("AgnosticRequest HandleNonSslAsync: " + ex.Message);
+ }
+ }
+ return Array.Empty();
+ }
+
+ private async Task HandleSslAsync(AgnosticSettingsSSL settingsSSL)
+ {
+ if (Tcp_Client != null && settingsSSL.EnableSSL && settingsSSL.Cert != null)
+ {
+ try
+ {
+ Stopwatch stopwatch = Stopwatch.StartNew();
+
+ SslServerAuthenticationOptions optionsServer = new()
+ {
+ ServerCertificate = settingsSSL.Cert,
+ ClientCertificateRequired = false,
+ EnabledSslProtocols = MsmhAgnosticServer.SSL_Protocols,
+ CertificateRevocationCheckMode = X509RevocationMode.NoCheck,
+ RemoteCertificateValidationCallback = MsmhAgnosticServer.Callback
+ };
+
+ Ssl_Stream = new(Tcp_Client.GetStream(), false, MsmhAgnosticServer.Callback, null);
+ await Ssl_Stream.AuthenticateAsServerAsync(optionsServer, CancellationToken.None).ConfigureAwait(false);
+
+ byte[] buffer = new byte[MsmhAgnosticServer.MaxDataSize];
+ int received = await Ssl_Stream.ReadAsync(buffer, CancellationToken.None).ConfigureAwait(false);
+ if (received > 0)
+ {
+ buffer = buffer[..received];
+ Debug.WriteLine($"SSL Handshake As Server Success. Took: {stopwatch.ElapsedMilliseconds}");
+ stopwatch.Stop();
+ return buffer;
+ }
+ stopwatch.Stop();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("AgnosticRequest HandleSslAsServerAsync: " + ex.GetInnerExceptions());
+ }
+ }
+ return Array.Empty();
+ }
+
+ public void Disconnect()
+ {
+ try
+ {
+ Tcp_Client?.Client.Shutdown(SocketShutdown.Both);
+ Tcp_Client?.Dispose();
+ Ssl_Stream?.Dispose();
+ }
+ catch (Exception) { }
+ }
+
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/AgnosticSettings.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/AgnosticSettings.cs
new file mode 100644
index 0000000..98e2513
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/AgnosticSettings.cs
@@ -0,0 +1,166 @@
+using System.Diagnostics;
+using System.Net;
+using System.Net.Sockets;
+
+#nullable enable
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+///
+/// Server Settings.
+///
+public class AgnosticSettings
+{
+ public enum WorkingMode
+ {
+ Dns,
+ DnsAndProxy
+ }
+
+ private int PListenerPort { get; set; } = 53;
+ ///
+ /// The Port On Which To Listen.
+ ///
+ public int ListenerPort
+ {
+ get => PListenerPort;
+ set
+ {
+ if (value < 0 || value > 65535) throw new ArgumentOutOfRangeException(nameof(ListenerPort));
+ PListenerPort = value;
+ }
+ }
+
+ public WorkingMode Working_Mode { get; set; } = WorkingMode.DnsAndProxy;
+
+ private int PMaxRequests { get; set; } = 5000;
+ ///
+ /// Maximum Number Of Threads Per Second. (Min: 20)
+ ///
+ public int MaxRequests
+ {
+ get => PMaxRequests;
+ set
+ {
+ PMaxRequests = value >= MsmhAgnosticServer.MaxRequestsDivide ? value : MsmhAgnosticServer.MaxRequestsDivide;
+ }
+ }
+
+ ///
+ /// Cancel Dns Request if didn't receive data for n seconds. Default: 5 Sec
+ ///
+ public int DnsTimeoutSec { get; set; } = 5;
+
+ ///
+ /// Kill Proxy Request if didn't receive data for n seconds. Default: 40 Sec ( 0 = Disabled)
+ ///
+ public int ProxyTimeoutSec { get; set; } = 40;
+
+ ///
+ /// Kill Proxy Requests If CPU Usage is Higher than this Value. (Windows Only)
+ ///
+ public float KillOnCpuUsage { get; set; } = 40;
+
+ public bool BlockPort80 { get; set; } = false;
+
+ public bool AllowInsecure { get; set; } = false;
+
+ public List DNSs { get; set; } = new();
+ public string? CloudflareCleanIP { get; set; }
+
+ private IPAddress PBootstrapIpAddress { get; set; } = IPAddress.None;
+ public IPAddress BootstrapIpAddress
+ {
+ get => PBootstrapIpAddress;
+ set => PBootstrapIpAddress = value;
+ }
+
+ private int PBootstrapPort { get; set; } = 53;
+ public int BootstrapPort
+ {
+ get => PBootstrapPort;
+ set
+ {
+ if (value < 0 || value > 65535) throw new ArgumentOutOfRangeException(nameof(BootstrapPort));
+ PBootstrapPort = value;
+ }
+ }
+
+ ///
+ /// Only HTTP And Socks5 Are Supported
+ ///
+ public string? UpstreamProxyScheme { get; set; }
+ public string? UpstreamProxyUser { get; set; }
+ public string? UpstreamProxyPass { get; set; }
+ public bool ApplyUpstreamOnlyToBlockedIps { get; set; }
+
+ // Default DNSs
+ public static List DefaultDNSs()
+ {
+ return new List()
+ {
+ "tcp://8.8.8.8:53",
+ "tcp://1.1.1.1:53",
+ "udp://9.9.9.9:9953",
+ //"system"
+ };
+ }
+
+ public bool IsIPv4SupportedByOS { get; private set; }
+ public bool IsIPv6SupportedByOS { get; private set; }
+ public bool IsIPv4SupportedByISP { get; private set; }
+ public bool IsIPv6SupportedByISP { get; private set; }
+ public IPAddress? ListenerIP { get; private set; }
+ public IPEndPoint ServerEndPoint { get; internal set; } = new(IPAddress.None, 0);
+ public string ServerUdpDnsAddress { get; internal set; } = string.Empty;
+ public string ServerTcpDnsAddress { get; internal set; } = string.Empty;
+ public string ServerDohDnsAddress { get; internal set; } = string.Empty;
+ public string ServerHttpProxyAddress { get; internal set; } = string.Empty;
+ public string ServerSocks5ProxyAddress { get; internal set; } = string.Empty;
+
+ public AgnosticSettings()
+ {
+ if (OperatingSystem.IsWindows())
+ {
+ ProcessManager.ExecuteOnly("ipconfig", null, "/flushdns", true, true);
+ IsIPv4SupportedByISP = NetworkTool.IsIpProtocolReachable("8.8.8.8");
+ IsIPv6SupportedByISP = NetworkTool.IsIpProtocolReachable("2001:4860:4860::8888");
+ }
+ else
+ {
+ IsIPv4SupportedByISP = true;
+ IsIPv6SupportedByISP = false;
+ }
+
+ IsIPv4SupportedByOS = NetworkTool.IsIPv4Supported();
+ IsIPv6SupportedByOS = NetworkTool.IsIPv6Supported();
+
+ ListenerIP = IsIPv6SupportedByOS ? IPAddress.IPv6Any : IsIPv4SupportedByOS ? IPAddress.Any : null;
+ }
+
+ public void Initialize()
+ {
+ try
+ {
+ if (ListenerIP != null) ServerEndPoint = new(ListenerIP, ListenerPort);
+
+ ServerUdpDnsAddress = $"udp://{IPAddress.Loopback}:{ListenerPort}";
+ ServerTcpDnsAddress = $"tcp://{IPAddress.Loopback}:{ListenerPort}";
+ ServerDohDnsAddress = $"https://{IPAddress.Loopback}:{ListenerPort}/dns-query";
+ ServerHttpProxyAddress = $"http://{IPAddress.Loopback}:{ListenerPort}";
+ ServerSocks5ProxyAddress = $"socks5://{IPAddress.Loopback}:{ListenerPort}";
+
+ if (ServerEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
+ {
+ ServerUdpDnsAddress = $"udp://[{IPAddress.IPv6Loopback}]:{ListenerPort}";
+ ServerTcpDnsAddress = $"tcp://[{IPAddress.IPv6Loopback}]:{ListenerPort}";
+ ServerDohDnsAddress = $"https://[{IPAddress.IPv6Loopback}]:{ListenerPort}/dns-query";
+ ServerHttpProxyAddress = $"http://[{IPAddress.IPv6Loopback}]:{ListenerPort}";
+ ServerSocks5ProxyAddress = $"socks5://[{IPAddress.IPv6Loopback}]:{ListenerPort}";
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("AgnosticSettings Initialize: " + ex.Message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/AgnosticSettingsSSL.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/AgnosticSettingsSSL.cs
new file mode 100644
index 0000000..4a74dc6
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/AgnosticSettingsSSL.cs
@@ -0,0 +1,193 @@
+using System.Diagnostics;
+using System.Net;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class AgnosticSettingsSSL
+{
+ public bool EnableSSL { get; set; } = false;
+ public X509Certificate2 RootCA { get; private set; }
+ public string? RootCA_Path { get; set; }
+ public string? RootCA_KeyPath { get; set; }
+ public X509Certificate2 Cert { get; private set; }
+ public string? Cert_Path { get; set; }
+ public string? Cert_KeyPath { get; set; }
+ public bool ChangeSni { get; set; } = true;
+ public string DefaultSni { get; set; } = string.Empty;
+
+ public AgnosticSettingsSSL(bool enableSSL)
+ {
+ EnableSSL = enableSSL;
+ RootCA = new(Array.Empty());
+ Cert = new(Array.Empty());
+ if (!EnableSSL)
+ {
+ try
+ {
+ RootCA.Dispose();
+ Cert.Dispose();
+ }
+ catch (Exception) { }
+ return;
+ }
+ }
+
+ public async Task Build()
+ {
+ if (!EnableSSL) return;
+
+ try
+ {
+ if (!string.IsNullOrEmpty(RootCA_Path) && File.Exists(RootCA_Path) &&
+ !string.IsNullOrEmpty(Cert_Path) && File.Exists(Cert_Path))
+ {
+ // Read From File
+ X509Certificate2? rootCA = BuildByFile(RootCA_Path, RootCA_KeyPath, true);
+ if (rootCA != null) RootCA = new(rootCA);
+
+ X509Certificate2? cert = BuildByFile(Cert_Path, Cert_KeyPath, false);
+ if (cert != null) Cert = new(cert);
+ }
+ else
+ {
+ string certificateDirPath = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "certificate"));
+ string issuerCertPath = Path.GetFullPath(Path.Combine(certificateDirPath, "rootCA.crt"));
+ string issuerKeyPath = Path.GetFullPath(Path.Combine(certificateDirPath, "rootCA.key"));
+ string certPath = Path.GetFullPath(Path.Combine(certificateDirPath, "localhost.crt"));
+ string keyPath = Path.GetFullPath(Path.Combine(certificateDirPath, "localhost.key"));
+
+ string issuerSubjectName = "Msmh Agnostic Server Authority";
+ string subjectName = "Msmh Agnostic Server";
+
+ X509Certificate2? rootCACert = null;
+ X509Certificate2? cert = null;
+
+ // Check IF Cert Exist
+ if (File.Exists(issuerCertPath) && File.Exists(issuerKeyPath) &&
+ File.Exists(certPath) && File.Exists(keyPath))
+ {
+ // Read From File
+ rootCACert = BuildByFile(issuerCertPath, issuerKeyPath, true);
+ cert = BuildByFile(certPath, keyPath, false);
+ }
+ else
+ {
+ try
+ {
+ if (File.Exists(issuerCertPath)) File.Delete(issuerCertPath);
+ if (File.Exists(issuerKeyPath)) File.Delete(issuerKeyPath);
+ if (File.Exists(certPath)) File.Delete(certPath);
+ if (File.Exists(keyPath)) File.Delete(keyPath);
+ }
+ catch (Exception) { }
+
+ bool isInstalled = CertificateTool.IsCertificateInstalled(issuerSubjectName, StoreName.Root, StoreLocation.CurrentUser);
+ if (isInstalled)
+ {
+ while (true)
+ {
+ bool uninstalled = CertificateTool.UninstallCertificate(issuerSubjectName, StoreName.Root, StoreLocation.CurrentUser);
+ if (uninstalled) break;
+ }
+ }
+
+ // Generate
+ // Create certificate directory
+ FileDirectory.CreateEmptyDirectory(certificateDirPath);
+ // It is overwritten, no need to delete.
+ IPAddress? gateway = NetworkTool.GetDefaultGateway() ?? IPAddress.Loopback;
+ await CertificateTool.GenerateCertificateAsync(certificateDirPath, gateway, issuerSubjectName, subjectName);
+ CertificateTool.CreateP12(issuerCertPath, issuerKeyPath);
+ CertificateTool.CreateP12(certPath, keyPath);
+
+ if (File.Exists(issuerCertPath) && File.Exists(issuerKeyPath) &&
+ File.Exists(certPath) && File.Exists(keyPath))
+ {
+ // Read From File
+ rootCACert = BuildByFile(issuerCertPath, issuerKeyPath, true);
+ cert = BuildByFile(certPath, keyPath, false);
+ }
+ }
+
+ if (rootCACert != null && cert != null)
+ {
+ RootCA_Path = issuerCertPath;
+ RootCA_KeyPath = issuerKeyPath;
+ Cert_Path = certPath;
+ Cert_KeyPath = keyPath;
+
+ RootCA = new(rootCACert);
+ Cert = new(cert);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ RootCA.Dispose();
+ EnableSSL = false;
+ Debug.WriteLine("AgnosticSettingsSSL: " + ex.Message);
+ }
+
+ // Check for "m_safeCertContext is an invalid handle"
+ try
+ {
+ _ = RootCA.Subject;
+ _ = Cert.Subject;
+ }
+ catch (Exception)
+ {
+ RootCA.Dispose();
+ Cert.Dispose();
+ EnableSSL = false;
+ }
+
+ try
+ {
+ // Install RootCA
+ if (EnableSSL && !string.IsNullOrEmpty(RootCA_Path) && File.Exists(RootCA_Path))
+ {
+ // Check If Cert is Installed
+ bool isInstalled = CertificateTool.IsCertificateInstalled(RootCA.Subject, StoreName.Root, StoreLocation.CurrentUser);
+ if (!isInstalled)
+ {
+ // Install Cert
+ bool certInstalled = CertificateTool.InstallCertificate(RootCA_Path, StoreName.Root, StoreLocation.CurrentUser);
+ if (!certInstalled)
+ {
+ // User refused to install cert
+ RootCA.Dispose();
+ Cert.Dispose();
+ EnableSSL = false;
+ }
+ }
+ }
+ }
+ catch (Exception) { }
+ }
+
+ private static X509Certificate2? BuildByFile(string cert_Path, string? cert_KeyPath, bool isRootCA)
+ {
+ try
+ {
+ X509Certificate2 cert = new(X509Certificate2.CreateFromCertFile(cert_Path));
+
+ if (!cert.HasPrivateKey && !string.IsNullOrEmpty(cert_KeyPath) && File.Exists(cert_KeyPath))
+ {
+ RSA key = RSA.Create();
+ key.ImportFromPem(File.ReadAllText(cert_KeyPath).ToCharArray());
+ cert = cert.CopyWithPrivateKey(key);
+ }
+ string pass = Guid.NewGuid().ToString();
+ cert = new(cert.Export(X509ContentType.Pfx, pass), pass);
+ return new(cert);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("AgnosticSettingsSSL BuildByFile: " + ex.Message);
+ return null;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/CommonTools.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/CommonTools.cs
new file mode 100644
index 0000000..0b066e4
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/CommonTools.cs
@@ -0,0 +1,134 @@
+using System.Net;
+using System.Text;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public static class CommonTools
+{
+ //public static unsafe void SetReUsePort(this Socket socket)
+ //{
+ // socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
+ // if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ // {
+ // // set SO_REUSEADDR (https://github.com/dotnet/corefx/issues/32027)
+ // var value = 1;
+ // setsockopt(socket.Handle.ToInt32(), 1, 2, &value, sizeof(int));
+ // }
+ // else
+ // socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, 1);
+
+ //}
+
+ //[DllImport("libc", SetLastError = true)]
+ //private static extern unsafe int setsockopt(int socket, int level, int option_name, void* option_value, uint option_len);
+
+ public static bool TryConvertToEnum(bool[] bits, out T result) where T : struct, IConvertible
+ {
+ try
+ {
+ int len = bits.Length;
+ StringBuilder sb = new(len);
+
+ for (int n = 0; n < len; n++) sb.Append(bits[n] ? "1" : "0");
+
+ result = (T)Enum.Parse(typeof(T), sb.ToString());
+ return true;
+ }
+ catch (Exception)
+ {
+ result = default;
+ return false;
+ }
+ }
+
+ ///
+ /// Get root domain from a given hostname
+ ///
+ ///
+ ///
+ public static string GetWildCardDomainName(string hostname)
+ {
+ // Only for subdomains we need wild card
+ // Example www.google.com or gstatic.google.com
+ // But NOT for google.com or IP address
+
+ if (NetworkTool.IsIp(hostname, out _)) return hostname;
+
+ NetworkTool.GetHostDetails(hostname, 443, out _, out _, out string baseHost, out _, out _, out _);
+
+ if (baseHost.Equals(hostname)) return hostname;
+
+ return $"*.{baseHost}";
+ }
+
+ public static bool IsCfIP(string ipString)
+ {
+ try
+ {
+ List cloudflareIPs = new()
+ {
+ "103.21.244.0 - 103.21.244.255",
+ "103.22.200.0 - 103.22.200.255",
+ "103.31.4.0 - 103.31.5.255",
+ "104.16.0.0 - 104.31.255.255",
+ "108.162.192.0 - 108.162.207.255",
+ "131.0.72.0 - 131.0.75.255",
+ "141.101.64.0 - 141.101.65.255",
+ "162.158.0.0 - 162.158.3.255",
+ "172.64.0.0 - 172.67.255.255",
+ "173.245.48.0 - 173.245.48.255",
+ "188.114.96.0 - 188.114.99.255",
+ "190.93.240.0 - 190.93.243.255",
+ "197.234.240.0 - 197.234.243.255",
+ "198.41.128.0 - 198.41.143.255"
+ };
+
+ string[] ips = ipString.Split('.');
+ int ip1 = int.Parse(ips[0]);
+ int ip2 = int.Parse(ips[1]);
+ int ip3 = int.Parse(ips[2]);
+ int ip4 = int.Parse(ips[3]);
+
+ for (int n = 0; n < cloudflareIPs.Count; n++)
+ {
+ string ipRange = cloudflareIPs[n].Trim();
+
+ if (!string.IsNullOrEmpty(ipRange))
+ {
+ string[] split = ipRange.Split('-', StringSplitOptions.TrimEntries);
+ string ipMin = split[0].Trim();
+ string ipMax = split[1].Trim();
+
+ string[] ipMins = ipMin.Split('.');
+ int ipMin1 = int.Parse(ipMins[0]);
+ int ipMin2 = int.Parse(ipMins[1]);
+ int ipMin3 = int.Parse(ipMins[2]);
+ int ipMin4 = int.Parse(ipMins[3]);
+
+ string[] ipMaxs = ipMax.Split('.');
+ int ipMax1 = int.Parse(ipMaxs[0]);
+ int ipMax2 = int.Parse(ipMaxs[1]);
+ int ipMax3 = int.Parse(ipMaxs[2]);
+ int ipMax4 = int.Parse(ipMaxs[3]);
+
+ if (ip1 >= ipMin1 && ip1 <= ipMax1)
+ if (ip2 >= ipMin2 && ip2 <= ipMax2)
+ if (ip3 >= ipMin3 && ip3 <= ipMax3)
+ if (ip4 >= ipMin4 && ip4 <= ipMax4)
+ return true;
+ }
+ }
+ return false;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ public static bool IsCfIP(IPAddress ipv4)
+ {
+ return IsCfIP(ipv4.ToString());
+ }
+
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsCache.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsCache.cs
new file mode 100644
index 0000000..21a0df6
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsCache.cs
@@ -0,0 +1,206 @@
+using System.Collections.Concurrent;
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class DnsCache
+{
+ private readonly ConcurrentDictionary Caches = new();
+
+ public bool TryGet(DnsMessage dmQ, out DnsMessage dmR)
+ {
+ dmR = new DnsMessage();
+ bool success = false;
+
+ try
+ {
+ success = Caches.TryGetValue(dmQ.Questions.ToString(), out DnsMessage? dmROut);
+ if (success && dmROut != null)
+ {
+ dmR = CreateFromCache(dmQ, dmROut);
+ };
+ }
+ catch (Exception) { }
+
+ return success;
+ }
+
+ public bool TryRemove(DnsMessage dmQ)
+ {
+ try
+ {
+ return Caches.TryRemove(dmQ.Questions.ToString(), out _);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ public bool TryAdd(DnsMessage dmQ, DnsMessage dmR)
+ {
+ try
+ {
+ bool canCache = CanCache(dmR);
+ //Debug.WriteLine("CAN CACHE: " + canCache);
+ if (canCache) return Caches.TryAdd(dmQ.Questions.ToString(), dmR);
+ else return false;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ public int CachedRequests
+ {
+ get
+ {
+ try
+ {
+ return Caches.ToList().Count;
+ }
+ catch (Exception)
+ {
+ return -1;
+ }
+ }
+ }
+
+ public void Flush()
+ {
+ try
+ {
+ Caches.Clear();
+ }
+ catch (Exception) { }
+ }
+
+ private static DnsMessage CreateFromCache(DnsMessage dmQ, DnsMessage dmR)
+ {
+ try
+ {
+ dmR.DnsProtocol = dmQ.DnsProtocol;
+ dmR.Header.ID = dmQ.Header.ID;
+ modifyTTL(dmR.Answers.AnswerRecords);
+ modifyTTL(dmR.Authorities.AuthorityRecords);
+ modifyTTL(dmR.Additionals.AdditionalRecords);
+ return dmR;
+
+ void modifyTTL(List rrs)
+ {
+ uint newTTL = 0;
+ foreach (ResourceRecord rr in rrs.Cast())
+ {
+ try
+ {
+ //Debug.WriteLine("-=-==-= " + rr.TTLDateTime);
+ //Debug.WriteLine("-=-==-= " + rr.TimeToLive);
+
+ if (newTTL != 0)
+ {
+ rr.TTLDateTime = DateTime.UtcNow;
+ rr.TimeToLive = newTTL;
+ }
+
+ if (newTTL == 0 && rr.TimeToLive > 0)
+ {
+ double ttlDifferDouble = (DateTime.UtcNow - rr.TTLDateTime).TotalSeconds;
+ //Debug.WriteLine("----------- " + ttlDifferDouble);
+ uint ttlDiffer = ttlDifferDouble > 0 ? Convert.ToUInt32(ttlDifferDouble) : 0;
+ if (rr.TimeToLive > ttlDiffer)
+ {
+ rr.TimeToLive -= ttlDiffer;
+ rr.TTLDateTime = DateTime.UtcNow;
+ //newTTL = rr.TimeToLive; // Sometimes TTLs Are Different
+ }
+ else
+ {
+ rr.TimeToLive = 0;
+ rr.TTLDateTime = DateTime.UtcNow;
+ }
+ }
+
+ if (rr.TimeToLive <= 0) dmR.IsSuccess = false;
+
+ //Debug.WriteLine("-=-==-= " + rr.TTLDateTime);
+ //Debug.WriteLine("-=-==-= " + rr.TimeToLive);
+ }
+ catch (Exception)
+ {
+ rr.TimeToLive = 0;
+ rr.TTLDateTime = DateTime.UtcNow;
+ dmR.IsSuccess = false;
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DnsMessage CreateFromCache: " + ex.Message);
+ return dmR;
+ }
+ }
+
+ private static bool CanCache(DnsMessage dmR)
+ {
+ try
+ {
+ // https://datatracker.ietf.org/doc/html/rfc1035#section-7.4
+ List rrs = new();
+ rrs.AddRange(dmR.Answers.AnswerRecords);
+ rrs.AddRange(dmR.Authorities.AuthorityRecords);
+ rrs.AddRange(dmR.Additionals.AdditionalRecords);
+
+ bool c1 = dmR.Header.QuestionsCount > 0;
+ //bool c2 = dmR.Header.TC != Enums.TC.Truncated; // DnsProxy By Adguard Doesn't Following This Rule!!
+ bool c3 = dmR.Header.OperationalCode != DnsEnums.OperationalCode.IQUERY;
+
+ bool c4 = true;
+ foreach (Question question in dmR.Questions.QuestionRecords)
+ {
+ if (question.QNAME.Contains('*'))
+ {
+ c4 = false;
+ break;
+ }
+ }
+
+ bool c5 = false;
+ foreach (Question question in dmR.Questions.QuestionRecords)
+ {
+ foreach (IResourceRecord rr in rrs)
+ {
+ if (question.QTYPE == rr.TYPE || question.QTYPE == DnsEnums.RRType.ANY)
+ {
+ c5 = true;
+ break;
+ }
+ }
+ if (c5) break;
+ }
+
+ bool c6 = dmR.IsSuccess && dmR.Header.IsSuccess && dmR.Questions.IsSuccess;
+ bool c7 = dmR.Header.AnswersCount > 0 || dmR.Header.AuthoritiesCount > 0 || dmR.Header.AdditionalsCount > 0;
+
+ bool c8 = true;
+ foreach (IResourceRecord rr in rrs)
+ {
+ if (rr is ARecord aRecord)
+ {
+ if (aRecord.IP.ToString().StartsWith("10."))
+ {
+ c8 = false;
+ break;
+ }
+ }
+ }
+
+ return c1 && c3 && c4 && c5 && c6 && c7 && c8;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/Bootstrap.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/Bootstrap.cs
new file mode 100644
index 0000000..8279fba
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/Bootstrap.cs
@@ -0,0 +1,27 @@
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public static class Bootstrap
+{
+ public static async Task GetDnsIpAsync(string domain, IPAddress bootstrapIP, int bootstrapPort, int timeoutSec, bool getIPv6, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ string domainIP = domain;
+ bool isIP = NetworkTool.IsIp(domain, out _);
+ if (!isIP)
+ {
+ if (bootstrapIP == IPAddress.None || bootstrapIP == IPAddress.IPv6None || bootstrapPort < 1)
+ {
+ IPAddress ip = GetIP.GetIpFromSystem(domain, getIPv6, true);
+ if (ip != IPAddress.None && ip != IPAddress.IPv6None) domainIP = ip.ToString();
+ }
+ else
+ {
+ string bootstrap = $"tcp://{bootstrapIP}:{bootstrapPort}"; // TCP Usually Don't Get Hijack!
+ IPAddress ip = await GetIP.GetIpFromDnsAddressAsync(domain, bootstrap, false, timeoutSec, getIPv6, IPAddress.None, 0, proxyScheme, proxyUser, proxyPass);
+ if (ip != IPAddress.None && ip != IPAddress.IPv6None) domainIP = ip.ToString();
+ }
+ }
+ return domainIP;
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DNSCryptDns.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DNSCryptDns.cs
new file mode 100644
index 0000000..8c6e705
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DNSCryptDns.cs
@@ -0,0 +1,411 @@
+using MsmhToolsClass.ExternLibs;
+using MsmhToolsClass.ProxifiedClients;
+using System.Buffers.Binary;
+using System.Diagnostics;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+// https://github.com/DNSCrypt/dnscrypt-protocol/blob/master/DNSCRYPT-V2-PROTOCOL.txt
+public class DNSCryptDns
+{
+ private byte[] QueryBuffer { get; set; } = Array.Empty();
+ private DnsReader Reader { get; set; } = new();
+ private int TimeoutMS { get; set; } = 5;
+ private CancellationToken CT { get; set; }
+ private string? ProxyScheme { get; set; }
+ private string? ProxyUser { get; set; }
+ private string? ProxyPass { get; set; }
+
+ private TextRecord.TXTCertificate Certificate { get; set; } = new();
+
+ public DNSCryptDns(byte[] queryBuffer, DnsReader reader, int timeoutMS, CancellationToken cT, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ QueryBuffer = queryBuffer;
+ Reader = reader;
+ TimeoutMS = timeoutMS;
+ CT = cT;
+ ProxyScheme = proxyScheme;
+ ProxyUser = proxyUser;
+ ProxyPass = proxyPass;
+ }
+
+ private async Task InitializeAsync(DnsEnums.DnsProtocol protocol)
+ {
+ bool result = false;
+ Task task = Task.Run(async () =>
+ {
+ try
+ {
+ DnsMessage initializeQuery = DnsMessage.CreateQuery(protocol, Reader.StampReader.ProviderName, DnsEnums.RRType.TEXT, DnsEnums.CLASS.IN);
+ DnsMessage.TryWrite(initializeQuery, out byte[] initializeQueryBuffer);
+
+ IPEndPoint ep = new(Reader.StampReader.IP, Reader.Port);
+
+ bool upStreamProxyApplied = false;
+ TcpClient? tcpClient = null;
+ Socket? initSocket = null;
+
+ try
+ {
+ if (protocol == DnsEnums.DnsProtocol.UDP)
+ initSocket = new(ep.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
+ else if (protocol == DnsEnums.DnsProtocol.TCP)
+ {
+ tcpClient = new();
+ initSocket = tcpClient.Client;
+
+ // Support Upstream Proxy
+ ProxifiedTcpClient proxifiedTcpClient = new(ProxyScheme, ProxyUser, ProxyPass);
+ var upstream = await proxifiedTcpClient.TryGetConnectedProxifiedTcpClient(ep);
+ if (upstream.isSuccess && upstream.proxifiedTcpClient != null)
+ {
+ tcpClient = upstream.proxifiedTcpClient;
+ initSocket = tcpClient.Client;
+ upStreamProxyApplied = true;
+ }
+ }
+
+ if (initSocket != null)
+ {
+ initSocket.SendTimeout = TimeoutMS;
+ initSocket.ReceiveTimeout = TimeoutMS;
+
+ if (!upStreamProxyApplied) await initSocket.ConnectAsync(ep, CT).ConfigureAwait(false);
+ await initSocket.SendAsync(initializeQueryBuffer, SocketFlags.None, CT).ConfigureAwait(false);
+
+ byte[] initBuffer = new byte[MsmhAgnosticServer.MaxDataSize];
+ int receivedLength = 0;
+ for (int i = 0; i < 5; i++)
+ {
+ receivedLength = await initSocket.ReceiveAsync(initBuffer, SocketFlags.None, CT).ConfigureAwait(false);
+ if (receivedLength > 0) break;
+ }
+
+ //Debug.WriteLine("========= DnsCrypt ReceivedLength => " + receivedLength);
+ initBuffer = initBuffer[..receivedLength];
+
+ bool isCertValid = false;
+ uint serial = 0;
+
+ DnsMessage verifyInitDM = DnsMessage.Read(initBuffer, protocol);
+ foreach (IResourceRecord answer in verifyInitDM.Answers.AnswerRecords)
+ {
+ if (answer is not TextRecord textRecord) continue;
+ //Debug.WriteLine("Number Of Certs: " + textRecord.TXTCertificates.Count);
+ foreach (var cert in textRecord.TXTCertificates)
+ {
+ if (cert.Serial > serial)
+ {
+ serial = cert.Serial;
+ Certificate = cert;
+ }
+
+ byte[] serverSignature = Convert.FromHexString(cert.Signature);
+ TextRecord.TXTCertificate.TryWrite(cert, out byte[] certBuffer);
+ byte[] afterServerSignature = certBuffer[72..];
+ byte[] providerPublicKey = Convert.FromHexString(Reader.StampReader.PublicKey);
+
+ int verify = LibSodium.crypto_sign_verify_detached(serverSignature, afterServerSignature, afterServerSignature.Length, providerPublicKey);
+ if (verify == 0) isCertValid = true;
+ }
+ }
+
+ result = isCertValid;
+ }
+ }
+ catch (Exception) { }
+
+ initSocket?.Shutdown(SocketShutdown.Both);
+ initSocket?.Close();
+ _ = Task.Run(() => initSocket?.Dispose());
+ _ = Task.Run(() => tcpClient?.Dispose());
+ }
+ catch (Exception) { }
+ });
+ try { await task.WaitAsync(TimeSpan.FromMilliseconds(TimeoutMS), CT).ConfigureAwait(false); } catch (Exception) { }
+
+ return result;
+ }
+
+ public async Task GetResponseAsync()
+ {
+ byte[] result = Array.Empty();
+
+ Task task = Task.Run(async () =>
+ {
+ try
+ {
+ bool isInitialized = await InitializeAsync(DnsEnums.DnsProtocol.UDP);
+ if (!isInitialized) isInitialized = await InitializeAsync(DnsEnums.DnsProtocol.TCP);
+
+ //Debug.WriteLine("Is Certificate Valid: " + isInitialized);
+ if (isInitialized)
+ {
+ byte[] clientNonce = ByteArrayTool.GenerateRandom(12);
+ byte[] clientNoncePad = new byte[12];
+ byte[] paddedClientNonce = clientNonce.Concat(clientNoncePad).ToArray();
+
+ byte[] queryPad = GenerateQueryPad(QueryBuffer.Length);
+ byte[] paddedQuery = QueryBuffer.Concat(queryPad).ToArray();
+
+ byte[] certPublicKey = Convert.FromHexString(Certificate.PublicKey);
+
+ byte[] clientSecretKey = ByteArrayTool.GenerateRandom(32);
+
+ bool isCreateSharedKeySuccess = CreateSharedKey(Certificate, certPublicKey, clientSecretKey, out byte[] sharedKeyBuffer);
+ //Debug.WriteLine("Is Create SharedKey Success: " + isCreateSharedKeySuccess);
+ //Debug.WriteLine("SharedKey: " + Convert.ToHexString(sharedKeyBuffer).ToLower());
+ if (isCreateSharedKeySuccess)
+ {
+ bool isEncryptedQuerySuccess = Encrypt(Certificate, ref paddedQuery, ref paddedClientNonce, sharedKeyBuffer, out byte[] encryptedQuery);
+ //Debug.WriteLine("Is Encrypted Query Success: " + isEncryptedQuerySuccess);
+ //Debug.WriteLine("Encrypted Query: " + Convert.ToHexString(encryptedQuery).ToLower());
+ if (isEncryptedQuerySuccess)
+ {
+ bool isCreateClientPublicKeySuccess = CreateClientPublicKey(clientSecretKey, out byte[] clientPublicKey);
+ //Debug.WriteLine("Is Create Client PublicKey Success: " + isCreateClientPublicKeySuccess);
+ //Debug.WriteLine("Client PublicKey: " + Convert.ToHexString(clientPublicKey).ToLower());
+ if (isCreateClientPublicKeySuccess)
+ {
+ //Debug.WriteLine("Certificate ClientMagic: " + Certificate.ClientMagic);
+ List queryPacketList = new();
+ queryPacketList.AddRange(Convert.FromHexString(Certificate.ClientMagic));
+ queryPacketList.AddRange(clientPublicKey);
+ queryPacketList.AddRange(clientNonce);
+ queryPacketList.AddRange(encryptedQuery);
+ byte[] queryPacket = queryPacketList.ToArray();
+
+ IPEndPoint ep = new(Reader.StampReader.IP, Reader.Port);
+
+ TcpClient tcpClient = new();
+ tcpClient.SendTimeout = TimeoutMS;
+ tcpClient.ReceiveTimeout = TimeoutMS;
+ tcpClient.Client.NoDelay = true;
+
+ // Support Upstream Proxy
+ ProxifiedTcpClient proxifiedTcpClient = new(ProxyScheme, ProxyUser, ProxyPass);
+ var upstream = await proxifiedTcpClient.TryGetConnectedProxifiedTcpClient(ep);
+ if (upstream.isSuccess && upstream.proxifiedTcpClient != null) tcpClient = upstream.proxifiedTcpClient;
+
+ try
+ {
+ if (!upstream.isSuccess)
+ await tcpClient.Client.ConnectAsync(ep, CT).ConfigureAwait(false);
+ //Debug.WriteLine("Is TCP Socket Connected: " + tcpSocket.Connected);
+
+ byte[] prefix = new byte[2];
+ BinaryPrimitives.WriteUInt16BigEndian(prefix, (ushort)queryPacket.Length);
+ byte[] queryPacketToSend = prefix.Concat(queryPacket).ToArray();
+
+ await tcpClient.Client.SendAsync(queryPacketToSend, SocketFlags.None).ConfigureAwait(false);
+
+ int lengthP = await tcpClient.Client.ReceiveAsync(prefix, SocketFlags.None).ConfigureAwait(false);
+ //Debug.WriteLine("=== Received Prefix Length: " + lengthP);
+ ushort size = BinaryPrimitives.ReadUInt16BigEndian(prefix);
+
+ if (size > 0)
+ {
+ byte[] answerPacket = new byte[size];
+ int lengthR = await tcpClient.Client.ReceiveAsync(answerPacket, SocketFlags.None).ConfigureAwait(false);
+ if (answerPacket[0] == 0)
+ lengthR = await tcpClient.Client.ReceiveAsync(answerPacket, SocketFlags.None).ConfigureAwait(false);
+ if (answerPacket[0] == 0)
+ lengthR = await tcpClient.Client.ReceiveAsync(answerPacket, SocketFlags.None).ConfigureAwait(false);
+ if (answerPacket[0] == 0)
+ lengthR = await tcpClient.Client.ReceiveAsync(answerPacket, SocketFlags.None).ConfigureAwait(false);
+
+ //Debug.WriteLine("=== Received Answer Length: " + lengthR);
+
+ string clientMagic = Encoding.UTF8.GetString(answerPacket[..8]);
+ //Debug.WriteLine("Received Client Magic: " + clientMagic);
+
+ string constClientMagic = "r6fnvWj8";
+ if (clientMagic != constClientMagic)
+ Debug.WriteLine("Invalid DNSCrypt Client Magic Received.");
+
+ if (!clientNonce.SequenceEqual(answerPacket[8..20]))
+ Debug.WriteLine("Invalid DNSCrypt Client Nonce Received.");
+
+ byte[] serverNonce = answerPacket[20..32];
+ byte[] nonce = clientNonce.Concat(serverNonce).ToArray();
+
+ byte[] encryptedAnswer = answerPacket[32..];
+
+ bool isDecryptedAnswerSuccess = Decrypt(Certificate, ref encryptedAnswer, ref nonce, sharedKeyBuffer, out byte[] decryptedAnswer);
+ //Debug.WriteLine("Is Decrypted Answer Success: " + isDecryptedAnswerSuccess);
+ if (isDecryptedAnswerSuccess)
+ result = decryptedAnswer;
+ }
+ }
+ catch (Exception) { }
+
+ tcpClient.Client.Shutdown(SocketShutdown.Both);
+ tcpClient.Client.Close();
+ _ = Task.Run(() => tcpClient.Dispose());
+ }
+ }
+ }
+ }
+ }
+ catch (Exception) { }
+ });
+ try { await task.WaitAsync(TimeSpan.FromMilliseconds(TimeoutMS), CT).ConfigureAwait(false); } catch (Exception) { }
+
+ return result;
+ }
+
+ private static bool CreateClientPublicKey(byte[] clientSecretKey, out byte[] clientPublicKey)
+ {
+ try
+ {
+ byte[] publicKey = new byte[clientSecretKey.Length];
+ int result = LibSodium.crypto_scalarmult_base(publicKey, clientSecretKey);
+ if (result == 0)
+ {
+ clientPublicKey = publicKey;
+ return true;
+ }
+ clientPublicKey = Array.Empty();
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNSCryptDns CreateClientPublicKey: " + ex.Message);
+ clientPublicKey = Array.Empty();
+ return false;
+ }
+ }
+
+ private static bool CreateSharedKey(TextRecord.TXTCertificate certificate, byte[] certPublicKey, byte[] secretKey, out byte[] sharedKey)
+ {
+ try
+ {
+ byte[] sharedKeyText = new byte[32];
+ if (certificate.Version == TextRecord.ESVersion.X25519_XSalsa20Poly1305)
+ {
+ int result = LibSodium.crypto_box_beforenm(sharedKeyText, certPublicKey, secretKey);
+ if (result == 0)
+ {
+ sharedKey = sharedKeyText;
+ return true;
+ }
+ }
+ else if (certificate.Version == TextRecord.ESVersion.X25519_XChacha20Poly1305)
+ {
+ int result = LibSodium.crypto_box_curve25519xchacha20poly1305_beforenm(sharedKeyText, certPublicKey, secretKey);
+ if (result == 0)
+ {
+ sharedKey = sharedKeyText;
+ return true;
+ }
+ }
+ sharedKey = Array.Empty();
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNSCryptDns CreateSharedKey: " + ex.Message);
+ sharedKey = Array.Empty();
+ return false;
+ }
+ }
+
+ private static bool Encrypt(TextRecord.TXTCertificate certificate, ref byte[] paddedQuery, ref byte[] clientNonce, byte[] sharedKey, out byte[] encrypted)
+ {
+ try
+ {
+ byte[] encryptedText = new byte[paddedQuery.Length + 16];
+ if (certificate.Version == TextRecord.ESVersion.X25519_XSalsa20Poly1305)
+ {
+ int result = LibSodium.crypto_box_easy_afternm(encryptedText, paddedQuery, paddedQuery.Length, clientNonce, sharedKey);
+ if (result == 0)
+ {
+ encrypted = encryptedText;
+ return true;
+ }
+ }
+ else if (certificate.Version == TextRecord.ESVersion.X25519_XChacha20Poly1305)
+ {
+ int result = LibSodium.crypto_box_curve25519xchacha20poly1305_easy_afternm(encryptedText, paddedQuery, paddedQuery.Length, clientNonce, sharedKey);
+ if (result == 0)
+ {
+ encrypted = encryptedText;
+ return true;
+ }
+ }
+ encrypted = Array.Empty();
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNSCryptDns Encrypt: " + ex.Message);
+ encrypted = Array.Empty();
+ return false;
+ }
+ }
+
+ private static bool Decrypt(TextRecord.TXTCertificate certificate, ref byte[] encryptedAnswer, ref byte[] serverNonce, byte[] sharedKey, out byte[] decrypted)
+ {
+ try
+ {
+ byte[] decryptedText = new byte[encryptedAnswer.Length - 16];
+ if (certificate.Version == TextRecord.ESVersion.X25519_XSalsa20Poly1305)
+ {
+ int result = LibSodium.crypto_box_open_easy_afternm(decryptedText, encryptedAnswer, encryptedAnswer.Length, serverNonce, sharedKey);
+ if (result == 0)
+ {
+ decrypted = decryptedText;
+ return true;
+ }
+ }
+ else if (certificate.Version == TextRecord.ESVersion.X25519_XChacha20Poly1305)
+ {
+ int result = LibSodium.crypto_box_curve25519xchacha20poly1305_open_easy_afternm(decryptedText, encryptedAnswer, encryptedAnswer.Length, serverNonce, sharedKey);
+ if (result == 0)
+ {
+ decrypted = decryptedText;
+ return true;
+ }
+ }
+ decrypted = Array.Empty();
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNSCryptDns Decrypt: " + ex.Message);
+ decrypted = Array.Empty();
+ return false;
+ }
+ }
+
+ private static byte[] GenerateQueryPad(int queryLength)
+ {
+ byte[] pad = Array.Empty();
+
+ try
+ {
+ if (queryLength < 256) pad = new byte[256 - queryLength];
+
+ if (queryLength > 256)
+ {
+ int paddingLength = 256 + 64;
+ while (paddingLength < queryLength)
+ {
+ paddingLength += 64;
+ }
+ pad = new byte[paddingLength - queryLength];
+ }
+
+ pad[0] = 0x80;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNSCryptDns GenerateQueryPad: " + ex.Message);
+ }
+
+ return pad;
+ }
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DnsClient.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DnsClient.cs
new file mode 100644
index 0000000..12b200a
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DnsClient.cs
@@ -0,0 +1,209 @@
+using System.Diagnostics;
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class DnsClient
+{
+ public static byte[] ConvertQueryBufferProtocol(byte[] buffer, DnsEnums.DnsProtocol fromProtocol, DnsEnums.DnsProtocol toProtocol)
+ {
+ try
+ {
+ if (buffer.Length < 3) return buffer;
+ if (fromProtocol == toProtocol) return buffer;
+ // UDP => UDP = Same
+ // UDP => TCP
+ if (fromProtocol == DnsEnums.DnsProtocol.UDP && toProtocol == DnsEnums.DnsProtocol.TCP)
+ return AddTcpMessageLength(buffer);
+ // UDP => DoH = Same
+ // UDP => DoT
+ if (fromProtocol == DnsEnums.DnsProtocol.UDP && toProtocol == DnsEnums.DnsProtocol.DoT)
+ return AddTcpMessageLength(buffer);
+ // UDP => DNSCrypt = Same
+
+ // TCP => UDP
+ else if (fromProtocol == DnsEnums.DnsProtocol.TCP && toProtocol == DnsEnums.DnsProtocol.UDP)
+ return RemoveTcpMessageLength(buffer);
+ // TCP => TCP = Same
+ // TCP => DoH
+ else if (fromProtocol == DnsEnums.DnsProtocol.TCP && toProtocol == DnsEnums.DnsProtocol.DoH)
+ return RemoveTcpMessageLength(buffer);
+ // TCP => DoT = Same
+ // TCP => DNSCrypt
+ else if (fromProtocol == DnsEnums.DnsProtocol.TCP && toProtocol == DnsEnums.DnsProtocol.DnsCrypt)
+ return RemoveTcpMessageLength(buffer);
+
+ // DoH => UDP = Same
+ // DoH => TCP
+ else if (fromProtocol == DnsEnums.DnsProtocol.DoH && toProtocol == DnsEnums.DnsProtocol.TCP)
+ return AddTcpMessageLength(buffer);
+ // DoH => DoH = Same
+ // DoH => DoT
+ else if (fromProtocol == DnsEnums.DnsProtocol.DoH && toProtocol == DnsEnums.DnsProtocol.DoT)
+ return AddTcpMessageLength(buffer);
+ // DoH => DNSCrypt = Same
+
+ // DoT => UDP
+ else if (fromProtocol == DnsEnums.DnsProtocol.DoT && toProtocol == DnsEnums.DnsProtocol.UDP)
+ return RemoveTcpMessageLength(buffer);
+ // DoT => TCP = Same
+ // DoT => DoH
+ else if (fromProtocol == DnsEnums.DnsProtocol.DoT && toProtocol == DnsEnums.DnsProtocol.DoH)
+ return RemoveTcpMessageLength(buffer);
+ // DoT => DoT = Same
+ // DoT => DNSCrypt
+ else if (fromProtocol == DnsEnums.DnsProtocol.DoT && toProtocol == DnsEnums.DnsProtocol.DnsCrypt)
+ return RemoveTcpMessageLength(buffer);
+
+ // DNSCrypt => UDP = Same
+ // DNSCrypt => TCP
+ else if (fromProtocol == DnsEnums.DnsProtocol.DnsCrypt && toProtocol == DnsEnums.DnsProtocol.TCP)
+ return AddTcpMessageLength(buffer);
+ // DNSCrypt => DoH = Same
+ // DNSCrypt => DoT
+ else if (fromProtocol == DnsEnums.DnsProtocol.DnsCrypt && toProtocol == DnsEnums.DnsProtocol.DoT)
+ return AddTcpMessageLength(buffer);
+ // DNSCrypt => DNSCrypt = Same
+ else return buffer;
+
+ static byte[] AddTcpMessageLength(byte[] buffer)
+ {
+ ushort tcpMessageLength = Convert.ToUInt16(buffer.Length);
+ ByteArrayTool.TryConvertUInt16ToBytes(tcpMessageLength, out byte[] tcpMessageLengthBytes);
+ return tcpMessageLengthBytes.Concat(buffer).ToArray();
+ }
+
+ static byte[] RemoveTcpMessageLength(byte[] buffer) => buffer[2..];
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS DnsClient ConvertQueryBufferProtocol: " + ex.Message);
+ return buffer;
+ }
+ }
+
+ public async static Task QueryAsync(byte[] queryBuffer, DnsEnums.DnsProtocol bufferProtocol, string dnsServer, bool allowInsecure, IPAddress bootstrapIP, int bootstrapPort, int timeoutMS, CancellationToken ct, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ byte[] result = Array.Empty();
+
+ try
+ {
+ if (queryBuffer.Length <= 0) return result;
+ if (string.IsNullOrEmpty(dnsServer)) return result;
+
+ // From System
+ if (dnsServer.ToLower().Equals("system"))
+ {
+ // Convert Buffer ListenerProtocol To System Protocol
+ queryBuffer = ConvertQueryBufferProtocol(queryBuffer, bufferProtocol, SystemDns.Protocol);
+
+ SystemDns systemDns = new(queryBuffer, timeoutMS, ct);
+ result = await systemDns.GetResponseAsync();
+
+ // Convert System Protocol To Buffer ListenerProtocol
+ return ConvertQueryBufferProtocol(result, SystemDns.Protocol, bufferProtocol);
+ }
+
+ // From Servers
+ DnsReader dnsReader = new(dnsServer, null);
+
+ // Convert Buffer ListenerProtocol To Dns Server ListenerProtocol
+ queryBuffer = ConvertQueryBufferProtocol(queryBuffer, bufferProtocol, dnsReader.Protocol);
+
+ //Stopwatch sw = Stopwatch.StartNew();
+
+ if (dnsReader.Protocol == DnsEnums.DnsProtocol.UDP)
+ {
+ UdpPlainDns udpPlainDns = new(queryBuffer, dnsReader, timeoutMS, ct);
+ result = await udpPlainDns.GetResponseAsync().ConfigureAwait(false);
+ }
+ else if (dnsReader.Protocol == DnsEnums.DnsProtocol.TCP)
+ {
+ TcpPlainDns tcpPlainDns = new(queryBuffer, dnsReader, timeoutMS, ct, proxyScheme, proxyUser, proxyPass);
+ result = await tcpPlainDns.GetResponseAsync().ConfigureAwait(false);
+ }
+ else if (dnsReader.Protocol == DnsEnums.DnsProtocol.DoH)
+ {
+ DoHDns doHDns = new(queryBuffer, dnsReader, allowInsecure, bootstrapIP, bootstrapPort, timeoutMS, ct, proxyScheme, proxyUser, proxyPass);
+ result = await doHDns.GetResponseAsync().ConfigureAwait(false);
+ }
+ else if (dnsReader.Protocol == DnsEnums.DnsProtocol.DoT)
+ {
+ DoTDns doTDns = new(queryBuffer, dnsReader, allowInsecure, bootstrapIP, bootstrapPort, timeoutMS, ct, proxyScheme, proxyUser, proxyPass);
+ result = await doTDns.GetResponseAsync().ConfigureAwait(false);
+ }
+ else if (dnsReader.Protocol == DnsEnums.DnsProtocol.DnsCrypt)
+ {
+ DNSCryptDns dnsCryptDns = new(queryBuffer, dnsReader, timeoutMS, ct, proxyScheme, proxyUser, proxyPass);
+ result = await dnsCryptDns.GetResponseAsync().ConfigureAwait(false);
+ }
+
+ //sw.Stop();
+ //Debug.WriteLine($"========= {dnsReader.Protocol} => " + sw.ElapsedMilliseconds);
+
+ // Convert Dns Server ListenerProtocol To Buffer ListenerProtocol
+ return ConvertQueryBufferProtocol(result, dnsReader.Protocol, bufferProtocol);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS DnsClient QueryAsync 1: " + ex.Message);
+ return result;
+ }
+ }
+
+ public async static Task QueryAsync(byte[] queryBuffer, DnsEnums.DnsProtocol bufferProtocol, List dnsServers, bool allowInsecure, IPAddress bootstrapIP, int bootstrapPort, int timeoutMS, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ return await Task.Run(async () =>
+ {
+ byte[] result = Array.Empty();
+ if (!dnsServers.Any()) return result;
+
+ try
+ {
+ if (dnsServers.Count > 1)
+ {
+ List> tasks = new();
+
+ CancellationTokenSource cts = new();
+ for (int n = 0; n < dnsServers.Count; n++)
+ {
+ string dns = dnsServers[n];
+ Task task = QueryAsync(queryBuffer, bufferProtocol, dns, allowInsecure, bootstrapIP, bootstrapPort, timeoutMS, cts.Token, proxyScheme, proxyUser, proxyPass);
+ tasks.Add(task);
+ }
+
+ while (true)
+ {
+ if (tasks.Count == 0) break;
+ Task taskResult = await Task.WhenAny(tasks).ConfigureAwait(false);
+ byte[] bytes = taskResult.Result;
+ DnsMessage dm = DnsMessage.Read(bytes, bufferProtocol);
+ if (dm.IsSuccess)
+ {
+ result = bytes;
+ break;
+ }
+ tasks.Remove(taskResult);
+ }
+ _ = Task.Run(() => cts.Cancel());
+ }
+ else if (dnsServers.Count == 1)
+ {
+ return await QueryAsync(queryBuffer, bufferProtocol, dnsServers[0], allowInsecure, bootstrapIP, bootstrapPort, timeoutMS, CancellationToken.None, proxyScheme, proxyUser, proxyPass);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS DnsClient QueryAsync 2: " + ex.Message);
+ }
+
+ return result;
+ }).ConfigureAwait(false);
+ }
+
+ public async static Task QueryAsync(byte[] queryBuffer, DnsEnums.DnsProtocol bufferProtocol, AgnosticSettings ds)
+ {
+ return await QueryAsync(queryBuffer, bufferProtocol, ds.DNSs, ds.AllowInsecure, ds.BootstrapIpAddress, ds.BootstrapPort, ds.DnsTimeoutSec * 1000, ds.UpstreamProxyScheme, ds.UpstreamProxyUser, ds.UpstreamProxyPass);
+ }
+
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DoHDns.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DoHDns.cs
new file mode 100644
index 0000000..b85c7b2
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DoHDns.cs
@@ -0,0 +1,78 @@
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class DoHDns
+{
+ private byte[] QueryBuffer { get; set; } = Array.Empty();
+ private DnsReader Reader { get; set; } = new();
+ private bool AllowInsecure { get; set; }
+ private IPAddress BootstrapIP { get; set; }
+ private int BootstrapPort { get; set; }
+ private int TimeoutMS { get; set; } = 5;
+ private CancellationToken CT { get; set; }
+ private string? ProxyScheme { get; set; }
+ private string? ProxyUser { get; set; }
+ private string? ProxyPass { get; set; }
+
+ public DoHDns(byte[] queryBuffer, DnsReader reader, bool allowInsecure, IPAddress bootstrapIP, int bootstrapPort, int timeoutMS, CancellationToken ct, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ QueryBuffer = queryBuffer;
+ Reader = reader;
+ AllowInsecure = allowInsecure;
+ BootstrapIP = bootstrapIP;
+ BootstrapPort = bootstrapPort;
+ TimeoutMS = timeoutMS;
+ CT = ct;
+ ProxyScheme = proxyScheme;
+ ProxyUser = proxyUser;
+ ProxyPass = proxyPass;
+ }
+
+ public async Task GetResponseAsync()
+ {
+ byte[] result = Array.Empty();
+
+ Task task = Task.Run(async () =>
+ {
+ try
+ {
+ string dnsServerIP = await Bootstrap.GetDnsIpAsync(Reader.Host, BootstrapIP, BootstrapPort, 3, false, ProxyScheme, ProxyUser, ProxyPass);
+ if (dnsServerIP.Equals(Reader.Host))
+ dnsServerIP = await Bootstrap.GetDnsIpAsync(Reader.Host, BootstrapIP, BootstrapPort, 3, true, ProxyScheme, ProxyUser, ProxyPass);
+
+ UriBuilder uriBuilder = new()
+ {
+ Scheme = Reader.Scheme,
+ Host = dnsServerIP,
+ Port = Reader.Port,
+ Path = Reader.Path
+ };
+
+ Uri uri = uriBuilder.Uri;
+
+ HttpRequest hr = new()
+ {
+ CT = CT,
+ URI = uri,
+ Method = HttpMethod.Post,
+ ContentType = MsmhAgnosticServer.DnsMessageContentType,
+ DataToSend = QueryBuffer,
+ TimeoutMS = TimeoutMS,
+ AllowInsecure = AllowInsecure,
+ ProxyScheme = ProxyScheme,
+ ProxyUser = ProxyUser,
+ ProxyPass = ProxyPass,
+ };
+ hr.Headers.Add("host", Reader.Host); // In Case Of Using Bootstrap
+
+ HttpRequestResponse hrr = await HttpRequest.SendAsync(hr).ConfigureAwait(false);
+ result = hrr.Data;
+ }
+ catch (Exception) { }
+ });
+ try { await task.WaitAsync(TimeSpan.FromMilliseconds(TimeoutMS), CT).ConfigureAwait(false); } catch (Exception) { }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DoQDns.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DoQDns.cs
new file mode 100644
index 0000000..0d629c8
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DoQDns.cs
@@ -0,0 +1,52 @@
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class DoQDns
+{
+ private byte[] QueryBuffer { get; set; } = Array.Empty();
+ private DnsReader Reader { get; set; } = new();
+ private IPAddress BootstrapIP { get; set; }
+ private int BootstrapPort { get; set; }
+ private int TimeoutSec { get; set; } = 5;
+ private CancellationToken CT { get; set; }
+ private string? ProxyScheme { get; set; }
+ private string? ProxyUser { get; set; }
+ private string? ProxyPass { get; set; }
+
+ public DoQDns(byte[] queryBuffer, DnsReader reader, IPAddress bootstrapIP, int bootstrapPort, int timeoutSec, CancellationToken cT, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ QueryBuffer = queryBuffer;
+ Reader = reader;
+ BootstrapIP = bootstrapIP;
+ BootstrapPort = bootstrapPort;
+ TimeoutSec = timeoutSec;
+ CT = cT;
+ ProxyScheme = proxyScheme;
+ ProxyUser = proxyUser;
+ ProxyPass = proxyPass;
+ }
+
+ public async Task GetResponseAsync()
+ {
+ byte[] result = Array.Empty();
+
+ Task task = Task.Run(() =>
+ {
+ try
+ {
+ // Reserved For .NET 8
+ //QuicClientConnectionOptions options = new();
+ //options.RemoteEndPoint
+
+ //QuicConnection quic = await QuicConnection.ConnectAsync();
+
+
+ }
+ catch (Exception) { }
+ });
+ try { await task.WaitAsync(TimeSpan.FromSeconds(TimeoutSec), CT).ConfigureAwait(false); } catch (Exception) { }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DoTDns.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DoTDns.cs
new file mode 100644
index 0000000..5e05c0f
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/DoTDns.cs
@@ -0,0 +1,106 @@
+using MsmhToolsClass.ProxifiedClients;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Cryptography.X509Certificates;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class DoTDns
+{
+ private byte[] QueryBuffer { get; set; } = Array.Empty();
+ private DnsReader Reader { get; set; } = new();
+ private bool AllowInsecure { get; set; }
+ private IPAddress BootstrapIP { get; set; }
+ private int BootstrapPort { get; set; }
+ private int TimeoutMS { get; set; } = 5;
+ private CancellationToken CT { get; set; }
+ private string? ProxyScheme { get; set; }
+ private string? ProxyUser { get; set; }
+ private string? ProxyPass { get; set; }
+
+ public DoTDns(byte[] queryBuffer, DnsReader reader, bool allowInsecure, IPAddress bootstrapIP, int bootstrapPort, int timeoutMS, CancellationToken ct, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ QueryBuffer = queryBuffer;
+ Reader = reader;
+ AllowInsecure = allowInsecure;
+ BootstrapIP = bootstrapIP;
+ BootstrapPort = bootstrapPort;
+ TimeoutMS = timeoutMS;
+ CT = ct;
+ ProxyScheme = proxyScheme;
+ ProxyUser = proxyUser;
+ ProxyPass = proxyPass;
+ }
+
+ public async Task GetResponseAsync()
+ {
+ byte[] result = Array.Empty();
+
+ Task task = Task.Run(async () =>
+ {
+ TcpClient? tcpClient = null;
+ SslStream? sslStream = null;
+
+ try
+ {
+ string dnsServerIP = await Bootstrap.GetDnsIpAsync(Reader.Host, BootstrapIP, BootstrapPort, 3, false, ProxyScheme, ProxyUser, ProxyPass);
+ if (dnsServerIP.Equals(Reader.Host))
+ dnsServerIP = await Bootstrap.GetDnsIpAsync(Reader.Host, BootstrapIP, BootstrapPort, 3, true, ProxyScheme, ProxyUser, ProxyPass);
+
+ tcpClient = new();
+ tcpClient.SendTimeout = TimeoutMS;
+ tcpClient.ReceiveTimeout = TimeoutMS;
+ tcpClient.Client.NoDelay = true;
+
+ // Support Upstream Proxy
+ ProxifiedTcpClient proxifiedTcpClient = new(ProxyScheme, ProxyUser, ProxyPass);
+ var upstream = await proxifiedTcpClient.TryGetConnectedProxifiedTcpClient(dnsServerIP, Reader.Port);
+ if (upstream.isSuccess && upstream.proxifiedTcpClient != null) tcpClient = upstream.proxifiedTcpClient;
+
+ if (!upstream.isSuccess)
+ await tcpClient.Client.ConnectAsync(dnsServerIP, Reader.Port, CT).ConfigureAwait(false);
+
+ SslClientAuthenticationOptions optionsClient = new();
+ optionsClient.TargetHost = Reader.Host;
+ optionsClient.EnabledSslProtocols = MsmhAgnosticServer.SSL_Protocols;
+ if (AllowInsecure)
+ {
+ optionsClient.CertificateRevocationCheckMode = X509RevocationMode.NoCheck;
+ optionsClient.RemoteCertificateValidationCallback = MsmhAgnosticServer.Callback;
+ }
+
+ sslStream = new(tcpClient.GetStream(), false, MsmhAgnosticServer.Callback, null);
+ await sslStream.AuthenticateAsClientAsync(optionsClient, CT).ConfigureAwait(false);
+
+ if (sslStream.IsAuthenticated && sslStream.CanWrite)
+ {
+ await sslStream.WriteAsync(QueryBuffer, CT).ConfigureAwait(false);
+
+ if (sslStream.CanRead)
+ {
+ byte[] buffer = new byte[MsmhAgnosticServer.MaxDataSize];
+ int receivedLength = await sslStream.ReadAsync(buffer, CT).ConfigureAwait(false);
+
+ if (receivedLength > 0) result = buffer[..receivedLength];
+ }
+ }
+ }
+ catch (Exception) { }
+ finally
+ {
+ try
+ {
+ tcpClient?.Client.Shutdown(SocketShutdown.Both);
+ tcpClient?.Client.Close();
+ tcpClient?.Dispose();
+ sslStream?.Dispose();
+ }
+ catch (Exception) { }
+ }
+ });
+ try { await task.WaitAsync(TimeSpan.FromMilliseconds(TimeoutMS), CT).ConfigureAwait(false); } catch (Exception) { }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/SystemDns.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/SystemDns.cs
new file mode 100644
index 0000000..9172152
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/SystemDns.cs
@@ -0,0 +1,55 @@
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class SystemDns
+{
+ private byte[] QueryBuffer { get; set; } = Array.Empty();
+ private int TimeoutMS { get; set; } = 5;
+ private CancellationToken CT { get; set; }
+ public static DnsEnums.DnsProtocol Protocol { get; private set; } = DnsEnums.DnsProtocol.UDP;
+
+ public SystemDns(byte[] queryBuffer, int timeoutMS, CancellationToken cT)
+ {
+ QueryBuffer = queryBuffer;
+ TimeoutMS = timeoutMS;
+ CT = cT;
+ }
+
+ public async Task GetResponseAsync()
+ {
+ byte[] result = Array.Empty();
+
+ Task task = Task.Run(() =>
+ {
+ try
+ {
+ DnsMessage dmQ = DnsMessage.Read(QueryBuffer, Protocol);
+ if (dmQ.IsSuccess && dmQ.Header.QuestionsCount > 0)
+ {
+ string host = dmQ.Questions.QuestionRecords[0].QNAME;
+ DnsEnums.RRType typeQ = dmQ.Questions.QuestionRecords[0].QTYPE;
+ bool getIpv6 = typeQ == DnsEnums.RRType.AAAA;
+ List ips = GetIP.GetIpsFromSystem(host, getIpv6);
+
+ if (ips.Count != 0)
+ {
+ DnsMessage dmR = DnsMessage.CreateResponse(dmQ, (ushort)ips.Count, 0, 0);
+ if (dmR.IsSuccess)
+ {
+ if (getIpv6) foreach (IPAddress ip in ips) dmR.Answers.AnswerRecords.Add(new AaaaRecord(host, 30, ip));
+ else foreach (IPAddress ip in ips) dmR.Answers.AnswerRecords.Add(new ARecord(host, 30, ip));
+
+ bool isWriteSuccess = DnsMessage.TryWrite(dmR, out byte[] aBuffer);
+ if (isWriteSuccess) result = aBuffer;
+ }
+ }
+ }
+ }
+ catch (Exception) { }
+ });
+ try { await task.WaitAsync(TimeSpan.FromMilliseconds(TimeoutMS), CT).ConfigureAwait(false); } catch (Exception) { }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/TcpPlainDns.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/TcpPlainDns.cs
new file mode 100644
index 0000000..b24b3ea
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/TcpPlainDns.cs
@@ -0,0 +1,79 @@
+using System.Net.Sockets;
+using System.Net;
+using MsmhToolsClass.ProxifiedClients;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class TcpPlainDns
+{
+ private byte[] QueryBuffer { get; set; } = Array.Empty();
+ private DnsReader Reader { get; set; } = new();
+ private int TimeoutMS { get; set; } = 5;
+ private CancellationToken CT { get; set; }
+ private string? ProxyScheme { get; set; }
+ private string? ProxyUser { get; set; }
+ private string? ProxyPass { get; set; }
+
+ public TcpPlainDns(byte[] queryBuffer, DnsReader reader, int timeoutMS, CancellationToken cT, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ QueryBuffer = queryBuffer;
+ Reader = reader;
+ TimeoutMS = timeoutMS;
+ CT = cT;
+ ProxyScheme = proxyScheme;
+ ProxyUser = proxyUser;
+ ProxyPass = proxyPass;
+ }
+
+ public async Task GetResponseAsync()
+ {
+ byte[] result = Array.Empty();
+
+ Task task = Task.Run(async () =>
+ {
+ try
+ {
+ IPEndPoint ep = new(IPAddress.Parse(Reader.Host), Reader.Port);
+
+ TcpClient tcpClient = new();
+ tcpClient.SendTimeout = TimeoutMS;
+ tcpClient.ReceiveTimeout = TimeoutMS;
+ tcpClient.Client.NoDelay = true;
+
+ // Support Upstream Proxy
+ ProxifiedTcpClient proxifiedTcpClient = new(ProxyScheme, ProxyUser, ProxyPass);
+ var upstream = await proxifiedTcpClient.TryGetConnectedProxifiedTcpClient(ep);
+ if (upstream.isSuccess && upstream.proxifiedTcpClient != null) tcpClient = upstream.proxifiedTcpClient;
+
+ try
+ {
+ if (!upstream.isSuccess)
+ await tcpClient.Client.ConnectAsync(ep, CT).ConfigureAwait(false);
+
+ await tcpClient.Client.SendAsync(QueryBuffer, SocketFlags.None, CT).ConfigureAwait(false);
+
+ byte[] buffer = new byte[MsmhAgnosticServer.MaxDataSize];
+ int receivedLength = await tcpClient.Client.ReceiveAsync(buffer, SocketFlags.None, CT).ConfigureAwait(false);
+
+ ByteArrayTool.TryConvertBytesToUInt16(buffer[0..2], out ushort answerLength);
+
+ while (receivedLength < answerLength)
+ {
+ receivedLength += await tcpClient.Client.ReceiveAsync(buffer.AsMemory()[receivedLength..], SocketFlags.None, CT).ConfigureAwait(false);
+ }
+
+ if (receivedLength > 0) result = buffer[..receivedLength];
+ }
+ catch (Exception) { }
+
+ tcpClient.Client.Shutdown(SocketShutdown.Both);
+ tcpClient.Client.Close();
+ tcpClient.Dispose();
+ }
+ catch (Exception) { }
+ });
+ try { await task.WaitAsync(TimeSpan.FromMilliseconds(TimeoutMS), CT).ConfigureAwait(false); } catch (Exception) { }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/UdpPlainDns.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/UdpPlainDns.cs
new file mode 100644
index 0000000..16e8803
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsClient/UdpPlainDns.cs
@@ -0,0 +1,58 @@
+using System.Net.Sockets;
+using System.Net;
+using System.Diagnostics;
+using System.Formats.Asn1;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class UdpPlainDns
+{
+ private byte[] QueryBuffer { get; set; } = Array.Empty();
+ private DnsReader Reader { get; set; } = new();
+ private int TimeoutMS { get; set; } = 5;
+ private CancellationToken CT { get; set; }
+
+ public UdpPlainDns(byte[] queryBuffer, DnsReader reader, int timeoutMS, CancellationToken cT)
+ {
+ QueryBuffer = queryBuffer;
+ Reader = reader;
+ TimeoutMS = timeoutMS;
+ CT = cT;
+ }
+
+ public async Task GetResponseAsync()
+ {
+ byte[] result = Array.Empty();
+
+ Task task = Task.Run(async () =>
+ {
+ try
+ {
+ IPEndPoint ep = new(IPAddress.Parse(Reader.Host), Reader.Port);
+
+ Socket socket = new(ep.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
+ socket.SendTimeout = TimeoutMS;
+ socket.ReceiveTimeout = TimeoutMS;
+
+ try
+ {
+ await socket.ConnectAsync(ep, CT).ConfigureAwait(false);
+ await socket.SendAsync(QueryBuffer, SocketFlags.None, CT).ConfigureAwait(false);
+ byte[] buffer = new byte[MsmhAgnosticServer.MaxDataSize];
+ int receivedLength = await socket.ReceiveAsync(buffer, SocketFlags.None, CT).ConfigureAwait(false);
+
+ if (receivedLength > 0) result = buffer[..receivedLength];
+ }
+ catch (Exception) { }
+
+ socket.Shutdown(SocketShutdown.Both);
+ socket.Close();
+ socket.Dispose();
+ }
+ catch (Exception) { }
+ });
+ try { await task.WaitAsync(TimeSpan.FromMilliseconds(TimeoutMS), CT).ConfigureAwait(false); } catch (Exception) { }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/DnsEnums.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/DnsEnums.cs
new file mode 100644
index 0000000..6b82d38
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/DnsEnums.cs
@@ -0,0 +1,423 @@
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class DnsEnums
+{
+ public enum DnsProtocol
+ {
+ System,
+ UDP,
+ TCP,
+ DnsCrypt,
+ DoH,
+ DoT,
+ DoQ,
+ ObliviousDohTarget,
+ AnonymizedDNSCryptRelay,
+ ObliviousDohRelay,
+ Unknown
+ }
+
+ public readonly struct DnsProtocolName
+ {
+ public static readonly string UDP = "UDP Plain DNS";
+ public static readonly string TCP = "TCP Plain DNS";
+ public static readonly string DnsCrypt = "DNSCrypt";
+ public static readonly string DoH = "DNS-Over-HTTPS";
+ public static readonly string DoT = "DNS-Over-TLS";
+ public static readonly string DoQ = "DNS-Over-Quic";
+ public static readonly string ObliviousDohTarget = "Oblivious DoH Target";
+ public static readonly string AnonymizedDNSCryptRelay = "Anonymized DNSCrypt Relay";
+ public static readonly string ObliviousDohRelay = "Oblivious DoH Relay";
+ public static readonly string Unknown = "Unknown";
+ }
+
+ public enum QR : ushort // 1 Bit
+ {
+ Query = 0,
+ Response = 1
+ }
+
+ ///
+ /// DNS Operation Codes. See https://www.iana.org/assignments/dns-parameters/dns-parameters.xml#dns-parameters-5
+ ///
+ public enum OperationalCode : ushort // 4 Bits
+ {
+ ///
+ /// Query
+ ///
+ QUERY = 0000, // 0
+ ///
+ /// IQuery (Inverse Query, OBSOLETE)
+ ///
+ IQUERY = 0001, // 1
+ ///
+ /// Status
+ ///
+ STATUS = 0010, // 2
+ ///
+ /// Notify
+ ///
+ NOTIFY = 0100, // 4
+ ///
+ /// Update
+ ///
+ UPDATE = 0101, // 5
+ ///
+ /// DNS Stateful Operations (DSO)
+ ///
+ DSO = 0110 // 6
+ // 3 and 7-15 Unassigned
+ }
+
+ ///
+ /// Authoritative Answer. This flag is only valid for responses.
+ ///
+ public enum AA : ushort // 1 Bit
+ {
+ NonAuthoritive = 0,
+ Authoritive = 1
+ }
+
+ public enum TC : ushort // 1 Bit
+ {
+ NotTruncated = 0,
+ Truncated = 1
+ }
+
+ public enum RD : ushort // 1 Bit
+ {
+ RecursionIsNotDesired = 0,
+ RecursionIsDesired = 1
+ }
+
+ public enum RA : ushort // 1 Bit
+ {
+ RecursionIsNotAvailable = 0,
+ RecursionIsAvailable = 1
+ }
+
+ public enum Z : ushort // 2 Bits
+ {
+ Reserved = 00 // Reserved for future use. Must be zero in all queries and responses
+ }
+
+ public enum AnswerAuthenticated : ushort // 1 Bit
+ {
+ False = 0,
+ True = 1
+ }
+
+ public enum NonAuthenticatedData : ushort // 1 Bit
+ {
+ False = 0,
+ True = 1
+ }
+
+ public enum ResponseCode : ushort // 4 Bits
+ {
+ NoError = 0000, // 0
+ FormatError = 0001, // 1
+ ServerFailure = 0010, // 2
+ NameError = 0011, // 3
+ NotImplemented = 0100, // 4
+ Refused = 0101 // 5
+ // 6-15 Reserved for future use
+ }
+
+ ///
+ /// DNS Resource Record Type. See https://www.iana.org/assignments/dns-parameters/dns-parameters.xml#dns-parameters-4
+ ///
+ public enum RRType : ushort
+ {
+ ///
+ /// A host address
+ ///
+ A = 1,
+ ///
+ /// An authoritative name server
+ ///
+ NS = 2,
+ ///
+ /// A mail destination (OBSOLETE - use MX)
+ ///
+ MD = 3,
+ ///
+ /// A mail forwarder (OBSOLETE - use MX)
+ ///
+ MF = 4,
+ ///
+ /// The canonical name for an alias
+ ///
+ CNAME = 5,
+ ///
+ /// Marks the start of a zone of authority
+ ///
+ SOA = 6,
+ ///
+ /// A mailbox domain name (EXPERIMENTAL)
+ ///
+ MB = 7,
+ ///
+ /// A mail group member (EXPERIMENTAL)
+ ///
+ MG = 8,
+ ///
+ /// A mail rename domain name (EXPERIMENTAL)
+ ///
+ MR = 9,
+ ///
+ /// A null RR (EXPERIMENTAL)
+ ///
+ NULL = 10,
+ ///
+ /// A well known service description
+ ///
+ WKS = 11,
+ ///
+ /// A domain name pointer
+ ///
+ PTR = 12,
+ ///
+ /// Host information
+ ///
+ HINFO = 13,
+ ///
+ /// Mailbox or mail list information
+ ///
+ MINFO = 14,
+ ///
+ /// Mail exchange
+ ///
+ MX = 15,
+ ///
+ /// Text strings
+ ///
+ TEXT = 16,
+ ///
+ /// For Responsible Person
+ ///
+ RP = 17,
+ ///
+ /// For AFS Data Base location
+ ///
+ AFSDB = 18,
+ ///
+ /// For X.25 PSDN address
+ ///
+ X25 = 19,
+ ///
+ /// For ISDN address
+ ///
+ ISDN = 20,
+ ///
+ /// For Route Through
+ ///
+ RT = 21,
+ ///
+ /// For NSAP address, NSAP style A record
+ ///
+ NSAP = 22,
+ ///
+ /// For domain name pointer, NSAP style
+ ///
+ NSAPPTR = 23,
+ ///
+ /// For security signature
+ ///
+ SIG = 24,
+ ///
+ /// For security key
+ ///
+ KEY = 25,
+ ///
+ /// X.400 mail mapping information
+ ///
+ PX = 26,
+ ///
+ /// Geographical Position
+ ///
+ GPOS = 27,
+ ///
+ /// IP6 Address
+ ///
+ AAAA = 28,
+ ///
+ /// Location Information
+ ///
+ LOC = 29,
+ ///
+ /// Next Domain (OBSOLETE)
+ ///
+ NXT = 30,
+ ///
+ /// Endpoint Identifier
+ ///
+ EID = 31,
+ ///
+ /// Nimrod Locator
+ ///
+ NIMLOC = 32,
+ ///
+ /// Server Selection
+ ///
+ SRV = 33,
+ ///
+ /// ATM Address
+ ///
+ ATMA = 34,
+ ///
+ /// Naming Authority Pointer
+ ///
+ NAPTR = 35,
+ ///
+ /// Key Exchanger
+ ///
+ KX = 36,
+ CERT = 37,
+ ///
+ /// A6 (OBSOLETE - use AAAA)
+ ///
+ A6 = 38,
+ DNAME = 39,
+ SINK = 40,
+ OPT = 41,
+ APL = 42,
+ ///
+ /// Delegation Signer
+ ///
+ DS = 43,
+ ///
+ /// SSH Key Fingerprint
+ ///
+ SSHFP = 44,
+ IPSECKEY = 45,
+ RRSIG = 46,
+ NSEC = 47,
+ DNSKEY = 48,
+ DHCID = 49,
+ NSEC3 = 50,
+ NSEC3PARAM = 51,
+ TLSA = 52,
+ ///
+ /// S/MIME cert association
+ ///
+ SMIMEA = 53,
+ ///
+ /// Host Identity ListenerProtocol
+ ///
+ HIP = 55,
+ NINFO = 56,
+ RKEY = 57,
+ ///
+ /// Trust Anchor LINK
+ ///
+ TALINK = 58,
+ ///
+ /// Child DS
+ ///
+ CDS = 59,
+ ///
+ /// DNSKEY(s) the Child wants reflected in DS
+ ///
+ CDNSKEY = 60,
+ ///
+ /// OpenPGP Key
+ ///
+ OPENPGPKEY = 61,
+ ///
+ /// Child-To-Parent Synchronization
+ ///
+ CSYNC = 62,
+ ///
+ /// Message digest for DNS zone
+ ///
+ ZONEMD = 63,
+ ///
+ /// Service Binding
+ ///
+ SVCB = 64,
+ ///
+ /// HTTPS Binding
+ ///
+ HTTPS = 65,
+ SPF = 99,
+ UINFO = 100,
+ UID = 101,
+ GID = 102,
+ UNSPEC = 103,
+ NID = 104,
+ L32 = 105,
+ L64 = 106,
+ LP = 107,
+ ///
+ /// An EUI-48 address
+ ///
+ EUI48 = 108,
+ ///
+ /// An EUI-64 address
+ ///
+ EUI64 = 109,
+ ///
+ /// Transaction Key
+ ///
+ TKEY = 249,
+ ///
+ /// Transaction Signature
+ ///
+ TSIG = 250,
+ ///
+ /// Incremental transfer
+ ///
+ IXFR = 251,
+ ///
+ /// Transfer of an entire zone
+ ///
+ AXFR = 252,
+ ///
+ /// Mailbox-related RRs (MB, MG or MR)
+ ///
+ MAILB = 253,
+ ///
+ /// Mail agent RRs (OBSOLETE - see MX)
+ ///
+ MAILA = 254,
+ ///
+ /// A request for some or all records the server has available
+ ///
+ ANY = 255,
+ URI = 256,
+ ///
+ /// Certification Authority Restriction
+ ///
+ CAA = 257,
+ ///
+ /// Application Visibility and Control
+ ///
+ AVC = 258,
+ ///
+ /// Digital Object Architecture
+ ///
+ DOA = 259,
+ ///
+ /// Automatic Multicast Tunneling Relay
+ ///
+ AMTRELAY = 260,
+ ///
+ /// DNSSEC Trust Authorities
+ ///
+ TA = 32768,
+ ///
+ /// DNSSEC Lookaside Validation (OBSOLETE)
+ ///
+ DLV = 32769
+ }
+
+ public enum CLASS : ushort
+ {
+ IN = 1, // The Internet system
+ CS = 2, // The CSNET class (Obsolete - used only for examples in some obsolete RFCs)
+ CH = 3, // The Chaos system
+ HS = 4, // Hesiod [Dyer 87]
+ }
+
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/DnsMessage.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/DnsMessage.cs
new file mode 100644
index 0000000..ddaf7f0
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/DnsMessage.cs
@@ -0,0 +1,257 @@
+using System.Diagnostics;
+using System.Text;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class DnsMessage
+{
+ public bool IsSuccess { get; internal set; } = false;
+ public byte[] DnsMessageBuffer { get; internal set; } = Array.Empty();
+ public DnsEnums.DnsProtocol DnsProtocol { get; set; }
+ public ushort TcpMessageLength { get; internal set; }
+ public Header Header { get; set; } = new();
+ public Questions Questions { get; set; } = new();
+ public Answers Answers { get; set; } = new();
+ public Authorities Authorities { get; set; } = new();
+ public Additionals Additionals { get; set; } = new();
+ internal List> LabelPositions { get; set; } = new();
+ private static readonly string DnsMessageContentType = "application/dns-message";
+
+ public DnsMessage() { }
+
+ ///
+ /// Clone With An Empty And New LabelPositions
+ ///
+ public DnsMessage(DnsMessage dm)
+ {
+ IsSuccess = dm.IsSuccess;
+ DnsMessageBuffer = dm.DnsMessageBuffer;
+ DnsProtocol = dm.DnsProtocol;
+ TcpMessageLength = dm.TcpMessageLength;
+ Header = dm.Header;
+ Questions = dm.Questions;
+ Answers = dm.Answers;
+ Authorities = dm.Authorities;
+ Additionals = dm.Additionals;
+ LabelPositions = new();
+ }
+
+ public override string ToString()
+ {
+ string result = "DNS Message:\n";
+ result += $"{nameof(IsSuccess)}: {IsSuccess}\n";
+ result += $"{nameof(DnsProtocol)}: {DnsProtocol}\n";
+ result += $"{nameof(TcpMessageLength)}: {TcpMessageLength}";
+ result += "\n\n";
+ result += Header.ToString();
+ result += "\n\n";
+ if (Header.QuestionsCount > 0)
+ {
+ result += Questions.ToString();
+ result += "\n\n";
+ }
+ if (Header.AnswersCount > 0)
+ {
+ result += Answers.ToString();
+ result += "\n\n";
+ }
+ if (Header.AuthoritiesCount > 0)
+ {
+ result += Authorities.ToString();
+ result += "\n\n";
+ }
+ if (Header.AdditionalsCount > 0)
+ {
+ result += Additionals.ToString();
+ }
+ return result;
+ }
+
+ public static DnsMessage Read(byte[] buffer, DnsEnums.DnsProtocol dnsProtocol)
+ {
+ DnsMessage dnsMessage = new();
+ if (buffer.Length <= 3) return dnsMessage;
+
+ try
+ {
+ dnsMessage.DnsMessageBuffer = buffer;
+ dnsMessage.DnsProtocol = dnsProtocol;
+
+ int pos = 0;
+ // https://datatracker.ietf.org/doc/html/rfc1035#section-4.2.2
+ if (dnsProtocol == DnsEnums.DnsProtocol.TCP)
+ {
+ bool tcpMessageLengthBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[0..2], out ushort tcpMessageLength);
+ if (!tcpMessageLengthBool) return dnsMessage;
+ dnsMessage.TcpMessageLength = tcpMessageLength;
+ buffer = buffer[2..]; // The Start Of Message Does Not Include TCP Message Length
+ dnsMessage.DnsMessageBuffer = buffer;
+ }
+
+ dnsMessage.Header = Header.Read(buffer, ref pos);
+ if (!dnsMessage.Header.IsSuccess) return dnsMessage;
+
+ if (dnsMessage.Header.QuestionsCount > 0)
+ {
+ dnsMessage.Questions = Questions.Read(buffer, ref pos, dnsMessage);
+ if (!dnsMessage.Questions.IsSuccess) return dnsMessage;
+
+ if (dnsMessage.Header.AnswersCount > 0)
+ dnsMessage.Answers = Answers.Read(buffer, ref pos, dnsMessage);
+
+ if (dnsMessage.Header.AuthoritiesCount > 0)
+ dnsMessage.Authorities = Authorities.Read(buffer, ref pos, dnsMessage);
+
+ if (dnsMessage.Header.AdditionalsCount > 0)
+ dnsMessage.Additionals = Additionals.Read(buffer, ref pos, dnsMessage);
+
+ dnsMessage.IsSuccess = dnsMessage.Header.IsSuccess && dnsMessage.Questions.IsSuccess;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DnsMessage Read: " + ex.Message);
+ }
+
+ return dnsMessage;
+ }
+
+ public static bool TryWrite(DnsMessage dnsMessage, out byte[] buffer)
+ {
+ dnsMessage = new DnsMessage(dnsMessage); // It's Important To Clear LabelPositions On New Write
+
+ buffer = Array.Empty();
+
+ try
+ {
+ int pos = 0;
+ bool headerBool = Header.TryWrite(dnsMessage, ref pos, out byte[] header);
+ if (!headerBool) return false;
+
+ bool questionBool = Questions.TryWrite(dnsMessage, ref pos, out byte[] questions);
+ if (!questionBool) return false;
+
+ bool answersBool = Answers.TryWrite(dnsMessage, ref pos, out byte[] answers);
+ if (!answersBool) return false;
+
+ bool authoritiesBool = Authorities.TryWrite(dnsMessage, ref pos, out byte[] authorities);
+ if (!authoritiesBool) return false;
+
+ bool additionalsBool = Additionals.TryWrite(dnsMessage, ref pos, out byte[] additionals);
+ if (!additionalsBool) return false;
+
+ List bufferList = new();
+ if (dnsMessage.DnsProtocol == DnsEnums.DnsProtocol.TCP)
+ {
+ dnsMessage.TcpMessageLength = Convert.ToUInt16(header.Length + questions.Length + answers.Length + authorities.Length + additionals.Length);
+ bool tcpMessageLengthBool = ByteArrayTool.TryConvertUInt16ToBytes(dnsMessage.TcpMessageLength, out byte[] tcpMessageLength);
+ if (!tcpMessageLengthBool) return false;
+ bufferList.AddRange(tcpMessageLength);
+ }
+
+ bufferList.AddRange(header);
+ bufferList.AddRange(questions);
+ bufferList.AddRange(answers);
+ bufferList.AddRange(authorities);
+ bufferList.AddRange(additionals);
+ buffer = bufferList.ToArray();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS TryWrite DnsMessage: " + ex.Message);
+ return false;
+ }
+ }
+
+ public static DnsMessage CreateQuery(DnsEnums.DnsProtocol dnsProtocol, string domain, DnsEnums.RRType rrType, DnsEnums.CLASS qClass)
+ {
+ DnsMessage dm = new()
+ {
+ DnsProtocol = dnsProtocol,
+ Header = new()
+ {
+ ID = Header.GenerateId(),
+ QR = DnsEnums.QR.Query,
+ OperationalCode = DnsEnums.OperationalCode.QUERY,
+ AA = DnsEnums.AA.NonAuthoritive,
+ TC = DnsEnums.TC.NotTruncated,
+ RD = DnsEnums.RD.RecursionIsDesired,
+ RA = DnsEnums.RA.RecursionIsNotAvailable,
+ AnswerAuthenticated = DnsEnums.AnswerAuthenticated.False,
+ NonAuthenticatedData = DnsEnums.NonAuthenticatedData.False,
+ ResponseCode = DnsEnums.ResponseCode.NoError,
+ QuestionsCount = 1,
+ AnswersCount = 0,
+ AuthoritiesCount = 0,
+ AdditionalsCount = 0
+ },
+ Questions = new()
+ {
+ QuestionRecords = new()
+ {
+ new Question()
+ {
+ QNAME = domain,
+ QTYPE = rrType,
+ QCLASS = qClass
+ }
+ }
+ }
+ };
+
+ return dm;
+ }
+
+ public static DnsMessage CreateResponse(DnsMessage dnsMessage, ushort answersCount, ushort authoritiesCount, ushort additionalsCount)
+ {
+ dnsMessage.Header.QR = DnsEnums.QR.Response;
+ dnsMessage.Header.ResponseCode = DnsEnums.ResponseCode.NoError;
+ dnsMessage.Header.AnswersCount = answersCount;
+ dnsMessage.Header.AuthoritiesCount = authoritiesCount;
+ dnsMessage.Header.AdditionalsCount = additionalsCount;
+ return dnsMessage;
+ }
+
+ public static DnsMessage CreateFailedResponse(DnsMessage dnsMessage)
+ {
+ dnsMessage.Header.QR = DnsEnums.QR.Response;
+ dnsMessage.Header.ResponseCode = DnsEnums.ResponseCode.ServerFailure;
+ dnsMessage.Header.AnswersCount = 0;
+ dnsMessage.Header.AuthoritiesCount = 0;
+ dnsMessage.Header.AdditionalsCount = 0;
+ return dnsMessage;
+ }
+
+ public static bool TryWriteDoHResponse(byte[] aBuffer, out byte[] result)
+ {
+ // https://datatracker.ietf.org/doc/html/rfc8484#section-4.2.2
+ try
+ {
+ List bufferList = new();
+ string statusLine = $"HTTP/1.1 200 OK\r\n";
+ bufferList.AddRange(Encoding.UTF8.GetBytes(statusLine));
+
+ string contentTypeLine = "Content-Type: " + DnsMessageContentType + "\r\n";
+ bufferList.AddRange(Encoding.UTF8.GetBytes(contentTypeLine));
+
+ string contentLenLine = "Content-Length: " + aBuffer.Length + "\r\n";
+ bufferList.AddRange(Encoding.UTF8.GetBytes(contentLenLine));
+
+ bufferList.AddRange(Encoding.UTF8.GetBytes("\r\n"));
+
+ // Merge Headers and Body
+ bufferList.AddRange(aBuffer);
+
+ result = bufferList.ToArray();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DnsMessage TryWriteDoHPostResponse: " + ex.Message);
+ result = Array.Empty();
+ return false;
+ }
+ }
+
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Header.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Header.cs
new file mode 100644
index 0000000..d06b5d5
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Header.cs
@@ -0,0 +1,191 @@
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class Header
+{
+ public bool IsSuccess { get; private set; } = false;
+ public ushort ID { get; set; } // 2 Bytes
+ public DnsEnums.QR QR { get; set; } // 1 Bit / Query = 0 / Response = 1
+ public DnsEnums.OperationalCode OperationalCode { get; set; } // 4 Bits / Query Type, 0 = Standard, 1 = Inverse, 2 = Server Status Request, 3-15 = Reserved For Future Use
+ public DnsEnums.AA AA { get; set; } // 1 Bit / Authoritative Answer
+ public DnsEnums.TC TC { get; set; } // 1 Bit / Truncate, 0 = Not Truncated, 1 = Truncated
+ public DnsEnums.RD RD { get; set; } // 1 Bit / Recursion Is Desired, 0 = False, 1 = True (Must Be True For a Query)
+ public DnsEnums.RA RA { get; set; } // 1 Bit / Recursion Is Available, 0 = False, 1 = True
+ public DnsEnums.Z Z { get; set; } = DnsEnums.Z.Reserved; // 2 Bits / Reserved for future use. Must be zero in all queries and responses
+ public DnsEnums.AnswerAuthenticated AnswerAuthenticated { get; set; } // 1 Bit
+ public DnsEnums.NonAuthenticatedData NonAuthenticatedData { get; set; } // 1 Bit
+ public DnsEnums.ResponseCode ResponseCode { get; set; } // 4 Bits
+ public ushort QuestionsCount { get; set; } // 2 Bytes / Number Of Questions
+ public ushort AnswersCount { get; set; } // 2 Bytes / Number Of Answers
+ public ushort AuthoritiesCount { get; set; } // 2 Bytes / Number Of Authorities
+ public ushort AdditionalsCount { get; set; } // 2 Bytes / Number Of Additional Records
+
+ public override string ToString()
+ {
+ string result = "DNS Header:\n";
+ result += $"{nameof(IsSuccess)}: {IsSuccess}\n";
+ result += $"{nameof(ID)}: {ID}\n";
+ result += $"{nameof(QR)}: {QR}\n";
+ result += $"{nameof(OperationalCode)}: {OperationalCode}\n";
+ result += $"{nameof(AA)}: {AA}\n";
+ result += $"{nameof(TC)}: {TC}\n";
+ result += $"{nameof(RD)}: {RD}\n";
+ result += $"{nameof(RA)}: {RA}\n";
+ result += $"{nameof(AnswerAuthenticated)}: {AnswerAuthenticated}\n";
+ result += $"{nameof(NonAuthenticatedData)}: {NonAuthenticatedData}\n";
+ result += $"{nameof(ResponseCode)}: {ResponseCode}\n";
+ result += $"{nameof(QuestionsCount)}: {QuestionsCount}\n";
+ result += $"{nameof(AnswersCount)}: {AnswersCount}\n";
+ result += $"{nameof(AuthoritiesCount)}: {AuthoritiesCount}\n";
+ result += $"{nameof(AdditionalsCount)}: {AdditionalsCount}";
+ return result;
+ }
+
+ internal static ushort GenerateId()
+ {
+ try
+ {
+ bool idBool = ByteArrayTool.TryConvertBytesToUInt16(Guid.NewGuid().ToByteArray(), out ushort id);
+ return idBool ? id : Convert.ToUInt16(12345);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS Header GenerateId: " + ex.Message);
+ return 12345;
+ }
+ }
+
+ public static Header Read(byte[] buffer, ref int pos)
+ {
+ try
+ {
+ if (buffer.Length <= pos + MsmhAgnosticServer.DNS_HEADER_LENGTH) return new Header();
+
+ bool idBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[pos..(pos + 2)], out ushort id);
+ pos += 2;
+
+ bool convertToBinaryBool = ByteArrayTool.TryConvertToBinary(buffer[pos..(pos + 2)], out string bitsStr);
+ bool splitBinaryBool = ByteArrayTool.TrySplitBinary(bitsStr, out bool[] bits);
+
+ pos += 2;
+ bool qrBool = CommonTools.TryConvertToEnum(new[] { bits[0] }, out DnsEnums.QR qr);
+ bool opCodeBool = CommonTools.TryConvertToEnum(new[] { bits[1], bits[2], bits[3], bits[4] }, out DnsEnums.OperationalCode opCode);
+ bool aaBool = CommonTools.TryConvertToEnum(new[] { bits[5] }, out DnsEnums.AA aa);
+ bool tcBool = CommonTools.TryConvertToEnum(new[] { bits[6] }, out DnsEnums.TC tc);
+ bool rdBool = CommonTools.TryConvertToEnum(new[] { bits[7] }, out DnsEnums.RD rd);
+ bool raBool = CommonTools.TryConvertToEnum(new[] { bits[8] }, out DnsEnums.RA ra);
+ // 9-10 Reserved
+ bool answerAuthenticatedBool = CommonTools.TryConvertToEnum(new[] { bits[11] }, out DnsEnums.AnswerAuthenticated answerAuthenticated);
+ bool nonAuthenticatedDataBool = CommonTools.TryConvertToEnum(new[] { bits[12] }, out DnsEnums.NonAuthenticatedData nonAuthenticatedData);
+ bool rCodeBool = CommonTools.TryConvertToEnum(new[] { bits[^4], bits[^3], bits[^2], bits[^1] }, out DnsEnums.ResponseCode rCode);
+
+ bool questionsCountBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[pos..(pos + 2)], out ushort questionsCount);
+ pos += 2;
+ bool answersCountBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[pos..(pos + 2)], out ushort answersCount);
+ pos += 2;
+ bool authoritiesCountBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[pos..(pos + 2)], out ushort authoritiesCount);
+ pos += 2;
+ bool additionalsCountBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[pos..(pos + 2)], out ushort additionalsCount);
+ pos += 2;
+ bool isSuccess = idBool && convertToBinaryBool && splitBinaryBool &&
+ qrBool && opCodeBool && aaBool && tcBool && rdBool && raBool && rCodeBool &&
+ answerAuthenticatedBool && nonAuthenticatedDataBool &&
+ questionsCountBool && answersCountBool && authoritiesCountBool && additionalsCountBool;
+
+ if (!isSuccess) return new Header();
+
+ Header header = new()
+ {
+ IsSuccess = isSuccess,
+ ID = id,
+ QR = qr,
+ OperationalCode = opCode,
+ AA = aa,
+ TC = tc,
+ RD = rd,
+ RA = ra,
+ Z = DnsEnums.Z.Reserved,
+ AnswerAuthenticated = answerAuthenticated,
+ NonAuthenticatedData = nonAuthenticatedData,
+ ResponseCode = rCode,
+ QuestionsCount = questionsCount,
+ AnswersCount = answersCount,
+ AuthoritiesCount = authoritiesCount,
+ AdditionalsCount = additionalsCount
+ };
+
+ return header;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS Read Header: " + ex.Message);
+ return new Header();
+ }
+ }
+
+ public static bool TryWrite(DnsMessage dnsMessage, ref int pos, out byte[] buffer)
+ {
+ try
+ {
+ Header header = dnsMessage.Header;
+ bool idBool = ByteArrayTool.TryConvertUInt16ToBytes(header.ID, out byte[] id);
+
+ bool[] bits = new bool[16]; // 16 Bits (2 Bytes)
+ bits[0] = Convert.ToBoolean(header.QR); // 0
+ bool opCodeBool = ByteArrayTool.TrySplitBinary((int)header.OperationalCode, out bool[] opCode);
+ if (opCodeBool) opCode.CopyTo(bits, 1); // 1-4
+ bits[5] = Convert.ToBoolean(header.AA); // 5
+ bits[6] = Convert.ToBoolean(header.TC); // 6
+ bits[7] = Convert.ToBoolean(header.RD); // 7
+ bits[8] = Convert.ToBoolean(header.RA); // 8
+ bool zBool = ByteArrayTool.TrySplitBinary((int)header.Z, out bool[] z);
+ if (zBool) z.CopyTo(bits, 9); // 9-10, ZZ, Reserved
+ bits[11] = Convert.ToBoolean(header.AnswerAuthenticated); // 11
+ bits[12] = Convert.ToBoolean(header.NonAuthenticatedData); // 12
+ bool rCodeBool = ByteArrayTool.TrySplitBinary((int)header.ResponseCode, out bool[] rCode);
+ if (rCodeBool) rCode.CopyTo(bits, 13); // 13-16
+ bool bitsBool = ByteArrayTool.TryConvertSplittedBinaryToBytes(bits, out byte[] bitsBytes);
+
+ bool questionsCountBool = ByteArrayTool.TryConvertUInt16ToBytes(header.QuestionsCount, out byte[] questionsCount);
+ bool answersCountBool = ByteArrayTool.TryConvertUInt16ToBytes(header.AnswersCount, out byte[] answersCount);
+ bool authoritiesCountBool = ByteArrayTool.TryConvertUInt16ToBytes(header.AuthoritiesCount, out byte[] authoritiesCount);
+ bool additionalRecordsCountBool = ByteArrayTool.TryConvertUInt16ToBytes(header.AdditionalsCount, out byte[] additionalRecordsCount);
+
+ bool isSuccess = idBool &&
+ opCodeBool && zBool && rCodeBool && bitsBool &&
+ questionsCountBool && answersCountBool && authoritiesCountBool && additionalRecordsCountBool;
+
+ if (!isSuccess)
+ {
+ buffer = Array.Empty();
+ return false;
+ }
+
+ List bufferList = new();
+ bufferList.AddRange(id);
+ bufferList.AddRange(bitsBytes);
+ bufferList.AddRange(questionsCount);
+ bufferList.AddRange(answersCount);
+ bufferList.AddRange(authoritiesCount);
+ bufferList.AddRange(additionalRecordsCount);
+ buffer = bufferList.ToArray();
+
+ bool totalSuccess = buffer.Length == MsmhAgnosticServer.DNS_HEADER_LENGTH;
+ if (!totalSuccess)
+ {
+ buffer = Array.Empty();
+ return false;
+ }
+
+ pos += MsmhAgnosticServer.DNS_HEADER_LENGTH;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS TryWrite Header: " + ex.Message);
+ buffer = Array.Empty();
+ return false;
+ }
+ }
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Questions.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Questions.cs
new file mode 100644
index 0000000..1c1bd95
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Questions.cs
@@ -0,0 +1,136 @@
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class Question
+{
+ public string QNAME { get; set; } = string.Empty; // URL Encoded Host, Terminates With 0x00 (e.g. 0x07 example 0x03 com 0x00)
+ public int QNamePosition { get; protected set; } = MsmhAgnosticServer.DNS_HEADER_LENGTH;
+ public DnsEnums.RRType QTYPE { get; set; } // 2 Bytes / DNS Record, A Record = 1
+ public DnsEnums.CLASS QCLASS { get; set; } // 2 Bytes / DNS Class, IN = 1
+
+ public Question() { }
+
+ public Question(string qNAME, int qNamePosition, DnsEnums.RRType qTYPE, DnsEnums.CLASS qCLASS)
+ {
+ QNAME = qNAME;
+ QNamePosition = qNamePosition;
+ QTYPE = qTYPE;
+ QCLASS = qCLASS;
+ }
+}
+
+public class Questions
+{
+ public bool IsSuccess { get; private set; } = false;
+ public List QuestionRecords { get; set; } = new();
+
+ public override string ToString()
+ {
+ string result = "DNS Questions:\n";
+ result += $"{nameof(IsSuccess)}: {IsSuccess}\n";
+ for (int n = 0; n < QuestionRecords.Count; n++)
+ {
+ result += $"Question Number {n + 1}:\n";
+ Question question = QuestionRecords[n];
+ result += $"{nameof(question.QNAME)}: {question.QNAME}\n";
+ result += $"{nameof(question.QTYPE)}: {question.QTYPE}\n";
+ result += $"{nameof(question.QCLASS)}: {question.QCLASS}";
+ }
+ return result;
+ }
+
+ public static Questions Read(byte[] buffer, ref int pos, DnsMessage dnsMessage)
+ {
+ try
+ {
+ Questions questions = new();
+
+ for (int n = 0; n < dnsMessage.Header.QuestionsCount; n++)
+ {
+ if (pos > buffer.Length) break;
+ if (buffer.Length < pos + 6) break;
+
+ // QNAME
+ string domain = ResourceRecord.ReadRecordName(buffer, pos, out int qLength).ToString();
+ if (string.IsNullOrEmpty(domain)) return questions;
+ int qNamePosition = pos;
+ pos += qLength;
+
+ // QTYPE
+ if (pos + 2 > buffer.Length) return questions;
+ bool qTypeBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[pos..(pos + 2)], out ushort qType);
+ pos += 2;
+
+ // QCLASS
+ if (pos + 2 > buffer.Length) return questions;
+ bool qClassBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[pos..(pos + 2)], out ushort qClass);
+ pos += 2;
+
+ if (!qTypeBool || !qClassBool) return questions;
+
+ DnsEnums.RRType typeEnum = (DnsEnums.RRType)Enum.Parse(typeof(DnsEnums.RRType), qType.ToString());
+ DnsEnums.CLASS classEnum = (DnsEnums.CLASS)Enum.Parse(typeof(DnsEnums.CLASS), qClass.ToString());
+
+ Question question = new(domain, qNamePosition, typeEnum, classEnum);
+ questions.QuestionRecords.Add(question);
+ questions.IsSuccess = true;
+ }
+
+ return questions;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS Read Questions: " + ex.Message);
+ return new Questions();
+ }
+ }
+
+ public static bool TryWrite(DnsMessage dnsMessage, ref int pos, out byte[] buffer)
+ {
+ try
+ {
+ Questions questions = dnsMessage.Questions;
+ List bufferList = new();
+
+ for (int n = 0; n < questions.QuestionRecords.Count; n++)
+ {
+ Question question = questions.QuestionRecords[n];
+
+ if (string.IsNullOrEmpty(question.QNAME.Trim()) || !NetworkTool.IsDomainNameValid(question.QNAME))
+ {
+ buffer = Array.Empty();
+ return false;
+ }
+
+ bool qTypeBool = ByteArrayTool.TryConvertUInt16ToBytes((ushort)question.QTYPE, out byte[] qType);
+ bool qClassBool = ByteArrayTool.TryConvertUInt16ToBytes((ushort)question.QCLASS, out byte[] qClass);
+
+ bool isSuccess = qTypeBool && qClassBool;
+
+ if (!isSuccess)
+ {
+ buffer = Array.Empty();
+ return false;
+ }
+
+ // QNAME
+ byte[] qName = ResourceRecord.WriteRecordName(dnsMessage, question.QNAME, question.QNamePosition);
+
+ bufferList.AddRange(qName);
+ bufferList.AddRange(qType); // QTYPE
+ bufferList.AddRange(qClass); // QCLASS
+ }
+
+ buffer = bufferList.ToArray();
+ pos += buffer.Length;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS TryWrite Questions: " + ex.Message);
+ buffer = Array.Empty();
+ return false;
+ }
+ }
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/ARecord.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/ARecord.cs
new file mode 100644
index 0000000..d854037
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/ARecord.cs
@@ -0,0 +1,79 @@
+using System.Diagnostics;
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+// https://datatracker.ietf.org/doc/html/rfc1035#section-3.4.1
+public class ARecord : ResourceRecord
+{
+ public IPAddress IP { get; private set; } = IPAddress.None;
+
+ public override string ToString()
+ {
+ string result = base.ToString() + "\n";
+ result += $"{nameof(IP)}: {IP}\n";
+ return result;
+ }
+
+ public ARecord() { }
+
+ public ARecord(string domain, uint ttl, IPAddress ipv4)
+ {
+ Name = domain;
+ TYPE = DnsEnums.RRType.A;
+ CLASS = DnsEnums.CLASS.IN;
+ TimeToLive = ttl;
+ TTLDateTime = DateTime.UtcNow;
+ IP = ipv4;
+ }
+
+ public ARecord(ResourceRecord resourceRecord, IPAddress ipv4)
+ {
+ Name = resourceRecord.Name;
+ TYPE = resourceRecord.TYPE;
+ CLASS = resourceRecord.CLASS;
+ TimeToLive = resourceRecord.TimeToLive;
+ TTLDateTime = resourceRecord.TTLDateTime;
+ IP = ipv4;
+ }
+
+ public static ResourceRecord Parse(ResourceRecord resourceRecord, byte[] buffer, int pos)
+ {
+ try
+ {
+ IPAddress ip = new(buffer[pos..(pos + 4)]);
+ return new ARecord(resourceRecord, ip);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS ARecord Parse: " + ex.Message);
+ return new ARecord();
+ }
+ }
+
+ public static bool TryWrite(IResourceRecord resourceRecord, List bufferList, ref int pos)
+ {
+ try
+ {
+ // RDLENGTH & RDDATA
+ if (resourceRecord is not ARecord aRecord) return false;
+ byte[] ipBytes = new byte[4];
+ bool success = aRecord.IP.TryWriteBytes(ipBytes, out _);
+ if (success)
+ {
+ bool rdLengthBool = ByteArrayTool.TryConvertUInt16ToBytes(Convert.ToUInt16(ipBytes.Length), out byte[] rdLength); // 2 Bytes
+ if (!rdLengthBool) return false;
+ bufferList.AddRange(rdLength);
+ bufferList.AddRange(ipBytes);
+ pos += 6;
+ return true;
+ }
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS ARecord TryWrite: " + ex.Message);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/AaaaRecord.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/AaaaRecord.cs
new file mode 100644
index 0000000..5274a89
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/AaaaRecord.cs
@@ -0,0 +1,79 @@
+using System.Diagnostics;
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+// https://datatracker.ietf.org/doc/html/rfc3596
+public class AaaaRecord : ResourceRecord
+{
+ public IPAddress IP { get; private set; } = IPAddress.None;
+
+ public override string ToString()
+ {
+ string result = base.ToString() + "\n";
+ result += $"{nameof(IP)}: {IP}\n";
+ return result;
+ }
+
+ public AaaaRecord() { }
+
+ public AaaaRecord(string domain, uint ttl, IPAddress ipv6)
+ {
+ Name = domain;
+ TYPE = DnsEnums.RRType.AAAA;
+ CLASS = DnsEnums.CLASS.IN;
+ TimeToLive = ttl;
+ TTLDateTime = DateTime.UtcNow;
+ IP = ipv6;
+ }
+
+ public AaaaRecord(ResourceRecord resourceRecord, IPAddress ipv6)
+ {
+ Name = resourceRecord.Name;
+ TYPE = resourceRecord.TYPE;
+ CLASS = resourceRecord.CLASS;
+ TimeToLive = resourceRecord.TimeToLive;
+ TTLDateTime = resourceRecord.TTLDateTime;
+ IP = ipv6;
+ }
+
+ public static ResourceRecord Parse(ResourceRecord resourceRecord, byte[] buffer, int pos)
+ {
+ try
+ {
+ IPAddress ip = new(buffer[pos..(pos + 16)]);
+ return new AaaaRecord(resourceRecord, ip);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS AaaaRecord Parse: " + ex.Message);
+ return new AaaaRecord();
+ }
+ }
+
+ public static bool TryWrite(IResourceRecord resourceRecord, List bufferList, ref int pos)
+ {
+ try
+ {
+ // RDLENGTH & RDDATA
+ if (resourceRecord is not AaaaRecord aaaaRecord) return false;
+ byte[] ipBytes = new byte[16];
+ bool success = aaaaRecord.IP.TryWriteBytes(ipBytes, out _);
+ if (success)
+ {
+ bool rdLengthBool = ByteArrayTool.TryConvertUInt16ToBytes(Convert.ToUInt16(ipBytes.Length), out byte[] rdLength); // 2 Bytes
+ if (!rdLengthBool) return false;
+ bufferList.AddRange(rdLength);
+ bufferList.AddRange(ipBytes);
+ pos += 18;
+ return true;
+ }
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS AaaaRecord TryWrite: " + ex.Message);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/CNameRecord.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/CNameRecord.cs
new file mode 100644
index 0000000..65fdf01
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/CNameRecord.cs
@@ -0,0 +1,76 @@
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+// https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.1
+public class CNameRecord : ResourceRecord
+{
+ public string CName { get; private set; } = string.Empty;
+
+ public override string ToString()
+ {
+ string result = base.ToString() + "\n";
+ result += $"{nameof(CName)}: {CName}\n";
+ return result;
+ }
+
+ public CNameRecord() { }
+
+ public CNameRecord(string domain, uint ttl, string cName)
+ {
+ Name = domain;
+ TYPE = DnsEnums.RRType.CNAME;
+ CLASS = DnsEnums.CLASS.IN;
+ TimeToLive = ttl;
+ TTLDateTime = DateTime.UtcNow;
+ CName = cName;
+ }
+
+ public CNameRecord(ResourceRecord resourceRecord, string domain)
+ {
+ Name = resourceRecord.Name;
+ TYPE = resourceRecord.TYPE;
+ CLASS = resourceRecord.CLASS;
+ TimeToLive = resourceRecord.TimeToLive;
+ TTLDateTime = resourceRecord.TTLDateTime;
+ CName = domain;
+ }
+
+ public static ResourceRecord Parse(ResourceRecord resourceRecord, byte[] buffer, int pos)
+ {
+ try
+ {
+ string domain = ReadRecordName(buffer, pos, out _).ToString();
+ if (string.IsNullOrEmpty(domain)) return new CNameRecord();
+ return new CNameRecord(resourceRecord, domain);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS CNameRecord Parse: " + ex.Message);
+ return new CNameRecord();
+ }
+ }
+
+ public static bool TryWrite(IResourceRecord resourceRecord, List bufferList, DnsMessage dnsMessage, ref int pos)
+ {
+ try
+ {
+ // RDLENGTH & RDDATA
+ if (resourceRecord is not CNameRecord cNameRecord) return false;
+ byte[] domainArray = WriteRecordName(dnsMessage, cNameRecord.CName, pos + 2);
+
+ bool rdLengthBool = ByteArrayTool.TryConvertUInt16ToBytes(Convert.ToUInt16(domainArray.Length), out byte[] rdLength); // 2 Bytes
+ if (!rdLengthBool) return false;
+ bufferList.AddRange(rdLength);
+ bufferList.AddRange(domainArray);
+ pos += 2;
+ pos += domainArray.Length;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS CNameRecord TryWrite: " + ex.Message);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/MxRecord.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/MxRecord.cs
new file mode 100644
index 0000000..d4f066f
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/MxRecord.cs
@@ -0,0 +1,89 @@
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+// https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
+public class MxRecord : ResourceRecord
+{
+ public ushort Preference { get; private set; }
+ ///
+ /// A host willing to act as a mail exchange
+ ///
+ public string Domain { get; private set; } = string.Empty;
+
+ public override string ToString()
+ {
+ string result = base.ToString() + "\n";
+ result += $"{nameof(Preference)}: {Preference}\n";
+ result += $"{nameof(Domain)}: {Domain}\n";
+ return result;
+ }
+
+ public MxRecord() { }
+
+ public MxRecord(string domain, uint ttl, ushort preference, string mailDomain)
+ {
+ Name = domain;
+ TYPE = DnsEnums.RRType.MX;
+ CLASS = DnsEnums.CLASS.IN;
+ TimeToLive = ttl;
+ TTLDateTime = DateTime.UtcNow;
+ Preference = preference;
+ Domain = mailDomain;
+ }
+
+ public MxRecord(ResourceRecord resourceRecord, ushort preference, string domain)
+ {
+ Name = resourceRecord.Name;
+ TYPE = resourceRecord.TYPE;
+ CLASS = resourceRecord.CLASS;
+ TimeToLive = resourceRecord.TimeToLive;
+ TTLDateTime = resourceRecord.TTLDateTime;
+ Preference = preference;
+ Domain = domain;
+ }
+
+ public static ResourceRecord Parse(ResourceRecord resourceRecord, byte[] buffer, int pos)
+ {
+ try
+ {
+ ByteArrayTool.TryConvertBytesToUInt16(buffer[pos..(pos + 2)], out ushort preference);
+ pos += 2;
+
+ string domain = ReadRecordName(buffer, pos, out _, false).ToString();
+ if (string.IsNullOrEmpty(domain)) return new MxRecord();
+ return new MxRecord(resourceRecord, preference, domain);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS MxRecord Parse: " + ex.Message);
+ return new MxRecord();
+ }
+ }
+
+ public static bool TryWrite(IResourceRecord resourceRecord, List bufferList, DnsMessage dnsMessage, ref int pos)
+ {
+ try
+ {
+ // RDLENGTH & RDDATA
+ if (resourceRecord is not MxRecord mxRecord) return false;
+ ByteArrayTool.TryConvertUInt16ToBytes(mxRecord.Preference, out byte[] preferenceArray);
+ byte[] domainArray = WriteRecordName(dnsMessage, mxRecord.Domain, pos + 2 + preferenceArray.Length);
+
+ int len = preferenceArray.Length + domainArray.Length;
+ bool rdLengthBool = ByteArrayTool.TryConvertUInt16ToBytes(Convert.ToUInt16(len), out byte[] rdLength); // 2 Bytes
+ if (!rdLengthBool) return false;
+ bufferList.AddRange(rdLength);
+ bufferList.AddRange(preferenceArray);
+ bufferList.AddRange(domainArray);
+ pos += 2;
+ pos += len;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS MxRecord TryWrite: " + ex.Message);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/NsRecord.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/NsRecord.cs
new file mode 100644
index 0000000..5351599
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/NsRecord.cs
@@ -0,0 +1,76 @@
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+// https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.11
+public class NsRecord : ResourceRecord
+{
+ public string NS { get; private set; } = string.Empty;
+
+ public override string ToString()
+ {
+ string result = base.ToString() + "\n";
+ result += $"{nameof(NS)}: {NS}\n";
+ return result;
+ }
+
+ public NsRecord() { }
+
+ public NsRecord(string domain, uint ttl, string ns)
+ {
+ Name = domain;
+ TYPE = DnsEnums.RRType.NS;
+ CLASS = DnsEnums.CLASS.IN;
+ TimeToLive = ttl;
+ TTLDateTime = DateTime.UtcNow;
+ NS = ns;
+ }
+
+ public NsRecord(ResourceRecord resourceRecord, string domain)
+ {
+ Name = resourceRecord.Name;
+ TYPE = resourceRecord.TYPE;
+ CLASS = resourceRecord.CLASS;
+ TimeToLive = resourceRecord.TimeToLive;
+ TTLDateTime = resourceRecord.TTLDateTime;
+ NS = domain;
+ }
+
+ public static ResourceRecord Parse(ResourceRecord resourceRecord, byte[] buffer, int pos)
+ {
+ try
+ {
+ string domain = ReadRecordName(buffer, pos, out _).ToString();
+ if (string.IsNullOrEmpty(domain)) return new NsRecord();
+ return new NsRecord(resourceRecord, domain);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS NsRecord Parse: " + ex.Message);
+ return new NsRecord();
+ }
+ }
+
+ public static bool TryWrite(IResourceRecord resourceRecord, List bufferList, DnsMessage dnsMessage, ref int pos)
+ {
+ try
+ {
+ // RDLENGTH & RDDATA
+ if (resourceRecord is not NsRecord nsRecord) return false;
+ byte[] domainArray = WriteRecordName(dnsMessage, nsRecord.NS, pos + 2);
+
+ bool rdLengthBool = ByteArrayTool.TryConvertUInt16ToBytes(Convert.ToUInt16(domainArray.Length), out byte[] rdLength); // 2 Bytes
+ if (!rdLengthBool) return false;
+ bufferList.AddRange(rdLength);
+ bufferList.AddRange(domainArray);
+ pos += 2;
+ pos += domainArray.Length;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS NsRecord TryWrite: " + ex.Message);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/SoaRecord.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/SoaRecord.cs
new file mode 100644
index 0000000..85b6cd8
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/SoaRecord.cs
@@ -0,0 +1,150 @@
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+// https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
+// https://www.tcpipguide.com/free/t_DNSMessageResourceRecordFieldFormats-4.htm
+public class SoaRecord : ResourceRecord
+{
+ public string PrimaryNameServer { get; private set; } = string.Empty;
+ ///
+ /// Authorities Mailbox (Use . instead of @)
+ ///
+ public string ResponsibleAuthoritiesMailbox { get; private set; } = string.Empty;
+ public uint SerialNumber { get; private set; }
+ public uint RefreshInterval { get; private set; }
+ public uint RetryInterval { get; private set; }
+ public uint ExpireLimit { get; private set; }
+ public uint MinTtl { get; private set; }
+
+ public override string ToString()
+ {
+ string result = base.ToString() + "\n";
+ result += $"{nameof(PrimaryNameServer)}: {PrimaryNameServer}\n";
+ result += $"{nameof(ResponsibleAuthoritiesMailbox)}: {ResponsibleAuthoritiesMailbox}\n";
+ result += $"{nameof(SerialNumber)}: {SerialNumber}\n";
+ result += $"{nameof(RefreshInterval)}: {RefreshInterval}\n";
+ result += $"{nameof(RetryInterval)}: {RetryInterval}\n";
+ result += $"{nameof(ExpireLimit)}: {ExpireLimit}\n";
+ result += $"{nameof(MinTtl)}: {MinTtl}\n";
+ return result;
+ }
+
+ public SoaRecord() { }
+
+ public SoaRecord(string domain, uint ttl, string primaryNameServer,
+ string responsibleAuthoritiesMailbox,
+ uint serialNumber,
+ uint refreshInterval,
+ uint retryInterval,
+ uint expireLimit,
+ uint minTtl)
+ {
+ Name = domain;
+ TYPE = DnsEnums.RRType.SOA;
+ CLASS = DnsEnums.CLASS.IN;
+ TimeToLive = ttl;
+ TTLDateTime = DateTime.UtcNow;
+ PrimaryNameServer = primaryNameServer;
+ ResponsibleAuthoritiesMailbox = responsibleAuthoritiesMailbox;
+ SerialNumber = serialNumber;
+ RefreshInterval = refreshInterval;
+ RetryInterval = retryInterval;
+ ExpireLimit = expireLimit;
+ MinTtl = minTtl;
+ }
+
+ public SoaRecord(ResourceRecord resourceRecord, string primaryNameServer,
+ string responsibleAuthoritiesMailbox,
+ uint serialNumber,
+ uint refreshInterval,
+ uint retryInterval,
+ uint expireLimit,
+ uint minTtl)
+ {
+ Name = resourceRecord.Name;
+ TYPE = resourceRecord.TYPE;
+ CLASS = resourceRecord.CLASS;
+ TimeToLive = resourceRecord.TimeToLive;
+ TTLDateTime = resourceRecord.TTLDateTime;
+ PrimaryNameServer = primaryNameServer;
+ ResponsibleAuthoritiesMailbox = responsibleAuthoritiesMailbox;
+ SerialNumber = serialNumber;
+ RefreshInterval = refreshInterval;
+ RetryInterval = retryInterval;
+ ExpireLimit = expireLimit;
+ MinTtl = minTtl;
+ }
+
+ public static ResourceRecord Parse(ResourceRecord resourceRecord, byte[] buffer, int pos)
+ {
+ try
+ {
+ string priNS = ReadRecordName(buffer, pos, out int length).ToString();
+ if (string.IsNullOrEmpty(priNS)) return new SoaRecord();
+ pos += length;
+
+ string ram = ReadRecordName(buffer, pos, out length, true).ToString();
+ if (string.IsNullOrEmpty(ram)) return new SoaRecord();
+ pos += length;
+
+ ByteArrayTool.TryConvertBytesToUInt32(buffer[pos..(pos + 4)], out uint serial);
+ pos += 4;
+
+ ByteArrayTool.TryConvertBytesToUInt32(buffer[pos..(pos + 4)], out uint refresh);
+ pos += 4;
+
+ ByteArrayTool.TryConvertBytesToUInt32(buffer[pos..(pos + 4)], out uint retry);
+ pos += 4;
+
+ ByteArrayTool.TryConvertBytesToUInt32(buffer[pos..(pos + 4)], out uint expire);
+ pos += 4;
+
+ ByteArrayTool.TryConvertBytesToUInt32(buffer[pos..(pos + 4)], out uint ttl);
+ pos += 4;
+
+ return new SoaRecord(resourceRecord, priNS, ram, serial, refresh, retry, expire, ttl);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS SoaRecord Parse: " + ex.Message);
+ return new SoaRecord();
+ }
+ }
+
+ public static bool TryWrite(IResourceRecord resourceRecord, List bufferList, DnsMessage dnsMessage, ref int pos)
+ {
+ try
+ {
+ // RDLENGTH & RDDATA
+ if (resourceRecord is not SoaRecord soaRecord) return false;
+ byte[] priNS = WriteRecordName(dnsMessage, soaRecord.PrimaryNameServer, pos + 2);
+ byte[] ram = WriteRecordName(dnsMessage, soaRecord.ResponsibleAuthoritiesMailbox, pos + 2 + priNS.Length);
+ ByteArrayTool.TryConvertUInt32ToBytes(soaRecord.SerialNumber, out byte[] serial);
+ ByteArrayTool.TryConvertUInt32ToBytes(soaRecord.RefreshInterval, out byte[] refresh);
+ ByteArrayTool.TryConvertUInt32ToBytes(soaRecord.RetryInterval, out byte[] retry);
+ ByteArrayTool.TryConvertUInt32ToBytes(soaRecord.ExpireLimit, out byte[] expire);
+ ByteArrayTool.TryConvertUInt32ToBytes(soaRecord.MinTtl, out byte[] ttl);
+
+ int len = priNS.Length + ram.Length + serial.Length + refresh.Length + retry.Length + expire.Length + ttl.Length;
+ bool rdLengthBool = ByteArrayTool.TryConvertUInt16ToBytes(Convert.ToUInt16(len), out byte[] rdLength); // 2 Bytes
+ if (!rdLengthBool) return false;
+ bufferList.AddRange(rdLength);
+ bufferList.AddRange(priNS);
+ bufferList.AddRange(ram);
+ bufferList.AddRange(serial);
+ bufferList.AddRange(refresh);
+ bufferList.AddRange(retry);
+ bufferList.AddRange(expire);
+ bufferList.AddRange(ttl);
+ pos += 2;
+ pos += len;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS SoaRecord TryWrite: " + ex.Message);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/TextRecord.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/TextRecord.cs
new file mode 100644
index 0000000..2c99f78
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/TextRecord.cs
@@ -0,0 +1,241 @@
+using System.Diagnostics;
+using System.Text;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+// https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
+public class TextRecord : ResourceRecord
+{
+ public List Texts { get; private set; } = new();
+ public List TXTCertificates { get; private set; } = new();
+
+ public enum ESVersion : ushort
+ {
+ X25519_XSalsa20Poly1305 = 1,
+ X25519_XChacha20Poly1305 = 2
+ }
+
+ public class TXTCertificate
+ {
+ public string Magic { get; private set; } = "DNSC";
+ public ESVersion Version { get; private set; }
+ public ushort MinorVersion { get; private set; }
+ ///
+ /// HEX String (128 Char)
+ ///
+ public string Signature { get; private set; } = string.Empty;
+ ///
+ /// HEX String (64 Char)
+ ///
+ public string PublicKey { get; private set; } = string.Empty;
+ ///
+ /// HEX String (16 Char)
+ ///
+ public string ClientMagic { get; private set; } = string.Empty;
+ public uint Serial { get; private set; }
+ public uint StartTimeStampInSec { get; private set; }
+ public uint EndTimeStampInSec { get; private set; }
+
+ public static TXTCertificate Read(byte[] buffer)
+ {
+ TXTCertificate txtCertificate = new();
+ if (buffer.Length != 124) return txtCertificate;
+
+ try
+ {
+ int pos = 0;
+ txtCertificate.Magic = Encoding.UTF8.GetString(buffer[pos..(pos + 4)]);
+ pos += 4;
+ ByteArrayTool.TryConvertBytesToUInt16(buffer[pos..(pos + 2)], out ushort version);
+ txtCertificate.Version = (ESVersion)Enum.Parse(typeof(ESVersion), version.ToString());
+ pos += 2;
+ ByteArrayTool.TryConvertBytesToUInt16(buffer[pos..(pos + 2)], out ushort minorVersion);
+ pos += 2;
+ txtCertificate.MinorVersion = minorVersion;
+ txtCertificate.Signature = Convert.ToHexString(buffer[pos..(pos + 64)]).ToLower();
+ pos += 64;
+ txtCertificate.PublicKey = Convert.ToHexString(buffer[pos..(pos + 32)]).ToLower();
+ pos += 32;
+ txtCertificate.ClientMagic = Convert.ToHexString(buffer[pos..(pos + 8)]).ToLower();
+ pos += 8;
+ ByteArrayTool.TryConvertBytesToUInt32(buffer[pos..(pos + 4)], out uint serial);
+ txtCertificate.Serial = serial;
+ pos += 4;
+ ByteArrayTool.TryConvertBytesToUInt32(buffer[pos..(pos + 4)], out uint startTimeStampInSec);
+ txtCertificate.StartTimeStampInSec = startTimeStampInSec;
+ pos += 4;
+ ByteArrayTool.TryConvertBytesToUInt32(buffer[pos..(pos + 4)], out uint endTimeStampInSec);
+ txtCertificate.EndTimeStampInSec = endTimeStampInSec;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS TextRecord TXTCertificate Read: " + ex.Message);
+ }
+
+ return txtCertificate;
+ }
+
+ public static bool TryWrite(TXTCertificate txtCertificate, out byte[] buffer)
+ {
+ try
+ {
+ List bufferList = new();
+ bufferList.AddRange(Encoding.UTF8.GetBytes(txtCertificate.Magic));
+ ByteArrayTool.TryConvertUInt16ToBytes((ushort)txtCertificate.Version, out byte[] version);
+ bufferList.AddRange(version);
+ ByteArrayTool.TryConvertUInt16ToBytes(txtCertificate.MinorVersion, out byte[] minorVersion);
+ bufferList.AddRange(minorVersion);
+ bufferList.AddRange(Convert.FromHexString(txtCertificate.Signature));
+ bufferList.AddRange(Convert.FromHexString(txtCertificate.PublicKey));
+ bufferList.AddRange(Convert.FromHexString(txtCertificate.ClientMagic));
+ ByteArrayTool.TryConvertUInt32ToBytes(txtCertificate.Serial, out byte[] serial);
+ bufferList.AddRange(serial);
+ ByteArrayTool.TryConvertUInt32ToBytes(txtCertificate.StartTimeStampInSec, out byte[] startTimeStampInSec);
+ bufferList.AddRange(startTimeStampInSec);
+ ByteArrayTool.TryConvertUInt32ToBytes(txtCertificate.EndTimeStampInSec, out byte[] endTimeStampInSec);
+ bufferList.AddRange(endTimeStampInSec);
+ buffer = bufferList.ToArray();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS TextRecord TXTCertificate TryWrite: " + ex.Message);
+ buffer = Array.Empty();
+ return false;
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ string result = base.ToString() + "\n";
+ for (int i = 0; i < Texts.Count; i++)
+ {
+ string text = Texts[i];
+ result += $"Text Number {i + 1}: {text}\n";
+ }
+ for (int j = 0; j < TXTCertificates.Count; j++)
+ {
+ TXTCertificate cert = TXTCertificates[j];
+ result += $"Certificate Number {j + 1}:\n";
+ result += $"{nameof(cert.Magic)}: {cert.Magic}\n";
+ result += $"{nameof(cert.Version)}: {cert.Version}\n";
+ result += $"{nameof(cert.MinorVersion)}: {cert.MinorVersion}\n";
+ result += $"{nameof(cert.Signature)}: {cert.Signature}\n";
+ result += $"{nameof(cert.PublicKey)}: {cert.PublicKey}\n";
+ result += $"{nameof(cert.ClientMagic)}: {cert.ClientMagic}\n";
+ result += $"{nameof(cert.Serial)}: {cert.Serial}\n";
+ result += $"{nameof(cert.StartTimeStampInSec)}: {cert.StartTimeStampInSec}\n";
+ result += $"{nameof(cert.EndTimeStampInSec)}: {cert.EndTimeStampInSec}\n";
+ }
+ return result;
+ }
+
+ public TextRecord() { }
+
+ public TextRecord(string domain, uint ttl, List texts, List txtCertificates)
+ {
+ Name = domain;
+ TYPE = DnsEnums.RRType.TEXT;
+ CLASS = DnsEnums.CLASS.IN;
+ TimeToLive = ttl;
+ TTLDateTime = DateTime.UtcNow;
+ Texts = texts;
+ TXTCertificates = txtCertificates;
+ }
+
+ public TextRecord(ResourceRecord resourceRecord, List texts, List txtCertificates)
+ {
+ Name = resourceRecord.Name;
+ TYPE = resourceRecord.TYPE;
+ CLASS = resourceRecord.CLASS;
+ TimeToLive = resourceRecord.TimeToLive;
+ TTLDateTime = resourceRecord.TTLDateTime;
+ Texts = texts;
+ TXTCertificates = txtCertificates;
+ }
+
+ public static ResourceRecord Parse(ResourceRecord resourceRecord, byte[] buffer, int pos, ushort rLength)
+ {
+ try
+ {
+ List texts = new();
+ List txtCertificates = new();
+
+ int currentPos = 0;
+ while (true)
+ {
+ int len = buffer[pos];
+ pos++;
+ currentPos++;
+
+ string magic = Encoding.UTF8.GetString(buffer, pos, 4);
+ if (magic.Equals("DNSC"))
+ {
+ txtCertificates.Add(TXTCertificate.Read(buffer[pos..(pos + len)]));
+ }
+ else
+ {
+ texts.Add(Encoding.UTF8.GetString(buffer, pos, len));
+ }
+
+ pos += len;
+ currentPos += len;
+ if (currentPos >= rLength) break;
+ }
+
+ return new TextRecord(resourceRecord, texts, txtCertificates);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS TextRecord Parse: " + ex.Message);
+ return new TextRecord();
+ }
+ }
+
+ public static bool TryWrite(IResourceRecord resourceRecord, List bufferList, ref int pos)
+ {
+ try
+ {
+ // RDLENGTH & RDDATA
+ if (resourceRecord is not TextRecord textRecord) return false;
+
+ List textsList = new();
+ for (int i = 0; i < textRecord.Texts.Count; i++)
+ {
+ string text = textRecord.Texts[i];
+ byte[] textArray = Encoding.UTF8.GetBytes(text);
+ byte textLen = (byte)textArray.Length;
+ textsList.Add(textLen);
+ textsList.AddRange(textArray);
+ }
+ byte[] textsArray = textsList.ToArray();
+
+ List txtCertificatesList = new();
+ for (int j = 0; j < textRecord.TXTCertificates.Count; j++)
+ {
+ TXTCertificate txtCertificate = textRecord.TXTCertificates[j];
+ TXTCertificate.TryWrite(txtCertificate, out byte[] txtCertificateArray);
+ byte txtCertificateLen = (byte)txtCertificateArray.Length; // 124
+ txtCertificatesList.Add(txtCertificateLen);
+ txtCertificatesList.AddRange(txtCertificateArray);
+ }
+ byte[] txtCertificatesArray = txtCertificatesList.ToArray();
+
+ int len = textsArray.Length + txtCertificatesArray.Length;
+ bool rdLengthBool = ByteArrayTool.TryConvertUInt16ToBytes(Convert.ToUInt16(len), out byte[] rdLength); // 2 Bytes
+ if (!rdLengthBool) return false;
+ bufferList.AddRange(rdLength);
+ bufferList.AddRange(textsArray);
+ bufferList.AddRange(txtCertificatesArray);
+ pos += 2;
+ pos += len;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS TextRecord TryWrite: " + ex.Message);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/UnknownRecord.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/UnknownRecord.cs
new file mode 100644
index 0000000..f41cd17
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/Records/UnknownRecord.cs
@@ -0,0 +1,73 @@
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class UnknownRecord : ResourceRecord
+{
+ public byte[] RDDATA { get; private set; } = Array.Empty();
+
+ public override string ToString()
+ {
+ string result = base.ToString() + "\n";
+ try { result += $"{nameof(RDDATA)}: {BitConverter.ToString(RDDATA)}\n"; } catch (Exception) { }
+ return result;
+ }
+
+ public UnknownRecord() { }
+
+ public UnknownRecord(ResourceRecord resourceRecord, byte[] rdData)
+ {
+ Name = resourceRecord.Name;
+ TYPE = resourceRecord.TYPE;
+ CLASS = resourceRecord.CLASS;
+ TimeToLive = resourceRecord.TimeToLive;
+ TTLDateTime = resourceRecord.TTLDateTime;
+ RDDATA = rdData;
+ }
+
+ public static ResourceRecord Parse(ResourceRecord resourceRecord, byte[] buffer, int pos, ushort rdLength)
+ {
+ try
+ {
+ int count = pos + rdLength;
+ if (count <= buffer.Length)
+ {
+ byte[] rdData = buffer[pos..count];
+ return new UnknownRecord(resourceRecord, rdData);
+ }
+ else return new UnknownRecord();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS UnknownRecord Parse: " + ex.Message);
+ return new UnknownRecord();
+ }
+ }
+
+ public static bool TryWrite(IResourceRecord resourceRecord, List bufferList, DnsMessage dnsMessage, ref int pos)
+ {
+ try
+ {
+ // RDLENGTH & RDDATA
+ if (resourceRecord is not ResourceRecord record) return false;
+
+ byte[] name = WriteRecordName(dnsMessage, resourceRecord.Name);
+ int lenOfTCT = 8; // TYPE(2), CLASS(2), TTL(4)
+ int iRecordLen = name.Length + lenOfTCT;
+
+ //bufferList.Clear(); // We Don't Replace The Whole Record To Be Able Modify TTL, etc.
+ byte[] rDataBuffer = record.RecordBuffer[10..];
+ bufferList.AddRange(rDataBuffer);
+
+ // Adjust Pos (Just In Case)
+ pos += rDataBuffer.Length;
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS UnknownRecord TryWrite: " + ex.Message);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/ResourceRecord.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/ResourceRecord.cs
new file mode 100644
index 0000000..9332eaa
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsMessage/ResourceRecord.cs
@@ -0,0 +1,608 @@
+using System.Diagnostics;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class Answers
+{
+ public List AnswerRecords = new();
+
+ public override string ToString()
+ {
+ if (!AnswerRecords.Any()) return string.Empty;
+ string result = "DNS Answer:\n";
+ foreach (IResourceRecord r in AnswerRecords) result += r.ToString() + "\n";
+ return result;
+ }
+
+ public static Answers Read(byte[] buffer, ref int pos, DnsMessage dnsMessage)
+ {
+ Answers answers = new()
+ {
+ AnswerRecords = ResourceRecord.Read(buffer, ref pos, dnsMessage.Header.AnswersCount)
+ };
+ return answers;
+ }
+
+ public static bool TryWrite(DnsMessage dnsMessage, ref int pos, out byte[] buffer)
+ {
+ return ResourceRecord.TryWrite(dnsMessage, dnsMessage.Answers.AnswerRecords, ref pos, out buffer);
+ }
+}
+
+public class Authorities
+{
+ public List AuthorityRecords = new();
+
+ public override string ToString()
+ {
+ if (!AuthorityRecords.Any()) return string.Empty;
+ string result = "DNS Authority:\n";
+ foreach (IResourceRecord r in AuthorityRecords) result += r.ToString() + "\n";
+ return result;
+ }
+
+ public static Authorities Read(byte[] buffer, ref int pos, DnsMessage dnsMessage)
+ {
+ Authorities authorities = new()
+ {
+ AuthorityRecords = ResourceRecord.Read(buffer, ref pos, dnsMessage.Header.AuthoritiesCount)
+ };
+ return authorities;
+ }
+
+ public static bool TryWrite(DnsMessage dnsMessage, ref int pos, out byte[] buffer)
+ {
+ return ResourceRecord.TryWrite(dnsMessage, dnsMessage.Authorities.AuthorityRecords, ref pos, out buffer);
+ }
+}
+
+public class Additionals
+{
+ public List AdditionalRecords = new();
+
+ public override string ToString()
+ {
+ if (!AdditionalRecords.Any()) return string.Empty;
+ string result = "DNS Additional:\n";
+ foreach (IResourceRecord r in AdditionalRecords) result += r.ToString() + "\n";
+ return result;
+ }
+
+ public static Additionals Read(byte[] buffer, ref int pos, DnsMessage dnsMessage)
+ {
+ Additionals additionals = new()
+ {
+ AdditionalRecords = ResourceRecord.Read(buffer, ref pos, dnsMessage.Header.AdditionalsCount)
+ };
+ return additionals;
+ }
+
+ public static bool TryWrite(DnsMessage dnsMessage, ref int pos, out byte[] buffer)
+ {
+ return ResourceRecord.TryWrite(dnsMessage, dnsMessage.Additionals.AdditionalRecords, ref pos, out buffer);
+ }
+}
+
+public interface IResourceRecord
+{
+ string Name { get; }
+ DnsEnums.RRType TYPE { get; }
+ DnsEnums.CLASS CLASS { get; }
+ uint TimeToLive { get; }
+ DateTime TTLDateTime { get; }
+ string ToString();
+}
+
+public class ResourceRecord : IResourceRecord
+{
+ public string Name { get; set; } = string.Empty; // Vary Bytes / Compressed Host, Contains Pointers
+ public DnsEnums.RRType TYPE { get; set; } // 2 Bytes / DNS Record, A Record = 1
+ public DnsEnums.CLASS CLASS { get; set; } // 2 Bytes / DNS Class, IN = 1
+ public uint TimeToLive { get; set; } // 4 Bytes / TTL In Sec
+ public DateTime TTLDateTime { get; set; }
+ internal byte[] RecordBuffer { get; set; } = Array.Empty();
+
+ public override string ToString()
+ {
+ string result = $"{nameof(Name)}: {Name}\n";
+ result += $"{nameof(TYPE)}: {TYPE}\n";
+ result += $"{nameof(CLASS)}: {CLASS}\n";
+ result += $"{nameof(TimeToLive)}: {TimeToLive}";
+ return result;
+ }
+
+ public static List Read(byte[] buffer, ref int pos, int resourceCount)
+ {
+ List resourceRecords = new();
+
+ try
+ {
+ int currentPos = pos;
+ for (int n = 0; n < resourceCount; n++)
+ {
+ int recordStartPos = pos;
+ ResourceRecord resourceRecord = new();
+
+ if (buffer.Length <= pos + 12) return resourceRecords;
+
+ string name = ReadRecordName(buffer, pos, out int length).ToString().Trim(); // Name Can Be Empty Here
+ resourceRecord.Name = name;
+ currentPos += length;
+ pos += length;
+
+ // TYPE
+ if (currentPos + 2 > buffer.Length) return resourceRecords;
+ bool rrTypeBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[currentPos..(currentPos + 2)], out ushort rrType);
+ if (!rrTypeBool) return resourceRecords;
+ resourceRecord.TYPE = (DnsEnums.RRType)Enum.Parse(typeof(DnsEnums.RRType), rrType.ToString());
+ currentPos += 2;
+ pos += 2;
+
+ // CLASS
+ if (currentPos + 2 > buffer.Length) return resourceRecords;
+ bool rClassBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[currentPos..(currentPos + 2)], out ushort rClass);
+ if (!rClassBool) return resourceRecords;
+ resourceRecord.CLASS = (DnsEnums.CLASS)Enum.Parse(typeof(DnsEnums.CLASS), rClass.ToString());
+ currentPos += 2;
+ pos += 2;
+
+ // TTL
+ if (currentPos + 4 > buffer.Length) return resourceRecords;
+ bool ttlBool = ByteArrayTool.TryConvertBytesToUInt32(buffer[currentPos..(currentPos + 4)], out uint ttl);
+ if (!ttlBool) return resourceRecords;
+ resourceRecord.TimeToLive = ttl;
+ resourceRecord.TTLDateTime = DateTime.UtcNow;
+ currentPos += 4;
+ pos += 4;
+
+ // RDLENGTH - The Length Of RDDATA
+ if (currentPos + 2 > buffer.Length) return resourceRecords;
+ bool rdLengthBool = ByteArrayTool.TryConvertBytesToUInt16(buffer[currentPos..(currentPos + 2)], out ushort rdLength);
+ if (!rdLengthBool) return resourceRecords;
+ currentPos += 2;
+ pos += 2;
+
+ // RDDATA
+ resourceRecord = ReadRDDATA(resourceRecord, buffer, pos, rdLength);
+ int rdLengthInt = Convert.ToInt32(rdLength);
+ currentPos += rdLengthInt;
+ pos += rdLengthInt;
+
+ if (pos <= buffer.Length && rdLengthInt != 0 && resourceRecord.TimeToLive != 0)
+ {
+ resourceRecord.RecordBuffer = buffer[recordStartPos..pos];
+ resourceRecords.Add(resourceRecord);
+ }
+ else
+ {
+ Debug.WriteLine("DNS ResourceRecord: Empty Record Received");
+ Debug.WriteLine($"Buffer Length: {buffer.Length} Position: {pos}");
+ Debug.WriteLine($"RDData Length: {rdLengthInt} TTL: {resourceRecord.TimeToLive}");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS ResourceRecord Read: " + ex.Message);
+ }
+
+ return resourceRecords;
+ }
+
+ public static bool TryWrite(DnsMessage dnsMessage, List resourceRecords, ref int pos, out byte[] buffer)
+ {
+ buffer = Array.Empty();
+ if (!resourceRecords.Any()) return true;
+ List rrsBufferList = new();
+
+ try
+ {
+ for (int n = 0; n < resourceRecords.Count; n++)
+ {
+ List rrBufferList = new();
+ IResourceRecord resourceRecord = resourceRecords[n];
+
+ byte[] name = WriteRecordName(dnsMessage, resourceRecord.Name, pos);
+
+ bool rTypeBool = ByteArrayTool.TryConvertUInt16ToBytes((ushort)resourceRecord.TYPE, out byte[] rType); // 2 Bytes
+ bool rClassBool = ByteArrayTool.TryConvertUInt16ToBytes((ushort)resourceRecord.CLASS, out byte[] rClass); // 2 Bytes
+ bool ttlBool = ByteArrayTool.TryConvertUInt32ToBytes(resourceRecord.TimeToLive, out byte[] ttl); // 4 Bytes
+
+ if (!rTypeBool || !rClassBool || !ttlBool) return false;
+
+ rrBufferList.AddRange(name);
+ rrBufferList.AddRange(rType);
+ rrBufferList.AddRange(rClass);
+ rrBufferList.AddRange(ttl);
+
+ pos += name.Length; // QNAME(Vari)
+ pos += 8; // TYPE(2), CLASS(2), TTL(4)
+
+ // RDLENGTH & RDDATA
+ bool writeRdDataBool = TryWriteRDDATA(resourceRecord, rrBufferList, dnsMessage, ref pos);
+ if (!writeRdDataBool) return false;
+
+ rrsBufferList.AddRange(rrBufferList);
+ }
+
+ buffer = rrsBufferList.ToArray();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS ResourceRecord TryWrite: " + ex.Message);
+ buffer = Array.Empty();
+ return false;
+ }
+ }
+
+ private enum CompressionPointer : ushort
+ {
+ NormalPointer = 11, // 2 Bytes
+ ExtendedPointer_8Bit_Offset = 01, // 3 Bytes
+ ExtendedPointer_16Bit_Offset = 10, // 4 Bytes
+ Label = 00,
+ Unknown = 99
+ }
+
+ private static bool TryGetPointer(byte pointerByte, out CompressionPointer pointer)
+ {
+ bool binaryStrBool = ByteArrayTool.TryConvertToBinary(pointerByte, out string binaryStr);
+ bool bitsBool = ByteArrayTool.TrySplitBinary(binaryStr, out bool[] bits);
+ bool pointBool = CommonTools.TryConvertToEnum(new[] { bits[0], bits[1] }, out pointer);
+ return binaryStrBool && bitsBool && pointBool;
+ }
+
+ private static int GetLabelPositionByPoint(CompressionPointer pointer, byte[] pointerBuffer, out int length)
+ {
+ // 0x3F To Convert Two First Bits to 00
+ if (pointer == CompressionPointer.NormalPointer)
+ {
+ length = 2;
+ int refPos = ((pointerBuffer[0] & 0x3F) << 8) | pointerBuffer[1];
+ return refPos;
+ }
+ else if (pointer == CompressionPointer.ExtendedPointer_8Bit_Offset)
+ {
+ length = 3;
+ int refPos = ((pointerBuffer[0] & 0x3F) << 8) | pointerBuffer[1];
+ int offset = pointerBuffer[2];
+ return refPos + offset;
+ }
+ else if (pointer == CompressionPointer.ExtendedPointer_16Bit_Offset)
+ {
+ length = 4;
+ int refPos = ((pointerBuffer[0] & 0x3F) << 8) | pointerBuffer[1];
+ int offset = (pointerBuffer[2] << 8) + pointerBuffer[3];
+ return refPos + offset;
+ }
+ else
+ {
+ length = 0;
+ return 0;
+ }
+ }
+
+ private static byte[] WritePointer(int labelPos)
+ {
+ try
+ {
+ // 00111111 = 63, 11111111 = 255, 0011111111111111 = 16383
+ int in14Bits = 16383; // Max: 16383
+ if (labelPos <= in14Bits) // 16383
+ {
+ // NormalPointer 11
+ //Debug.WriteLine("======= WritePointer: NormalPointer");
+ byte[] bytes = new byte[2];
+ bytes[0] = (byte)(labelPos >> 8);
+ bytes[1] = (byte)(labelPos & 255);
+ bytes[0] &= 63; // 00
+ bytes[0] |= 192; // 11
+ return bytes;
+ }
+ else if (labelPos <= in14Bits + 255) // 16383 + 255
+ {
+ // ExtendedPointer_8Bit_Offset 01
+ Debug.WriteLine("======= WritePointer: ExtendedPointer_8Bit_Offset " + labelPos);
+ byte[] bytes = new byte[3];
+ int offset = labelPos - in14Bits;
+ bytes[0] = (byte)(in14Bits >> 8);
+ bytes[1] = (byte)(in14Bits & 255);
+ bytes[2] = (byte)offset;
+ bytes[0] &= 63; // 00
+ bytes[0] |= 64; // 01
+ return bytes;
+ }
+ else if (labelPos <= in14Bits + 65535) // 16383 + 65535
+ {
+ // ExtendedPointer_16Bit_Offset 10
+ Debug.WriteLine("======= WritePointer: ExtendedPointer_16Bit_Offset " + labelPos);
+ byte[] bytes = new byte[4];
+ int offset = labelPos - in14Bits;
+ bytes[0] = (byte)(in14Bits >> 8);
+ bytes[1] = (byte)(in14Bits & 255);
+ bytes[2] = (byte)(offset >> 8);
+ bytes[3] = (byte)offset;
+ bytes[0] &= 63; // 00
+ bytes[0] |= 128; // 10
+ return bytes;
+ }
+ else return Array.Empty();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS ResourceRecord WritePointer: " + ex.Message);
+ return Array.Empty();
+ }
+ }
+
+ public static StringBuilder ReadRecordName(byte[] buffer, int startPostion, out int length, bool isEmail = false)
+ {
+ int overflow = 0;
+ return ReadRecordNameInternal(ref overflow, buffer, startPostion, out length, isEmail);
+ }
+
+ public static StringBuilder ReadRecordNameInternal(ref int innerOverflow, byte[] buffer, int startPostion, out int length, bool isEmail = false)
+ {
+ // https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4
+ // A sequence of labels ending in a zero octet
+ // A pointer
+ // A sequence of labels ending with a pointer
+ length = 0;
+ int pos = startPostion;
+ StringBuilder sb = new();
+ innerOverflow++;
+
+ try
+ {
+ int stackOverflowMax = 10;
+ int stackOverflow = 0;
+ bool isPointer = false;
+ while (true)
+ {
+ stackOverflow++;
+ // Detect Infinite Pointer To Pointer
+ if (stackOverflow > stackOverflowMax || innerOverflow > stackOverflowMax)
+ {
+ Debug.WriteLine("======== StackOverflowException =======");
+ break;
+ }
+
+ if (pos > buffer.Length) break;
+
+ bool pointBool = TryGetPointer(buffer[pos], out CompressionPointer pointer);
+ if (!pointBool)
+ {
+ Debug.WriteLine("DNS TryGetPoint Failed");
+ break;
+ }
+
+ if (pointer == CompressionPointer.NormalPointer)
+ {
+ if (pos + 2 > buffer.Length) break;
+ int refPos = GetLabelPositionByPoint(pointer, buffer[pos..(pos + 2)], out int len);
+ string recordName = ReadRecordNameInternal(ref innerOverflow, buffer, refPos, out _).ToString();
+ if (string.IsNullOrEmpty(recordName)) break;
+ sb.Append(recordName);
+ pos += len;
+ isPointer = true;
+ }
+ else if (pointer == CompressionPointer.ExtendedPointer_8Bit_Offset)
+ {
+ if (pos + 3 > buffer.Length) break;
+ int refPos = GetLabelPositionByPoint(pointer, buffer[pos..(pos + 3)], out int len);
+ string recordName = ReadRecordNameInternal(ref innerOverflow, buffer, refPos, out _).ToString();
+ if (string.IsNullOrEmpty(recordName)) break;
+ sb.Append(recordName);
+ pos += len;
+ isPointer = true;
+ Debug.WriteLine("=============================== " + pointer + " " + sb.ToString());
+ }
+ else if (pointer == CompressionPointer.ExtendedPointer_16Bit_Offset)
+ {
+ if (pos + 4 > buffer.Length) break;
+ int refPos = GetLabelPositionByPoint(pointer, buffer[pos..(pos + 4)], out int len);
+ string recordName = ReadRecordNameInternal(ref innerOverflow, buffer, refPos, out _).ToString();
+ if (string.IsNullOrEmpty(recordName)) break;
+ sb.Append(recordName);
+ pos += len;
+ isPointer = true;
+ Debug.WriteLine("=============================== " + pointer);
+ }
+ else if (pointer == CompressionPointer.Label)
+ {
+ int len = buffer[pos];
+ pos++;
+ if (pos + len >= buffer.Length) break;
+ string label = Encoding.UTF8.GetString(buffer[pos..(pos + len)]) + ".";
+ sb.Append(label);
+ pos += len;
+ }
+ else
+ {
+ Debug.WriteLine("DNS Unknown CompressionPointer: " + pointer);
+ break;
+ }
+
+ if (pos >= buffer.Length) break; // End Of Buffer
+
+ if (buffer[pos] == 0 && !isPointer)
+ {
+ pos++; // Label Terminator 0x00
+ break;
+ }
+
+ if (isPointer) break;
+ }
+
+ //Debug.WriteLine($"======== Found {sb} In {innerOverflow + 1} Loops =======");
+ if (sb.Length > 0 && sb[^1] == '.') sb.Remove(sb.Length - 1, 1);
+
+ StringBuilder sbResult = new();
+ bool email = false;
+ for (int n = 0; n < sb.Length; n++)
+ {
+ char c = sb[n];
+ if (isEmail && !email && c.Equals('.'))
+ {
+ sbResult.Append('@');
+ email = true;
+ }
+ else sbResult.Append(c);
+ }
+
+ string nameResult = sbResult.ToString().Trim();
+ if (string.IsNullOrEmpty(nameResult))
+ {
+ length = 1;
+ }
+ else
+ {
+ Regex regex = new("^[0-9a-zA-Z-.@:]+$"); // Domain / IPv4 / IPv6 / Email (IP Is An Invalid Question)
+ if (stackOverflow <= stackOverflowMax && innerOverflow <= stackOverflowMax && regex.IsMatch(nameResult))
+ {
+ length = pos - startPostion;
+ }
+ else
+ {
+ // Unexpected EOF
+ length = MsmhAgnosticServer.MaxDataSize - buffer.Length;
+ sbResult.Clear();
+ }
+ }
+
+ return sbResult;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS ReadRecordName: " + ex.Message);
+ return sb;
+ }
+ }
+
+ public static byte[] WriteRecordName(DnsMessage dnsMessage, string domainName, int domainPos = -1)
+ {
+ // https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4
+ // A sequence of labels ending in a zero octet
+ // A pointer
+ // A sequence of labels ending with a pointer
+ // Limitation: https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.4
+ List bufferList = new();
+
+ try
+ {
+ if (string.IsNullOrEmpty(domainName))
+ {
+ bufferList.Add(0x00); // Terminator
+ return bufferList.ToArray();
+ }
+
+ // Convert @ To Dot
+ string domainNameConverted = string.Empty;
+ for (int n = 0; n < domainName.Length; n++)
+ {
+ char c = domainName[n];
+ if (c.Equals('@')) c = '.';
+ domainNameConverted += c;
+ }
+
+ bool isPointer = false;
+ int labelSequencePos = domainPos;
+ string[] labels = domainNameConverted.Split('.');
+ for (int n = 0; n < labels.Length; n++)
+ {
+ string label = labels[n];
+
+ if (string.IsNullOrEmpty(label))
+ {
+ bufferList.Add(0x00);
+ continue;
+ }
+
+ string labelSequence = string.Join('.', labels[n..]);
+ bool exist = isExist(labelSequence, out Tuple? lp);
+ if (exist && lp != null)
+ {
+ // Create Pointer
+ byte[] pointerBytes = WritePointer(lp.Item2);
+ bufferList.AddRange(pointerBytes);
+ isPointer = true;
+ //Debug.WriteLine($"======= Used Pointer For: {lp.Item1} Pos: {lp.Item2}");
+ break;
+ }
+ else
+ {
+ // Create Label
+ bufferList.Add(Convert.ToByte(label.Length));
+ bufferList.AddRange(Encoding.UTF8.GetBytes(label));
+ //Debug.WriteLine($"------- Label Created: {label}");
+
+ // Add Current Domain Name Labels To Compress DnsMessage
+ if (domainPos != -1 && !string.IsNullOrEmpty(label) && !string.IsNullOrEmpty(labelSequence))
+ {
+ if (n > 0)
+ {
+ string preLabel = labels[n - 1];
+ labelSequencePos = labelSequencePos + Encoding.UTF8.GetBytes(preLabel).Length + 1; // 1 Byte Of Label Length
+ }
+
+ dnsMessage.LabelPositions.Add(new Tuple(labelSequence, labelSequencePos));
+ //Debug.WriteLine($"======= Label Sequence Saved: {labelSequence} Pos: {labelSequencePos}");
+ }
+ }
+ }
+
+ if (!isPointer) bufferList.Add(0x00); // Domain Terminator If Doesn't End With A Pointer
+
+ bool isExist(string newLabel, out Tuple? lpOut)
+ {
+ lpOut = null;
+ foreach (Tuple lp in dnsMessage.LabelPositions.ToList())
+ if (lp != null && newLabel.Equals(lp.Item1))
+ {
+ lpOut = lp;
+ return true;
+ }
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS ResourceRecord WriteRecordLabel: " + ex.Message);
+ }
+
+ return bufferList.ToArray();
+ }
+
+ protected static ResourceRecord ReadRDDATA(ResourceRecord resourceRecord, byte[] buffer, int pos, ushort rLength)
+ {
+ return resourceRecord.TYPE switch
+ {
+ DnsEnums.RRType.A => ARecord.Parse(resourceRecord, buffer, pos),
+ DnsEnums.RRType.AAAA => AaaaRecord.Parse(resourceRecord, buffer, pos),
+ DnsEnums.RRType.CNAME => CNameRecord.Parse(resourceRecord, buffer, pos),
+ DnsEnums.RRType.MX => MxRecord.Parse(resourceRecord, buffer, pos),
+ DnsEnums.RRType.NS => NsRecord.Parse(resourceRecord, buffer, pos),
+ DnsEnums.RRType.SOA => SoaRecord.Parse(resourceRecord, buffer, pos),
+ DnsEnums.RRType.TEXT => TextRecord.Parse(resourceRecord, buffer, pos, rLength),
+ _ => UnknownRecord.Parse(resourceRecord, buffer, pos, rLength)
+ };
+ }
+
+ protected static bool TryWriteRDDATA(IResourceRecord resourceRecord, List bufferList, DnsMessage dnsMessage, ref int pos)
+ {
+ return resourceRecord.TYPE switch
+ {
+ DnsEnums.RRType.A => ARecord.TryWrite(resourceRecord, bufferList, ref pos),
+ DnsEnums.RRType.AAAA => AaaaRecord.TryWrite(resourceRecord, bufferList, ref pos),
+ DnsEnums.RRType.CNAME => CNameRecord.TryWrite(resourceRecord, bufferList, dnsMessage, ref pos),
+ DnsEnums.RRType.MX => MxRecord.TryWrite(resourceRecord, bufferList, dnsMessage, ref pos),
+ DnsEnums.RRType.NS => NsRecord.TryWrite(resourceRecord, bufferList, dnsMessage, ref pos),
+ DnsEnums.RRType.SOA => SoaRecord.TryWrite(resourceRecord, bufferList, dnsMessage, ref pos),
+ DnsEnums.RRType.TEXT => TextRecord.TryWrite(resourceRecord, bufferList, ref pos),
+ _ => UnknownRecord.TryWrite(resourceRecord, bufferList, dnsMessage, ref pos)
+ };
+ }
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsRequest.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsRequest.cs
new file mode 100644
index 0000000..4b669a5
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsRequest.cs
@@ -0,0 +1,90 @@
+using System.Diagnostics;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class DnsRequest
+{
+ public Socket? Socket_ { get; set; }
+ public SslStream? Ssl_Stream { get; set; }
+ public SslKind Ssl_Kind { get; set; } = SslKind.NonSSL;
+ public EndPoint? LocalEndPoint { get; set; }
+ public EndPoint? RemoteEndPoint { get; set; }
+ public byte[] Buffer { get; set; } = Array.Empty();
+ public DnsEnums.DnsProtocol Protocol { get; set; }
+
+ public DnsRequest() { }
+
+ public DnsRequest(Socket? socket, SslStream? sslStream, SslKind sslKind, EndPoint? localEndPoint, EndPoint? remoteEndPoint, byte[] buffer, DnsEnums.DnsProtocol protocol)
+ {
+ Socket_ = socket;
+ Ssl_Stream = sslStream;
+ Ssl_Kind = sslKind;
+ LocalEndPoint = localEndPoint;
+ RemoteEndPoint = remoteEndPoint;
+ Buffer = buffer;
+ Protocol = protocol;
+ }
+
+ public async Task SendToAsync(byte[] aBuffer)
+ {
+ try
+ {
+ if (Buffer.Length == 0 || LocalEndPoint == null || RemoteEndPoint == null)
+ {
+ Disconnect();
+ return;
+ }
+
+ if (Ssl_Kind == SslKind.NonSSL && Socket_ != null)
+ await Socket_.SendToAsync(aBuffer, SocketFlags.None, RemoteEndPoint);
+
+ if (Ssl_Kind == SslKind.SSL && Ssl_Stream != null && Protocol == DnsEnums.DnsProtocol.DoH)
+ {
+ bool isDohWriteSuccess = DnsMessage.TryWriteDoHResponse(aBuffer, out byte[] result);
+ if (isDohWriteSuccess) await Ssl_Stream.WriteAsync(result);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS DnsRequest SendToAsync: " + ex.Message);
+ }
+ }
+
+ public async Task SendFailedResponseAsync()
+ {
+ try
+ {
+ if (Socket_ == null || Buffer.Length == 0 || LocalEndPoint == null || RemoteEndPoint == null)
+ {
+ Disconnect();
+ return;
+ }
+ DnsMessage dm = DnsMessage.Read(Buffer, Protocol);
+ dm = DnsMessage.CreateFailedResponse(dm);
+ DnsMessage.TryWrite(dm, out byte[] failedBuffer);
+ await SendToAsync(failedBuffer);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNS ProxyRequest SendFailedResponseAsync: " + ex.Message);
+ }
+ }
+
+ public void Disconnect()
+ {
+ if (Protocol != DnsEnums.DnsProtocol.UDP)
+ {
+ try
+ {
+ Socket_?.Shutdown(SocketShutdown.Both);
+ Socket_?.Dispose();
+ }
+ catch (Exception) { }
+ }
+
+ try { Ssl_Stream?.Dispose(); } catch (Exception) { }
+ }
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DNSCryptConfigEditor.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DNSCryptConfigEditor.cs
similarity index 99%
rename from MsmhToolsClass/MsmhToolsClass/DnsTool/DNSCryptConfigEditor.cs
rename to MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DNSCryptConfigEditor.cs
index 548cac0..c771c85 100644
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DNSCryptConfigEditor.cs
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DNSCryptConfigEditor.cs
@@ -1,9 +1,8 @@
-using System;
-using System.Diagnostics;
+using System.Diagnostics;
using System.Net;
using System.Text;
-namespace MsmhToolsClass.DnsTool;
+namespace MsmhToolsClass.MsmhAgnosticServer;
public class DNSCryptConfigEditor
{
@@ -248,4 +247,4 @@ public async Task WriteAsync()
Debug.WriteLine("DNSCryptConfigEditor WriteAsync: " + ex.Message);
}
}
-}
\ No newline at end of file
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DNSCryptStampGenerator.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DNSCryptStampGenerator.cs
similarity index 99%
rename from MsmhToolsClass/MsmhToolsClass/DnsTool/DNSCryptStampGenerator.cs
rename to MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DNSCryptStampGenerator.cs
index f6b8cdb..a58b4f6 100644
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DNSCryptStampGenerator.cs
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DNSCryptStampGenerator.cs
@@ -1,8 +1,7 @@
-using System;
-using System.Diagnostics;
+using System.Diagnostics;
using System.Text;
-namespace MsmhToolsClass.DnsTool;
+namespace MsmhToolsClass.MsmhAgnosticServer;
public class DNSCryptStampGenerator
{
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DNSCryptStampReader.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DNSCryptStampReader.cs
similarity index 59%
rename from MsmhToolsClass/MsmhToolsClass/DnsTool/DNSCryptStampReader.cs
rename to MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DNSCryptStampReader.cs
index c7581ec..6f23fcd 100644
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DNSCryptStampReader.cs
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DNSCryptStampReader.cs
@@ -1,15 +1,15 @@
-using System;
-using System.Diagnostics;
+using System.Diagnostics;
+using System.Net;
using System.Text;
-namespace MsmhToolsClass.DnsTool;
+namespace MsmhToolsClass.MsmhAgnosticServer;
public class DNSCryptStampReader
{
// More info: https://dnscrypt.info/stamps-specifications/
public bool IsDecryptionSuccess { get; private set; } = false;
public string Stamp { get; private set; } = string.Empty;
- public string IP { get; private set; } = string.Empty;
+ public IPAddress IP { get; private set; } = IPAddress.None;
public int Port { get; private set; } = -1;
public string Host { get; private set; } = string.Empty;
public string Path { get; private set; } = string.Empty;
@@ -63,6 +63,7 @@ private struct StampProtocolName
public DNSCryptStampReader(string stamp)
{
+ if (string.IsNullOrEmpty(stamp)) return;
Stamp = stamp;
if (stamp.StartsWith("sdns://"))
@@ -75,7 +76,7 @@ public DNSCryptStampReader(string stamp)
// Get Stamp Binary
byte[] stampBinary = EncodingTool.UrlDecode(stamp);
- // Get Protocol
+ // Get ListenerProtocol
if (stampBinary.Length > 0)
{
Protocol = GetProtocol(stampBinary, out string protocolName);
@@ -95,12 +96,19 @@ public DNSCryptStampReader(string stamp)
}
// Get IP, Port, Host, Path, PublicKey, ProviderName, Hashi, Bootstraps
- bool isOk = DecryptTheRest(stampBinary, out string ip, out int port, out string host, out string path, out string publicKey,
+ bool isOk = DecryptTheRest(stampBinary, out string ipStr, out int port, out string host, out string path, out string publicKey,
out string providerName, out List hashi, out List bootstraps);
if (!isOk) return;
- IP = ip;
+ bool isIpOk = IPAddress.TryParse(ipStr, out IPAddress? ip);
+ IP = isIpOk && ip != null ? ip : IPAddress.None;
+ if (IP == IPAddress.None && !string.IsNullOrEmpty(host))
+ {
+ isIpOk = IPAddress.TryParse(host, out ip);
+ IP = isIpOk && ip != null ? ip : IPAddress.None;
+ }
+
Port = port;
Host = host;
Path = path;
@@ -111,10 +119,7 @@ public DNSCryptStampReader(string stamp)
IsDecryptionSuccess = true;
}
- catch (Exception)
- {
- // do nothing
- }
+ catch (Exception) { }
}
else
{
@@ -124,6 +129,12 @@ public DNSCryptStampReader(string stamp)
private static StampProtocol GetProtocol(byte[] stampBinary, out string protocolName)
{
+ if (stampBinary.Length < 1)
+ {
+ protocolName = StampProtocolName.Unknown;
+ return StampProtocol.Unknown;
+ }
+
byte stampProtocol = stampBinary[0];
if (stampProtocol == 0x00)
@@ -175,11 +186,21 @@ private static StampProtocol GetProtocol(byte[] stampBinary, out string protocol
private static void GetStampProperties(byte[] stampBinary, out bool isDNSSec, out bool isNoLog, out bool isNoFilter)
{
- byte dnsCryptProperties = stampBinary[1];
+ try
+ {
+ byte dnsCryptProperties = stampBinary[1];
- isDNSSec = Convert.ToBoolean((dnsCryptProperties >> 0) & 1);
- isNoLog = Convert.ToBoolean((dnsCryptProperties >> 1) & 1);
- isNoFilter = Convert.ToBoolean((dnsCryptProperties >> 2) & 1);
+ isDNSSec = Convert.ToBoolean((dnsCryptProperties >> 0) & 1);
+ isNoLog = Convert.ToBoolean((dnsCryptProperties >> 1) & 1);
+ isNoFilter = Convert.ToBoolean((dnsCryptProperties >> 2) & 1);
+ }
+ catch (Exception ex)
+ {
+ isDNSSec = false;
+ isNoLog = false;
+ isNoFilter = false;
+ Debug.WriteLine("DNSCryptStampReader GetStampProperties: " + ex.Message);
+ }
}
private bool DecryptTheRest(byte[] stampBinary, out string ip, out int port, out string host, out string path, out string publicKey,
@@ -194,153 +215,160 @@ private bool DecryptTheRest(byte[] stampBinary, out string ip, out int port, out
hashi = new List();
bootstraps = new List();
- int position = 0;
- position += 1; // Skip Protocol
- if (Protocol == StampProtocol.PlainDNS)
+ try
{
- position += 8; // Skip Properties
+ int position = 0;
+ position += 1; // Skip ListenerProtocol
+ if (Protocol == StampProtocol.PlainDNS)
+ {
+ position += 8; // Skip Properties
- // LP(addr [:port])
- position = LPHostIpPort(position, stampBinary, DefaultPort.PlainDNS, out ip, out port);
- if (position == -1) return false;
- return true;
- }
- else if (Protocol == StampProtocol.DnsCrypt)
- {
- position += 8; // Skip Properties
+ // LP(addr [:port])
+ position = LPHostIpPort(position, stampBinary, DefaultPort.PlainDNS, out ip, out port);
+ if (position == -1) return false;
+ return true;
+ }
+ else if (Protocol == StampProtocol.DnsCrypt)
+ {
+ position += 8; // Skip Properties
- // LP(addr [:port])
- position = LPHostIpPort(position, stampBinary, DefaultPort.DnsCrypt, out ip, out port);
- if (position == -1) return false;
+ // LP(addr [:port])
+ position = LPHostIpPort(position, stampBinary, DefaultPort.DnsCrypt, out ip, out port);
+ if (position == -1) return false;
- // LP(pk)
- position = LPPublicKey(position, stampBinary, out publicKey);
- if (position == -1) return false;
+ // LP(pk)
+ position = LPPublicKey(position, stampBinary, out publicKey);
+ if (position == -1) return false;
- // LP(providerName))
- position = LP(position, stampBinary, out providerName);
- if (position == -1) return false;
- return true;
- }
- else if (Protocol == StampProtocol.DoH)
- {
- position += 8; // Skip Properties
+ // LP(providerName))
+ position = LP(position, stampBinary, out providerName);
+ if (position == -1) return false;
+ return true;
+ }
+ else if (Protocol == StampProtocol.DoH)
+ {
+ position += 8; // Skip Properties
- // LP(addr)
- position = LPHostIpPort(position, stampBinary, DefaultPort.DoH, out ip, out port);
- if (position == -1) return false;
+ // LP(addr)
+ position = LPHostIpPort(position, stampBinary, DefaultPort.DoH, out ip, out port);
+ if (position == -1) return false;
- // VLP(hash1, hash2, ...hashn)
- position = VLPHash(position, stampBinary, out hashi);
- if (position == -1) return false;
+ // VLP(hash1, hash2, ...hashn)
+ position = VLPHash(position, stampBinary, out hashi);
+ if (position == -1) return false;
- // LP(hostname[:port])
- position = LPHostIpPort(position, stampBinary, port, out host, out port);
- if (position == -1) return false;
+ // LP(hostname[:port])
+ position = LPHostIpPort(position, stampBinary, port, out host, out port);
+ if (position == -1) return false;
- // LP(path)
- position = LP(position, stampBinary, out path);
- if (position == -1) return false;
+ // LP(path)
+ position = LP(position, stampBinary, out path);
+ if (position == -1) return false;
- // VLP(bootstrap_ip1, bootstrap_ip2, ...bootstrap_ipn) - Optional
- position = VLPBootstrap(position, stampBinary, out bootstraps);
- if (position == -1) return false;
- return true;
- }
- else if (Protocol == StampProtocol.DoT)
- {
- position += 8; // Skip Properties
+ // VLP(bootstrap_ip1, bootstrap_ip2, ...bootstrap_ipn) - Optional
+ position = VLPBootstrap(position, stampBinary, out bootstraps);
+ if (position == -1) return false;
+ return true;
+ }
+ else if (Protocol == StampProtocol.DoT)
+ {
+ position += 8; // Skip Properties
- // LP(addr)
- position = LPHostIpPort(position, stampBinary, DefaultPort.DoT, out ip, out port);
- if (position == -1) return false;
+ // LP(addr)
+ position = LPHostIpPort(position, stampBinary, DefaultPort.DoT, out ip, out port);
+ if (position == -1) return false;
- // VLP(hash1, hash2, ...hashn)
- position = VLPHash(position, stampBinary, out hashi);
- if (position == -1) return false;
+ // VLP(hash1, hash2, ...hashn)
+ position = VLPHash(position, stampBinary, out hashi);
+ if (position == -1) return false;
- // LP(hostname [:port])
- position = LPHostIpPort(position, stampBinary, port, out host, out port);
- if (position == -1) return false;
+ // LP(hostname [:port])
+ position = LPHostIpPort(position, stampBinary, port, out host, out port);
+ if (position == -1) return false;
- // VLP(bootstrap_ip1, bootstrap_ip2, ...bootstrap_ipn) - Optional
- position = VLPBootstrap(position, stampBinary, out bootstraps);
- if (position == -1) return false;
- return true;
- }
- else if (Protocol == StampProtocol.DoQ)
- {
- position += 8; // Skip Properties
+ // VLP(bootstrap_ip1, bootstrap_ip2, ...bootstrap_ipn) - Optional
+ position = VLPBootstrap(position, stampBinary, out bootstraps);
+ if (position == -1) return false;
+ return true;
+ }
+ else if (Protocol == StampProtocol.DoQ)
+ {
+ position += 8; // Skip Properties
- // LP(addr)
- position = LPHostIpPort(position, stampBinary, DefaultPort.DoQ, out ip, out port);
- if (position == -1) return false;
+ // LP(addr)
+ position = LPHostIpPort(position, stampBinary, DefaultPort.DoQ, out ip, out port);
+ if (position == -1) return false;
- // VLP(hash1, hash2, ...hashn)
- position = VLPHash(position, stampBinary, out hashi);
- if (position == -1) return false;
+ // VLP(hash1, hash2, ...hashn)
+ position = VLPHash(position, stampBinary, out hashi);
+ if (position == -1) return false;
- // LP(hostname [:port])
- position = LPHostIpPort(position, stampBinary, port, out host, out port);
- if (position == -1) return false;
+ // LP(hostname [:port])
+ position = LPHostIpPort(position, stampBinary, port, out host, out port);
+ if (position == -1) return false;
- // VLP(bootstrap_ip1, bootstrap_ip2, ...bootstrap_ipn) - Optional
- position = VLPBootstrap(position, stampBinary, out bootstraps);
- if (position == -1) return false;
- return true;
- }
- else if (Protocol == StampProtocol.ObliviousDohTarget)
- {
- position += 8; // Skip Properties
+ // VLP(bootstrap_ip1, bootstrap_ip2, ...bootstrap_ipn) - Optional
+ position = VLPBootstrap(position, stampBinary, out bootstraps);
+ if (position == -1) return false;
+ return true;
+ }
+ else if (Protocol == StampProtocol.ObliviousDohTarget)
+ {
+ position += 8; // Skip Properties
- // LP(hostname [:port])
- position = LPHostIpPort(position, stampBinary, DefaultPort.ObliviousDohTarget, out host, out port);
- if (position == -1) return false;
+ // LP(hostname [:port])
+ position = LPHostIpPort(position, stampBinary, DefaultPort.ObliviousDohTarget, out host, out port);
+ if (position == -1) return false;
- // LP(path)
- position = LP(position, stampBinary, out path);
- if (position == -1) return false;
- return true;
- }
- else if (Protocol == StampProtocol.AnonymizedDNSCryptRelay)
- {
- // Anonymized DNSCrypt Relay doesn't have properties to skip
+ // LP(path)
+ position = LP(position, stampBinary, out path);
+ if (position == -1) return false;
+ return true;
+ }
+ else if (Protocol == StampProtocol.AnonymizedDNSCryptRelay)
+ {
+ // Anonymized DNSCrypt Relay doesn't have properties to skip
- // LP(addr)
- position = LPHostIpPort(position, stampBinary, DefaultPort.AnonymizedDNSCryptRelay, out ip, out port);
- if (position == -1) return false;
- return true;
- }
- else if (Protocol == StampProtocol.ObliviousDohRelay)
- {
- position += 8; // Skip Properties
+ // LP(addr)
+ position = LPHostIpPort(position, stampBinary, DefaultPort.AnonymizedDNSCryptRelay, out ip, out port);
+ if (position == -1) return false;
+ return true;
+ }
+ else if (Protocol == StampProtocol.ObliviousDohRelay)
+ {
+ position += 8; // Skip Properties
- // LP(addr)
- position = LPHostIpPort(position, stampBinary, DefaultPort.ObliviousDohRelay, out ip, out port);
- if (position == -1) return false;
+ // LP(addr)
+ position = LPHostIpPort(position, stampBinary, DefaultPort.ObliviousDohRelay, out ip, out port);
+ if (position == -1) return false;
- // VLP(hash1, hash2, ...hashn)
- position = VLPHash(position, stampBinary, out hashi);
- if (position == -1) return false;
+ // VLP(hash1, hash2, ...hashn)
+ position = VLPHash(position, stampBinary, out hashi);
+ if (position == -1) return false;
- // LP(hostname [:port])
- position = LPHostIpPort(position, stampBinary, port, out host, out port);
- if (position == -1) return false;
+ // LP(hostname [:port])
+ position = LPHostIpPort(position, stampBinary, port, out host, out port);
+ if (position == -1) return false;
- // LP(path)
- position = LP(position, stampBinary, out path);
- if (position == -1) return false;
+ // LP(path)
+ position = LP(position, stampBinary, out path);
+ if (position == -1) return false;
- // VLP(bootstrap_ip1, bootstrap_ip2, ...bootstrap_ipn) - Optional
- position = VLPBootstrap(position, stampBinary, out bootstraps);
- if (position == -1) return false;
- return true;
+ // VLP(bootstrap_ip1, bootstrap_ip2, ...bootstrap_ipn) - Optional
+ position = VLPBootstrap(position, stampBinary, out bootstraps);
+ if (position == -1) return false;
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DNSCryptStampReader DecryptTheRest: " + ex.Message);
}
return true;
}
- private int LPHostIpPort(int position, byte[] stampBinary, int defaultPort, out string host, out int port)
+ private static int LPHostIpPort(int position, byte[] stampBinary, int defaultPort, out string host, out int port)
{
try
{
@@ -372,7 +400,7 @@ private int LPHostIpPort(int position, byte[] stampBinary, int defaultPort, out
}
}
- private int LP(int position, byte[] stampBinary, out string outResult)
+ private static int LP(int position, byte[] stampBinary, out string outResult)
{
try
{
@@ -401,7 +429,7 @@ private int LP(int position, byte[] stampBinary, out string outResult)
}
}
- private int LPPublicKey(int position, byte[] stampBinary, out string outResult)
+ private static int LPPublicKey(int position, byte[] stampBinary, out string outResult)
{
try
{
@@ -430,7 +458,7 @@ private int LPPublicKey(int position, byte[] stampBinary, out string outResult)
}
}
- private int VLPHash(int position, byte[] stampBinary, out List hashi)
+ private static int VLPHash(int position, byte[] stampBinary, out List hashi)
{
try
{
@@ -477,7 +505,7 @@ private int VLPHash(int position, byte[] stampBinary, out List hashi)
}
}
- private int VLPBootstrap(int position, byte[] stampBinary, out List bootstraps)
+ private static int VLPBootstrap(int position, byte[] stampBinary, out List bootstraps)
{
try
{
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsDeDup.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DnsDeDup.cs
similarity index 88%
rename from MsmhToolsClass/MsmhToolsClass/DnsTool/DnsDeDup.cs
rename to MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DnsDeDup.cs
index ef5ea78..d0cc8a2 100644
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/DnsDeDup.cs
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DnsDeDup.cs
@@ -1,7 +1,7 @@
-using MsmhToolsClass.DnsTool;
-using System.Diagnostics;
+using System.Diagnostics;
+using System.Net;
-namespace MsmhToolsClass.MsmhToolsClass.DnsTool;
+namespace MsmhToolsClass.MsmhAgnosticServer;
public class DnsDeDup
{
@@ -31,7 +31,7 @@ await Task.Run(async () =>
if (CTS.IsCancellationRequested) break;
Status = $"Working on {n + 1} of {DnsList.Count}";
-
+
string dns1 = DnsList[n].Trim();
bool isSame = false;
for (int n2 = 0; n2 < DedupDnsList.Count; n2++)
@@ -52,7 +52,7 @@ await Task.Run(async () =>
if (!isSame) DedupDnsList.Add(dns1);
}
});
-
+
return DedupDnsList;
}
@@ -60,18 +60,18 @@ private async Task IsTheSame(string dns1, string dns2)
{
return await Task.Run(() =>
{
- string ip1 = string.Empty;
+ IPAddress ip1 = IPAddress.None;
DnsReader dr1 = new(dns1, null);
NetworkTool.GetUrlDetails(dns1, dr1.Port, out string scheme1, out string host1, out _, out _, out int port1, out string path1, out _);
if (dr1.IsDnsCryptStamp)
{
host1 = dr1.Host;
- ip1 = dr1.IP ?? string.Empty;
+ ip1 = dr1.IP;
port1 = dr1.Port;
path1 = dr1.Path;
}
- string ip2 = string.Empty;
+ IPAddress ip2 = IPAddress.None;
DnsReader dr2 = new(dns2, null);
NetworkTool.GetUrlDetails(dns2, dr2.Port, out string scheme2, out string host2, out _, out _, out int port2, out string path2, out _);
if (dr2.IsDnsCryptStamp)
@@ -80,7 +80,7 @@ private async Task IsTheSame(string dns1, string dns2)
if (dr1.Protocol == dr2.Protocol) scheme2 = scheme1;
host2 = dr2.Host;
- ip2 = dr2.IP ?? string.Empty;
+ ip2 = dr2.IP;
port2 = dr2.Port;
path2 = dr2.Path;
}
@@ -102,13 +102,13 @@ private async Task IsTheSame(string dns1, string dns2)
if (dr1.IsDnsCryptStamp && !dr2.IsDnsCryptStamp)
{
- if (string.IsNullOrEmpty(host1)) host1 = dr1.IP ?? string.Empty;
+ if (string.IsNullOrEmpty(host1)) host1 = dr1.IP.ToString();
result = scheme1.Equals(scheme2) && host1.Equals(host2) && port1.Equals(port2) && path1.Equals(path2);
}
if (!dr1.IsDnsCryptStamp && dr2.IsDnsCryptStamp)
{
- if (string.IsNullOrEmpty(host2)) host2 = dr2.IP ?? string.Empty;
+ if (string.IsNullOrEmpty(host2)) host2 = dr2.IP.ToString();
result = scheme1.Equals(scheme2) && host1.Equals(host2) && port1.Equals(port2) && path1.Equals(path2);
}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DnsReader.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DnsReader.cs
new file mode 100644
index 0000000..5b586f9
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/DnsReader.cs
@@ -0,0 +1,178 @@
+using System.Diagnostics;
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class DnsReader
+{
+ public string Dns { get; private set; } = string.Empty;
+ public string Scheme { get; private set; } = string.Empty;
+ public string Host { get; private set; } = string.Empty;
+ public bool IsHostIP { get; private set; } = false;
+ public IPAddress IP { get; private set; } = IPAddress.None;
+ public int Port { get; private set; }
+ public string Path { get; private set; } = string.Empty;
+ public string CompanyName { get; private set; } = string.Empty;
+ private string CompanyNameDataFileContent { get; set; } = string.Empty;
+ public DnsEnums.DnsProtocol Protocol { get; private set; } = DnsEnums.DnsProtocol.Unknown;
+ public string ProtocolName { get; private set; } = string.Empty;
+ public bool IsDnsCryptStamp { get; private set; } = false;
+ public DNSCryptStampReader StampReader { get; private set; } = new(string.Empty);
+
+ public DnsReader() { }
+
+ ///
+ /// Read any DNS
+ ///
+ /// DNS Address
+ /// File content to get company name. each line e.g. 8.8.8.8|Google Inc.
+ public DnsReader(string dns, string? companyNameDataFileContent = null)
+ {
+ Dns = dns;
+
+ if (!string.IsNullOrEmpty(companyNameDataFileContent))
+ CompanyNameDataFileContent = companyNameDataFileContent;
+
+ Protocol = DnsEnums.DnsProtocol.Unknown;
+ ProtocolName = DnsEnums.DnsProtocolName.Unknown;
+
+ if (string.IsNullOrEmpty(Dns)) return;
+
+ try
+ {
+ if (dns.ToLower().StartsWith("sdns://"))
+ {
+ IsDnsCryptStamp = true;
+
+ // Decode Stamp
+ DNSCryptStampReader stamp = new(dns);
+ if (stamp != null && stamp.IsDecryptionSuccess)
+ {
+ Scheme = "stamp://";
+ Host = stamp.Host;
+ if (!string.IsNullOrEmpty(Host))
+ IsHostIP = IPAddress.TryParse(Host, out _);
+ IP = stamp.IP;
+ Port = stamp.Port;
+ Path = stamp.Path;
+ Protocol = ParseProtocol(stamp.Protocol);
+
+ if (Protocol == DnsEnums.DnsProtocol.DoH || Protocol == DnsEnums.DnsProtocol.ObliviousDohTarget) Scheme = "https://";
+ else if (Protocol == DnsEnums.DnsProtocol.DoQ) Scheme = "quic://";
+ else if (Protocol == DnsEnums.DnsProtocol.DoT) Scheme = "tls://";
+ else if (Protocol == DnsEnums.DnsProtocol.TCP) Scheme = "tcp://";
+ else if (Protocol == DnsEnums.DnsProtocol.UDP) Scheme = "udp://";
+
+ ProtocolName = stamp.ProtocolName;
+ StampReader = stamp;
+
+ // Get Company Name (SDNS)
+ string stampHost = stamp.Host;
+ if (string.IsNullOrEmpty(stampHost)) stampHost = stamp.IP.ToString();
+ if (string.IsNullOrEmpty(stampHost)) stampHost = stamp.ProviderName;
+ if (!string.IsNullOrEmpty(CompanyNameDataFileContent))
+ CompanyName = GetCompanyName.HostToCompanyOffline(stampHost, CompanyNameDataFileContent);
+ }
+ }
+ else
+ {
+ if (dns.ToLower().StartsWith("https://") || dns.ToLower().StartsWith("http://"))
+ {
+ // DoH
+ SetIpPortHostPath(dns, 443);
+
+ Protocol = DnsEnums.DnsProtocol.DoH;
+ ProtocolName = DnsEnums.DnsProtocolName.DoH;
+ }
+ else if (dns.ToLower().StartsWith("tls://"))
+ {
+ // TLS
+ SetIpPortHostPath(dns, 853);
+
+ Protocol = DnsEnums.DnsProtocol.DoT;
+ ProtocolName = DnsEnums.DnsProtocolName.DoT;
+ }
+ else if (dns.ToLower().StartsWith("quic://"))
+ {
+ // DoQ
+ SetIpPortHostPath(dns, 853);
+
+ Protocol = DnsEnums.DnsProtocol.DoQ;
+ ProtocolName = DnsEnums.DnsProtocolName.DoQ;
+ }
+ else if (dns.ToLower().StartsWith("udp://"))
+ {
+ // Plain DNS UDP
+ SetIpPortHostPath(dns, 53);
+
+ Protocol = DnsEnums.DnsProtocol.UDP;
+ ProtocolName = DnsEnums.DnsProtocolName.UDP;
+ }
+ else if (dns.ToLower().StartsWith("tcp://"))
+ {
+ // Plain DNS TCP
+ SetIpPortHostPath(dns, 53);
+
+ Protocol = DnsEnums.DnsProtocol.TCP;
+ ProtocolName = DnsEnums.DnsProtocolName.TCP;
+ }
+ else
+ {
+ // Plain DNS UDP
+ SetIpPortHostPath(dns, 53);
+
+ Protocol = DnsEnums.DnsProtocol.UDP;
+ ProtocolName = DnsEnums.DnsProtocolName.UDP;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DnsReader: " + ex.Message);
+ }
+ }
+
+ private void SetIpPortHostPath(string dns, int defaultPort)
+ {
+ try
+ {
+ NetworkTool.GetUrlDetails(dns, defaultPort, out string scheme, out string host, out _, out _, out int port, out string path, out bool isIPv6);
+ Scheme = scheme;
+ Host = host;
+ IsHostIP = IPAddress.TryParse(Host, out _);
+ Port = port;
+ Path = path;
+
+ if (!string.IsNullOrEmpty(CompanyNameDataFileContent))
+ {
+ string? ipOrHost = Host;
+ if (string.IsNullOrEmpty(ipOrHost)) ipOrHost = IP.ToString();
+ if (string.IsNullOrEmpty(ipOrHost)) ipOrHost = host;
+ CompanyName = GetCompanyName.HostToCompanyOffline(ipOrHost, CompanyNameDataFileContent);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DnsReader SetIpPortHostPath: " + ex.Message);
+ }
+ }
+
+ private static DnsEnums.DnsProtocol ParseProtocol(DNSCryptStampReader.StampProtocol stampProtocol)
+ {
+ var protocol = stampProtocol switch
+ {
+ DNSCryptStampReader.StampProtocol.PlainDNS => DnsEnums.DnsProtocol.UDP,
+ DNSCryptStampReader.StampProtocol.DnsCrypt => DnsEnums.DnsProtocol.DnsCrypt,
+ DNSCryptStampReader.StampProtocol.DoH => DnsEnums.DnsProtocol.DoH,
+ DNSCryptStampReader.StampProtocol.DoT => DnsEnums.DnsProtocol.DoT,
+ DNSCryptStampReader.StampProtocol.DoQ => DnsEnums.DnsProtocol.DoQ,
+ DNSCryptStampReader.StampProtocol.ObliviousDohTarget => DnsEnums.DnsProtocol.ObliviousDohTarget,
+ DNSCryptStampReader.StampProtocol.AnonymizedDNSCryptRelay => DnsEnums.DnsProtocol.AnonymizedDNSCryptRelay,
+ DNSCryptStampReader.StampProtocol.ObliviousDohRelay => DnsEnums.DnsProtocol.ObliviousDohRelay,
+ DNSCryptStampReader.StampProtocol.Unknown => DnsEnums.DnsProtocol.Unknown,
+ _ => DnsEnums.DnsProtocol.Unknown,
+ };
+ return protocol;
+ }
+
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/DnsTool/GetCompanyName.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/GetCompanyName.cs
similarity index 90%
rename from MsmhToolsClass/MsmhToolsClass/DnsTool/GetCompanyName.cs
rename to MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/GetCompanyName.cs
index 8d5e5ca..95070c5 100644
--- a/MsmhToolsClass/MsmhToolsClass/DnsTool/GetCompanyName.cs
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/GetCompanyName.cs
@@ -1,6 +1,6 @@
using System.Diagnostics;
-namespace MsmhToolsClass.DnsTool;
+namespace MsmhToolsClass.MsmhAgnosticServer;
public static class GetCompanyName
{
@@ -49,8 +49,8 @@ public static string StampToCompanyOffline(string stampUrl, string fileContent)
if (!string.IsNullOrEmpty(stamp.Host))
company = HostToCompanyOffline(stamp.Host, fileContent);
- else if (!string.IsNullOrEmpty(stamp.IP))
- company = HostToCompanyOffline(stamp.IP, fileContent);
+ else if (!string.IsNullOrEmpty(stamp.IP.ToString()))
+ company = HostToCompanyOffline(stamp.IP.ToString(), fileContent);
else
company = HostToCompanyOffline(stampUrl, fileContent);
}
@@ -61,4 +61,5 @@ public static string StampToCompanyOffline(string stampUrl, string fileContent)
return company;
}
-}
\ No newline at end of file
+}
+
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/GetIP.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/GetIP.cs
new file mode 100644
index 0000000..ddb58c4
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTool/GetIP.cs
@@ -0,0 +1,245 @@
+using System.Diagnostics;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public static class GetIP
+{
+ ///
+ /// Get First IP in Answer Section
+ ///
+ /// Host
+ /// Look for IPv6
+ /// Returns IPAddress.None/IPAddress.IPv6None If Fail
+ public static IPAddress GetIpFromSystem(string host, bool getIPv6 = false, bool alsoUsePing = true)
+ {
+ List ips = GetIpsFromSystem(host, getIPv6);
+ if (ips.Count != 0) return ips[0];
+ else
+ {
+ if (alsoUsePing)
+ {
+ try
+ {
+ using Ping ping = new();
+ PingReply reply = ping.Send(host, 3000);
+ IPAddress ip = reply.Address;
+ bool isIpv6 = NetworkTool.IsIPv6(ip);
+
+ if (getIPv6 && isIpv6 && !IPAddress.IsLoopback(ip)) return ip;
+ if (!getIPv6 && !isIpv6 && !IPAddress.IsLoopback(ip)) return ip;
+ }
+ catch (Exception) { }
+ }
+ }
+
+ return getIPv6 ? IPAddress.IPv6None : IPAddress.None;
+ }
+
+ ///
+ /// Get a List of IPs
+ ///
+ /// Host
+ /// Look for IPv6
+ /// Returns Empty List If Fail
+ public static List GetIpsFromSystem(string host, bool getIPv6 = false)
+ {
+ List ips = new();
+
+ try
+ {
+ //IPAddress[] ipAddresses = System.Net.Dns.GetHostAddresses(host);
+ IPHostEntry ipHostEntry = Dns.GetHostEntry(host);
+ IPAddress[] ipAddresses = ipHostEntry.AddressList;
+
+ if (ipAddresses == null || ipAddresses.Length == 0) return ips;
+
+ if (!getIPv6)
+ {
+ for (int n = 0; n < ipAddresses.Length; n++)
+ {
+ AddressFamily addressFamily = ipAddresses[n].AddressFamily;
+ IPAddress ip = ipAddresses[n];
+ if (addressFamily != AddressFamily.InterNetworkV6)
+ if (!string.IsNullOrEmpty(ip.ToString()) && !NetworkTool.IsLocalIP(ip.ToString()) && !IPAddress.IsLoopback(ip))
+ ips.Add(ip);
+ }
+ }
+ else
+ {
+ for (int n = 0; n < ipAddresses.Length; n++)
+ {
+ AddressFamily addressFamily = ipAddresses[n].AddressFamily;
+ IPAddress ip = ipAddresses[n];
+ if (addressFamily == AddressFamily.InterNetworkV6)
+ if (!string.IsNullOrEmpty(ip.ToString()) && !IPAddress.IsLoopback(ip))
+ ips.Add(ip);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex.Message);
+ }
+
+ return ips;
+ }
+
+ ///
+ /// Get IPs in Answer Section (Using Wire Format)
+ ///
+ /// Host
+ /// A List Of DNS Servers
+ /// Timeout (Sec)
+ /// Use Proxy to Get IP
+ /// Returns An Empty List If Fail
+ public static async Task> GetIPsFromDnsAddressAsync(string host, List dnss, bool allowInsecure, int timeoutSec, bool getIPv6, IPAddress bootstrapIP, int bootstrapPort, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ List ips = new();
+ DnsEnums.RRType rrType = getIPv6 ? DnsEnums.RRType.AAAA : DnsEnums.RRType.A;
+ DnsMessage dmQ = DnsMessage.CreateQuery(DnsEnums.DnsProtocol.UDP, host, rrType, DnsEnums.CLASS.IN);
+ bool isWriteSuccess = DnsMessage.TryWrite(dmQ, out byte[] dmQBuffer);
+ if (isWriteSuccess)
+ {
+ byte[] dmABuffer = await DnsClient.QueryAsync(dmQBuffer, DnsEnums.DnsProtocol.UDP, dnss, allowInsecure, bootstrapIP, bootstrapPort, timeoutSec * 1000, proxyScheme, proxyUser, proxyPass);
+ DnsMessage dmA = DnsMessage.Read(dmABuffer, DnsEnums.DnsProtocol.UDP);
+
+ if (dmA.IsSuccess && dmA.Header.AnswersCount > 0)
+ {
+ foreach (IResourceRecord irr in dmA.Answers.AnswerRecords)
+ {
+ if (getIPv6)
+ {
+ if (irr is not AaaaRecord aaaaRecord) continue;
+ bool isLoopbackIP = IPAddress.IsLoopback(aaaaRecord.IP);
+ if (!isLoopbackIP) ips.Add(aaaaRecord.IP);
+ }
+ else
+ {
+ if (irr is not ARecord aRecord) continue;
+ string ipStr = aRecord.IP.ToString();
+ bool isLocalIP = NetworkTool.IsLocalIP(ipStr);
+ bool isLoopbackIP = IPAddress.IsLoopback(aRecord.IP);
+ if (!isLocalIP && !isLoopbackIP) ips.Add(aRecord.IP);
+ }
+ }
+ }
+ }
+ return ips;
+ }
+
+ ///
+ /// Get IPs in Answer Section (Using Wire Format)
+ ///
+ /// Host
+ /// A List Of DNS Servers
+ /// Returns An Empty List If Fail
+ public static async Task> GetIPsFromDnsAddressAsync(string host, List dnss, bool getIPv6, AgnosticSettings s)
+ {
+ return await GetIPsFromDnsAddressAsync(host, dnss, s.AllowInsecure, s.DnsTimeoutSec, getIPv6, s.BootstrapIpAddress, s.BootstrapPort, s.UpstreamProxyScheme, s.UpstreamProxyUser, s.UpstreamProxyPass);
+ }
+
+ ///
+ /// Get IPs in Answer Section (Using Wire Format)
+ ///
+ /// Host
+ /// A DNS Server
+ /// Timeout (Sec)
+ /// Use Proxy to Get IP
+ /// Returns An Empty List If Fail
+ public static async Task> GetIPsFromDnsAddressAsync(string host, string dns, bool allowInsecure, int timeoutSec, bool getIPv6, IPAddress bootstrapIP, int bootstrapPort, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ return await GetIPsFromDnsAddressAsync(host, new List() { dns }, allowInsecure, timeoutSec, getIPv6, bootstrapIP, bootstrapPort, proxyScheme, proxyUser, proxyPass);
+ }
+
+ ///
+ /// Get IPs in Answer Section (Using Wire Format)
+ ///
+ /// Host
+ /// A DNS Server
+ /// Returns An Empty List If Fail
+ public static async Task> GetIPsFromDnsAddressAsync(string host, string dns, bool getIPv6, AgnosticSettings s)
+ {
+ return await GetIPsFromDnsAddressAsync(host, new List() { dns }, s.AllowInsecure, s.DnsTimeoutSec, getIPv6, s.BootstrapIpAddress, s.BootstrapPort, s.UpstreamProxyScheme, s.UpstreamProxyUser, s.UpstreamProxyPass);
+ }
+
+ ///
+ /// Get First IP in Answer Section (Using Wire Format)
+ ///
+ /// Host
+ /// A List Of DNS Servers
+ /// Timeout (Sec)
+ /// Use Proxy to Get IP
+ /// Returns IPAddress.None/IPAddress.IPv6None If Fail
+ public static async Task GetIpFromDnsAddressAsync(string host, List dnss, bool allowInsecure, int timeoutSec, bool getIPv6, IPAddress bootstrapIP, int bootstrapPort, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ DnsEnums.RRType rrType = getIPv6 ? DnsEnums.RRType.AAAA : DnsEnums.RRType.A;
+ DnsMessage dmQ = DnsMessage.CreateQuery(DnsEnums.DnsProtocol.UDP, host, rrType, DnsEnums.CLASS.IN);
+ bool isWriteSuccess = DnsMessage.TryWrite(dmQ, out byte[] dmQBuffer);
+ if (isWriteSuccess)
+ {
+ byte[] dmABuffer = await DnsClient.QueryAsync(dmQBuffer, DnsEnums.DnsProtocol.UDP, dnss, allowInsecure, bootstrapIP, bootstrapPort, timeoutSec * 1000, proxyScheme, proxyUser, proxyPass);
+ DnsMessage dmA = DnsMessage.Read(dmABuffer, DnsEnums.DnsProtocol.UDP);
+
+ if (dmA.IsSuccess && dmA.Header.AnswersCount > 0)
+ {
+ foreach (IResourceRecord irr in dmA.Answers.AnswerRecords)
+ {
+ if (getIPv6)
+ {
+ if (irr is not AaaaRecord aaaaRecord) continue;
+ bool isLoopbackIP = IPAddress.IsLoopback(aaaaRecord.IP);
+ if (!isLoopbackIP) return aaaaRecord.IP;
+ }
+ else
+ {
+ if (irr is not ARecord aRecord) continue;
+ string ipStr = aRecord.IP.ToString();
+ bool isLocalIP = NetworkTool.IsLocalIP(ipStr);
+ bool isLoopbackIP = IPAddress.IsLoopback(aRecord.IP);
+ if (!isLocalIP && !isLoopbackIP) return aRecord.IP;
+ }
+ }
+ }
+ }
+ return getIPv6 ? IPAddress.IPv6None : IPAddress.None;
+ }
+
+ ///
+ /// Get First IP in Answer Section (Using Wire Format)
+ ///
+ /// Host
+ /// A List Of DNS Servers
+ /// Returns IPAddress.None/IPAddress.IPv6None If Fail
+ public static async Task GetIpFromDnsAddressAsync(string host, List dnss, bool getIPv6, AgnosticSettings s)
+ {
+ return await GetIpFromDnsAddressAsync(host, dnss, s.AllowInsecure, s.DnsTimeoutSec, getIPv6, s.BootstrapIpAddress, s.BootstrapPort, s.UpstreamProxyScheme, s.UpstreamProxyUser, s.UpstreamProxyPass);
+ }
+
+ ///
+ /// Get First IP in Answer Section (Using Wire Format)
+ ///
+ /// Host
+ /// A DNS Server
+ /// Timeout (Sec)
+ /// Use Proxy to Get IP
+ /// Returns IPAddress.None/IPAddress.IPv6None If Fail
+ public static async Task GetIpFromDnsAddressAsync(string host, string dns, bool allowInsecure, int timeoutSec, bool getIPv6, IPAddress bootstrapIP, int bootstrapPort, string? proxyScheme = null, string? proxyUser = null, string? proxyPass = null)
+ {
+ return await GetIpFromDnsAddressAsync(host, new List() { dns }, allowInsecure, timeoutSec, getIPv6, bootstrapIP, bootstrapPort, proxyScheme, proxyUser, proxyPass);
+ }
+
+ ///
+ /// Get First IP in Answer Section (Using Wire Format)
+ ///
+ /// Host
+ /// A DNS Server
+ /// Returns IPAddress.None/IPAddress.IPv6None If Fail
+ public static async Task GetIpFromDnsAddressAsync(string host, string dns, bool getIPv6, AgnosticSettings s)
+ {
+ return await GetIpFromDnsAddressAsync(host, new List() { dns }, s.AllowInsecure, s.DnsTimeoutSec, getIPv6, s.BootstrapIpAddress, s.BootstrapPort, s.UpstreamProxyScheme, s.UpstreamProxyUser, s.UpstreamProxyPass);
+ }
+
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTunnel.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTunnel.cs
new file mode 100644
index 0000000..778bfb5
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/DnsServer/DnsTunnel.cs
@@ -0,0 +1,184 @@
+using System.Diagnostics;
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public class DnsTunnel
+{
+ public static async Task Process(AgnosticResult aResult, AgnosticProgram.DnsRules dnsRulesProgram, DnsCache dnsCaches, AgnosticSettings settings, EventHandler? onRequestReceived)
+ {
+ DnsEnums.DnsProtocol dnsProtocol = aResult.Protocol switch
+ {
+ RequestProtocol.UDP => DnsEnums.DnsProtocol.UDP,
+ RequestProtocol.TCP => DnsEnums.DnsProtocol.TCP,
+ RequestProtocol.DoH => DnsEnums.DnsProtocol.DoH,
+ _ => DnsEnums.DnsProtocol.Unknown
+ };
+
+ // Event
+ string msgReqEvent = $"[{aResult.Local_EndPoint.Address}] [{dnsProtocol}] ";
+
+ // Create Request
+ DnsMessage dmQ = DnsMessage.Read(aResult.FirstBuffer, dnsProtocol);
+ if (dmQ.IsSuccess && dmQ.Header.QuestionsCount > 0 && dmQ.Questions.QuestionRecords.Count > 0)
+ {
+ DnsRequest dnsRequest = new(aResult.Socket, aResult.Ssl_Stream, aResult.Ssl_Kind, aResult.Local_EndPoint, aResult.Remote_EndPoint, aResult.FirstBuffer, dnsProtocol);
+ string addressQ = dmQ.Questions.QuestionRecords[0].QNAME;
+ DnsEnums.RRType typeQ = dmQ.Questions.QuestionRecords[0].QTYPE;
+
+ msgReqEvent += $"Q: {addressQ}, A: ";
+
+ bool isCached = dnsCaches.TryGet(dmQ, out DnsMessage dmR);
+ bool usedCache = false;
+ if (isCached)
+ {
+ if (dmR.IsSuccess)
+ {
+ bool isTryWriteSuccess = DnsMessage.TryWrite(dmR, out byte[] responseCached);
+ //Debug.WriteLine("========== IsTryWriteSuccess: " + isTryWriteSuccess);
+ if (isTryWriteSuccess)
+ {
+ DnsMessage validate = DnsMessage.Read(responseCached, dnsRequest.Protocol);
+ if (validate.IsSuccess)
+ {
+ await dnsRequest.SendToAsync(responseCached).ConfigureAwait(false);
+ usedCache = true;
+ }
+ }
+ }
+
+ if (!usedCache)
+ {
+ // TTL Expired - Remove From Cache
+ dnsCaches.TryRemove(dmQ);
+ }
+ }
+ //Debug.WriteLine("========== Used Cache: " + usedCache);
+
+ if (!usedCache)
+ {
+ // Apply DnsRules Program
+ AgnosticProgram.DnsRules.DnsRulesResult drr = new();
+ if (dnsRulesProgram.RulesMode != AgnosticProgram.DnsRules.Mode.Disable)
+ {
+ drr = await dnsRulesProgram.GetAsync(aResult.Local_EndPoint.Address.ToString(), addressQ, settings).ConfigureAwait(false);
+ }
+
+ bool usedFakeOrCustom = false;
+ if (drr.IsMatch)
+ {
+ // Black List
+ if (drr.IsBlackList)
+ {
+ await dnsRequest.SendFailedResponseAsync().ConfigureAwait(false);
+ usedFakeOrCustom = true;
+
+ msgReqEvent += "Request Denied - Black List";
+ onRequestReceived?.Invoke(msgReqEvent, EventArgs.Empty);
+ return;
+ }
+
+ // If Custom Dns Couldn't Get An IP
+ if (string.IsNullOrEmpty(drr.Dns))
+ {
+ await dnsRequest.SendFailedResponseAsync().ConfigureAwait(false);
+ usedFakeOrCustom = true;
+
+ msgReqEvent += "Request Denied - Your Dns Rule Couldn't Get An IP!";
+ onRequestReceived?.Invoke(msgReqEvent, EventArgs.Empty);
+ return;
+ }
+
+ // Fake DNS / Dns Domain / Custom Dns Or Smart DNS
+ bool isDnsIp = NetworkTool.IsIp(drr.Dns, out IPAddress? dnsIp);
+ if (isDnsIp && dnsIp != null)
+ {
+ bool isDnsIpv6 = NetworkTool.IsIPv6(dnsIp);
+ if (isDnsIpv6)
+ {
+ // IPv6
+ if (typeQ == DnsEnums.RRType.AAAA)
+ {
+ dmR = DnsMessage.CreateResponse(dmQ, 1, 0, 0);
+ dmR.Answers.AnswerRecords.Add(new AaaaRecord(addressQ, 60, dnsIp));
+
+ bool isTryWriteSuccess = DnsMessage.TryWrite(dmR, out byte[] aBuffer);
+ if (isTryWriteSuccess)
+ {
+ await dnsRequest.SendToAsync(aBuffer).ConfigureAwait(false);
+ usedFakeOrCustom = true;
+ bool cacheSuccess = dnsCaches.TryAdd(dmQ, dmR);
+ Debug.WriteLine("ADDED TO CACHE 1: " + cacheSuccess);
+ }
+ }
+ }
+ else
+ {
+ // IPv4
+ if (typeQ == DnsEnums.RRType.A)
+ {
+ dmR = DnsMessage.CreateResponse(dmQ, 1, 0, 0);
+ dmR.Answers.AnswerRecords.Add(new ARecord(addressQ, 60, dnsIp));
+
+ bool isTryWriteSuccess = DnsMessage.TryWrite(dmR, out byte[] aBuffer);
+ if (isTryWriteSuccess)
+ {
+ await dnsRequest.SendToAsync(aBuffer).ConfigureAwait(false);
+ usedFakeOrCustom = true;
+ bool cacheSuccess = dnsCaches.TryAdd(dmQ, dmR);
+ Debug.WriteLine("ADDED TO CACHE 2: " + cacheSuccess);
+ }
+ }
+ }
+ }
+ }
+
+ if (!usedFakeOrCustom && settings.DNSs.Count > 0)
+ {
+ byte[] response = await DnsClient.QueryAsync(dnsRequest.Buffer, dnsRequest.Protocol, settings).ConfigureAwait(false);
+ dmR = DnsMessage.Read(response, dnsRequest.Protocol);
+ if (dmR.IsSuccess)
+ {
+ await dnsRequest.SendToAsync(response).ConfigureAwait(false);
+ bool cacheSuccess = dnsCaches.TryAdd(dmQ, dmR);
+ Debug.WriteLine("ADDED TO CACHE 3: " + cacheSuccess);
+ }
+ else
+ {
+ await dnsRequest.SendFailedResponseAsync().ConfigureAwait(false);
+ }
+ }
+ }
+
+ // Event
+ List answers = new();
+ if (dmR.IsSuccess)
+ {
+ foreach (IResourceRecord answer in dmR.Answers.AnswerRecords)
+ {
+ if (answer is ARecord aRecord) answers.Add(aRecord.IP.ToString());
+ else if (answer is AaaaRecord aaaaRecord) answers.Add(aaaaRecord.IP.ToString());
+ //else if (answer is CNameRecord cNameRecord) answers.Add(cNameRecord.CName);
+ else
+ {
+ string a = answer.TYPE.ToString();
+ if (!answers.IsContain(a))
+ {
+ answers.Add(answer.CLASS.ToString());
+ answers.Add(answer.TimeToLive.ToString());
+ answers.Add(a);
+ }
+ }
+ }
+ }
+
+ if (answers.Count != 0)
+ {
+ msgReqEvent += answers.ToString(", ");
+ onRequestReceived?.Invoke(msgReqEvent, EventArgs.Empty);
+ }
+
+ //Debug.WriteLine(dmR.ToString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/MsmhAgnosticServer.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/MsmhAgnosticServer.cs
new file mode 100644
index 0000000..4b8fc37
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/MsmhAgnosticServer.cs
@@ -0,0 +1,674 @@
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+// MSMH Agnostic Server - CopyRight GPLv3 MSasanMH (msasanmh@gmail.com) 2023 - 2024
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public partial class MsmhAgnosticServer
+{
+ //======================================= Fragment Support: Static
+ public static AgnosticProgram.Fragment StaticFragmentProgram { get; set; } = new();
+ public void EnableStaticFragment(AgnosticProgram.Fragment fragmentProgram)
+ {
+ StaticFragmentProgram = fragmentProgram;
+ }
+
+ //--- Constant
+ public AgnosticProgram.Fragment FragmentProgram = new();
+ public void EnableFragment(AgnosticProgram.Fragment fragmentProgram)
+ {
+ FragmentProgram = fragmentProgram;
+ }
+
+ //======================================= DnsRules Support
+ public AgnosticProgram.DnsRules DnsRulesProgram = new();
+ public void EnableDnsRules(AgnosticProgram.DnsRules dnsRules)
+ {
+ DnsRulesProgram = dnsRules;
+ }
+
+ //======================================= ProxyRules Support
+ public AgnosticProgram.ProxyRules ProxyRulesProgram = new();
+ public void EnableProxyRules(AgnosticProgram.ProxyRules proxyRules)
+ {
+ ProxyRulesProgram = proxyRules;
+ }
+
+ // ====================================== Const
+ internal static readonly int MaxDataSize = 65536;
+ internal static readonly string DnsMessageContentType = "application/dns-message";
+ internal static readonly int DNS_HEADER_LENGTH = 12;
+ internal static readonly SslProtocols SSL_Protocols = SslProtocols.None | SslProtocols.Tls12 | SslProtocols.Tls13;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "")]
+ internal static bool Callback(object sender, X509Certificate? cert, X509Chain? chain, SslPolicyErrors sslPolicyErrors) => true;
+
+ //======================================= Start Server
+ internal AgnosticSettings Settings_ = new();
+ public AgnosticSettingsSSL SettingsSSL_ { get; internal set; } = new(false);
+
+ private Socket? UdpSocket_;
+ private readonly DnsCache DnsCaches = new();
+ private readonly ProxyRequestsCache ProxyRequestsCaches = new();
+ private TcpListener? TcpListener_;
+ internal TunnelManager TunnelManager_ = new();
+
+ private CancellationTokenSource? CancelTokenSource_;
+ private CancellationToken CancelToken_;
+ private CancellationTokenSource CTS_PR = new();
+
+ private System.Timers.Timer KillOnOverloadTimer { get; set; } = new(5000);
+ private float CpuUsage { get; set; } = -1;
+
+ private bool Cancel { get; set; } = false;
+
+ private Thread? MainThread;
+
+ public event EventHandler? OnRequestReceived;
+ public event EventHandler? OnDebugInfoReceived;
+
+ public Stats Stats { get; private set; } = new();
+ public bool IsRunning { get; private set; } = false;
+
+ private readonly ConcurrentQueue MaxRequestsQueue = new();
+ private readonly ConcurrentDictionary DelinquentRequests = new();
+ private readonly ConcurrentDictionary TestRequests = new();
+ internal static readonly int MaxRequestsDelay = 50;
+ internal static readonly int MaxRequestsDivide = 20; // 20 * 50 = 1000 ms
+
+ public MsmhAgnosticServer() { }
+
+ public async Task EnableSSL(AgnosticSettingsSSL settingsSSL)
+ {
+ SettingsSSL_ = settingsSSL;
+ await SettingsSSL_.Build().ConfigureAwait(false);
+ }
+
+ public void Start(AgnosticSettings settings)
+ {
+ if (IsRunning) return;
+ IsRunning = true;
+
+ Settings_ = settings;
+ Settings_.Initialize();
+
+ // Set Default DNSs
+ if (Settings_.DNSs.Count == 0) Settings_.DNSs = AgnosticSettings.DefaultDNSs();
+
+ Stats = new Stats();
+
+ Welcome();
+
+ TunnelManager_ = new();
+
+ CancelTokenSource_ = new();
+ CancelToken_ = CancelTokenSource_.Token;
+
+ Cancel = false;
+
+ MaxRequestsTimer();
+
+ KillOnOverloadTimer.Elapsed += KillOnOverloadTimer_Elapsed;
+ KillOnOverloadTimer.Start();
+
+ ThreadStart threadStart = new(AcceptConnections);
+ MainThread = new(threadStart);
+ if (OperatingSystem.IsWindows()) MainThread.SetApartmentState(ApartmentState.STA);
+ MainThread.Start();
+ }
+
+ private void Welcome()
+ {
+ // Event
+ string msgEvent = $"Server Starting On Port:{Settings_.ListenerPort}";
+ OnRequestReceived?.Invoke(msgEvent, EventArgs.Empty);
+ OnDebugInfoReceived?.Invoke(msgEvent, EventArgs.Empty);
+ }
+
+ private async void MaxRequestsTimer()
+ {
+ await Task.Run(async () =>
+ {
+ while (true)
+ {
+ if (Cancel) break;
+ await Task.Delay(MaxRequestsDelay);
+
+ try
+ {
+ bool peek = MaxRequestsQueue.TryPeek(out DateTime dt);
+ if (peek)
+ {
+ double diff = (DateTime.UtcNow - dt).TotalMilliseconds;
+ if (diff > 1000 / MaxRequestsDivide) // Dequeue If Older Than 50 ms (1000 / 20)
+ MaxRequestsQueue.TryDequeue(out _);
+ }
+ }
+ catch (Exception) { }
+ }
+ });
+ }
+
+ private async void KillOnOverloadTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
+ {
+ if (OperatingSystem.IsWindows() && typeof(PerformanceCounter) != null)
+ CpuUsage = await ProcessManager.GetCpuUsage(Environment.ProcessId, 1000);
+
+ if (CpuUsage >= Settings_.KillOnCpuUsage && Settings_.KillOnCpuUsage > 0)
+ {
+ KillAll();
+ }
+
+ if (CpuUsage >= 95f)
+ {
+ try { Environment.Exit(0); } catch (Exception) { }
+ ProcessManager.KillProcessByPID(Environment.ProcessId);
+ }
+ }
+
+ ///
+ /// Kill all active requests
+ ///
+ public void KillAll()
+ {
+ try
+ {
+ CTS_PR.Cancel();
+ ProxyRequestsCaches.Clear();
+ if (TunnelManager_ != null)
+ {
+ var dic = TunnelManager_.GetTunnels();
+ Debug.WriteLine(dic.Count);
+ foreach (var item in dic)
+ {
+ Debug.WriteLine(item.Key);
+ TunnelManager_.Remove(item.Value.Value);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("MsmhAgnosticServer KillAll: " + ex.Message);
+ }
+ }
+
+ public void Stop()
+ {
+ if (IsRunning && CancelTokenSource_ != null)
+ {
+ try
+ {
+ IsRunning = false;
+ CancelTokenSource_.Cancel(true);
+ Cancel = true;
+ UdpSocket_?.Shutdown(SocketShutdown.Both);
+ UdpSocket_?.Dispose();
+ TcpListener_?.Stop();
+ TcpListener_ = null;
+
+ KillAll();
+
+ MaxRequestsQueue.Clear();
+ DelinquentRequests.Clear();
+ TestRequests.Clear();
+
+ KillOnOverloadTimer.Stop();
+ Goodbye();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Stop MsmhAgnosticServer: " + ex.GetInnerExceptions());
+ }
+ }
+ }
+
+ private void Goodbye()
+ {
+ // Event
+ string msgEvent = "Proxy Server Stopped.";
+ OnRequestReceived?.Invoke(msgEvent, EventArgs.Empty);
+ OnDebugInfoReceived?.Invoke(msgEvent, EventArgs.Empty);
+ }
+
+ public int ListeningPort => Settings_.ListenerPort;
+ public bool IsFragmentActive => FragmentProgram.FragmentMode != AgnosticProgram.Fragment.Mode.Disable || StaticFragmentProgram.FragmentMode != AgnosticProgram.Fragment.Mode.Disable;
+ public bool IsFakeSniActive => SettingsSSL_.EnableSSL && SettingsSSL_.ChangeSni;
+ public int ActiveProxyTunnels => TunnelManager_.Count;
+ public int MaxRequests => Settings_.MaxRequests;
+ public int CachedDnsRequests => DnsCaches.CachedRequests;
+
+ public void FlushDnsCache()
+ {
+ DnsCaches.Flush();
+ }
+
+ private async void AcceptConnections()
+ {
+ if (Cancel) return;
+
+ try
+ {
+ if (Settings_.ListenerIP == null)
+ {
+ string msg = "Neither IPv4 Nor IPv6 Is Supported By Your OS!";
+ OnRequestReceived?.Invoke(msg, EventArgs.Empty);
+ Stop();
+ return;
+ }
+
+ // EndPoint
+ IPEndPoint ipEndPoint = Settings_.ServerEndPoint;
+
+ // UDP
+ UdpSocket_ = new(ipEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
+ if (ipEndPoint.Address.Equals(IPAddress.IPv6Any))
+ {
+ UdpSocket_.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
+ UdpSocket_.DualMode = true;
+ }
+ UdpSocket_.Bind(ipEndPoint);
+
+ // TCP
+ TcpListener_ = new(ipEndPoint);
+ SocketOptionName socketOptionName = SocketOptionName.ReuseAddress | SocketOptionName.ReuseUnicastPort;
+ TcpListener_.Server.SetSocketOption(SocketOptionLevel.Socket, socketOptionName, true);
+ if (ipEndPoint.Address.Equals(IPAddress.IPv6Any))
+ {
+ TcpListener_.Server.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
+ TcpListener_.Server.DualMode = true;
+ }
+ TcpListener_.Start();
+
+ await Task.Delay(200);
+
+ if (UdpSocket_ != null && TcpListener_ != null)
+ {
+ IsRunning = TcpListener_.Server.IsBound && UdpSocket_.IsBound;
+ if (!IsRunning)
+ {
+ UdpSocket_.Dispose();
+ TcpListener_.Stop();
+ Stop();
+ return;
+ }
+
+ // Run UDP & TCP Listener In Parallel
+ udp(UdpSocket_);
+ tcp(TcpListener_);
+
+ // UDP
+ async void udp(Socket udpSocket)
+ {
+ while (!Cancel)
+ {
+ try
+ {
+ byte[] buffer = new byte[MaxDataSize];
+ SocketReceiveMessageFromResult udpMessage = await udpSocket.ReceiveMessageFromAsync(buffer, SocketFlags.None, ipEndPoint).ConfigureAwait(false);
+ if (CpuUsage < Settings_.KillOnCpuUsage || Settings_.KillOnCpuUsage <= 0)
+ {
+ if (udpMessage.ReceivedBytes > 0)
+ {
+ buffer = buffer[..udpMessage.ReceivedBytes];
+ //Debug.WriteLine(" UDP ===> " + BitConverter.ToString(buffer));
+ AgnosticRequest agnosticRequest = new()
+ {
+ Udp_Socket = udpSocket,
+ Local_EndPoint = udpSocket.LocalEndPoint as IPEndPoint,
+ Remote_EndPoint = udpMessage.RemoteEndPoint as IPEndPoint,
+ Peeked_Buffer = buffer,
+ Protocol = AgnosticRequest.ListenerProtocol.UDP
+ };
+
+ ProcessConnectionSync(agnosticRequest);
+ }
+ }
+ if (CancelToken_.IsCancellationRequested || Cancel) break;
+ }
+ catch (Exception ex)
+ {
+ // Event Error
+ if (!CancelToken_.IsCancellationRequested || !Cancel)
+ {
+ string msgEventErr = $"ERROR: Accept Connections UDP: {ex.GetInnerExceptions()}";
+ //OnRequestReceived?.Invoke(msgEventErr, EventArgs.Empty);
+ Debug.WriteLine(msgEventErr);
+ }
+ }
+ }
+
+ Stop();
+ }
+
+ // TCP
+ async void tcp(TcpListener tcpListener)
+ {
+ while (!Cancel)
+ {
+ try
+ {
+ TcpClient tcpClient = await tcpListener.AcceptTcpClientAsync().ConfigureAwait(false);
+ if (CpuUsage < Settings_.KillOnCpuUsage || Settings_.KillOnCpuUsage <= 0)
+ {
+ byte[] buffer = new byte[MaxDataSize];
+ int received = await tcpClient.Client.ReceiveAsync(buffer, SocketFlags.Peek).ConfigureAwait(false);
+ if (received > 0)
+ {
+ tcpClient.NoDelay = true;
+ buffer = buffer[..received];
+ //Debug.WriteLine(" TCP ===> " + BitConverter.ToString(buffer));
+ AgnosticRequest agnosticRequest = new()
+ {
+ Tcp_Client = tcpClient,
+ Local_EndPoint = tcpClient.Client.LocalEndPoint as IPEndPoint,
+ Remote_EndPoint = tcpClient.Client.RemoteEndPoint as IPEndPoint,
+ Peeked_Buffer = buffer,
+ Protocol = AgnosticRequest.ListenerProtocol.TCP
+ };
+
+ ProcessConnectionSync(agnosticRequest);
+ }
+ }
+ if (CancelToken_.IsCancellationRequested || Cancel) break;
+ }
+ catch (Exception ex)
+ {
+ // Event Error
+ if (!CancelToken_.IsCancellationRequested || !Cancel)
+ {
+ string msgEventErr = $"ERROR: Accept Connections TCP: {ex.GetInnerExceptions()}";
+ //OnRequestReceived?.Invoke(msgEventErr, EventArgs.Empty);
+ Debug.WriteLine(msgEventErr);
+ }
+ }
+ }
+
+ Stop();
+ }
+ }
+ else
+ {
+ IsRunning = false;
+ UdpSocket_?.Dispose();
+ TcpListener_?.Stop();
+ }
+ }
+ catch (Exception ex)
+ {
+ // Event Error
+ if (!CancelToken_.IsCancellationRequested || !Cancel)
+ {
+ string msgEventErr = $"ERROR: Accept Connections: {ex.GetInnerExceptions()}";
+ Debug.WriteLine(msgEventErr);
+ OnRequestReceived?.Invoke(msgEventErr, EventArgs.Empty);
+ TcpListener_?.Stop();
+ }
+ }
+ }
+
+ private void ProcessConnectionSync(AgnosticRequest agnosticRequest)
+ {
+ Task.Run(() => ClientConnected(agnosticRequest));
+ }
+
+ private async void ClientConnected(AgnosticRequest aRequest)
+ {
+ if (aRequest.Local_EndPoint == null || aRequest.Remote_EndPoint == null)
+ {
+ aRequest.Disconnect();
+ return;
+ }
+
+ // Count Max Requests
+ try
+ {
+ MaxRequestsQueue.Enqueue(DateTime.UtcNow);
+ if (Settings_.MaxRequests >= MaxRequestsDivide)
+ {
+ if (MaxRequestsQueue.Count >= Settings_.MaxRequests / MaxRequestsDivide) // Check for 50 ms (1000 / 20)
+ {
+ // Event
+ string blockEvent = $"Recevied {MaxRequestsQueue.Count * MaxRequestsDivide} Requests Per Second - Request Denied Due To Max Requests of {Settings_.MaxRequests}.";
+ Debug.WriteLine("====================> " + blockEvent);
+ OnRequestReceived?.Invoke(blockEvent, EventArgs.Empty);
+ aRequest.Disconnect();
+ return;
+ }
+ }
+ }
+ catch (Exception) { }
+
+ // Generate unique int
+ int connectionId;
+ try
+ {
+ connectionId = Guid.NewGuid().GetHashCode() + BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0);
+ }
+ catch (Exception)
+ {
+ connectionId = Guid.NewGuid().GetHashCode();
+ }
+
+ AgnosticResult aResult = await aRequest.GetResultAsync(SettingsSSL_).ConfigureAwait(false);
+
+ if (aResult.Socket == null || aResult.FirstBuffer.Length == 0 || aResult.Protocol == RequestProtocol.Unknown)
+ {
+ aRequest.Disconnect();
+ return;
+ }
+
+ //Debug.WriteLine("Request ListenerProtocol ===> " + aResult.Protocol);
+ //Debug.WriteLine("Request Buffer ===> " + BitConverter.ToString(aResult.FirstBuffer));
+
+ if (aResult.Protocol == RequestProtocol.UDP ||
+ aResult.Protocol == RequestProtocol.TCP ||
+ aResult.Protocol == RequestProtocol.DoH)
+ {
+ // ===== Process DNS
+ await DnsTunnel.Process(aResult, DnsRulesProgram, DnsCaches, Settings_, OnRequestReceived);
+ aRequest.Disconnect();
+ }
+ else
+ {
+ // ===== Process Proxy
+ if (Settings_.Working_Mode == AgnosticSettings.WorkingMode.DnsAndProxy)
+ {
+ // Create Client
+ ProxyClient proxyClient = new(aResult.Socket);
+
+ // Create Request
+ CTS_PR = new();
+ ProxyRequest? req = null;
+ if (aResult.Protocol == RequestProtocol.HTTP_S)
+ req = await ProxyRequest.RequestHTTP_S(aResult.FirstBuffer, CTS_PR.Token).ConfigureAwait(false);
+ else if (aResult.Protocol == RequestProtocol.SOCKS4_4A)
+ req = await ProxyRequest.RequestSocks4_4A(proxyClient, aResult.FirstBuffer, CTS_PR.Token).ConfigureAwait(false);
+ else if (aResult.Protocol == RequestProtocol.SOCKS5)
+ req = await ProxyRequest.RequestSocks5(proxyClient, aResult.FirstBuffer, CTS_PR.Token).ConfigureAwait(false);
+ else if (aResult.Protocol == RequestProtocol.SniProxy)
+ req = await ProxyRequest.RequestSniProxy(aResult.SNI, Settings_.ListenerPort, CTS_PR.Token).ConfigureAwait(false);
+
+ // Apply Programs
+ req = await ApplyPrograms(aResult.Local_EndPoint.Address, req).ConfigureAwait(false);
+
+ if (req == null)
+ {
+ aRequest.Disconnect();
+ return;
+ }
+
+ // Create Tunnel
+ ProxyTunnel proxyTunnel = new(connectionId, proxyClient, req, Settings_, SettingsSSL_);
+ proxyTunnel.Open(ProxyRulesProgram);
+
+ proxyTunnel.OnTunnelDisconnected += ProxyTunnel_OnTunnelDisconnected;
+ proxyTunnel.OnDataReceived += ProxyTunnel_OnDataReceived;
+
+ TunnelManager_.Add(proxyTunnel);
+ }
+ }
+ }
+
+ private void ProxyTunnel_OnTunnelDisconnected(object? sender, EventArgs e)
+ {
+ try
+ {
+ if (sender is not ProxyTunnel pt) return;
+
+ if (pt.KillOnTimeout.IsRunning)
+ {
+ pt.KillOnTimeout.Reset();
+ pt.KillOnTimeout.Stop();
+ }
+
+ pt.OnTunnelDisconnected -= ProxyTunnel_OnTunnelDisconnected;
+ pt.OnDataReceived -= ProxyTunnel_OnDataReceived;
+ pt.ClientSSL?.Disconnect();
+
+ TunnelManager_.Remove(pt);
+ Debug.WriteLine($"{pt.Req.Address} Disconnected");
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("ProxyTunnel_OnTunnelDisconnected: " + ex.Message);
+ }
+ }
+
+ private void ProxyTunnel_OnDataReceived(object? sender, EventArgs e)
+ {
+ try
+ {
+ if (sender is not ProxyTunnel t) return;
+
+ t.Client.OnDataReceived += async (s, e) =>
+ {
+ // Client Received == Remote Sent
+ if (!t.KillOnTimeout.IsRunning) t.KillOnTimeout.Start();
+ t.KillOnTimeout.Restart();
+ if (e.Buffer.Length > 0)
+ {
+ if (t.Req.ApplyFragment)
+ Send(e.Buffer, t);
+ else
+ await t.RemoteClient.SendAsync(e.Buffer).ConfigureAwait(false);
+
+ lock (Stats)
+ {
+ Stats.AddBytes(e.Buffer.Length, ByteType.Sent);
+ }
+ }
+
+ t.KillOnTimeout.Restart();
+ await t.Client.StartReceiveAsync().ConfigureAwait(false);
+ t.KillOnTimeout.Restart();
+ };
+
+ t.Client.OnDataSent += (s, e) =>
+ {
+ // Client Sent == Remote Received
+ if (!t.KillOnTimeout.IsRunning) t.KillOnTimeout.Start();
+ t.KillOnTimeout.Restart();
+ lock (Stats)
+ {
+ Stats.AddBytes(e.Buffer.Length, ByteType.Received);
+ }
+ };
+
+ t.RemoteClient.OnDataReceived += async (s, e) =>
+ {
+ t.KillOnTimeout.Restart();
+
+ if (e.Buffer.Length > 0)
+ await t.Client.SendAsync(e.Buffer).ConfigureAwait(false);
+
+ t.KillOnTimeout.Restart();
+ await t.RemoteClient.StartReceiveAsync().ConfigureAwait(false);
+ t.KillOnTimeout.Restart();
+ };
+
+ // Handle SSL
+ if (t.ClientSSL != null)
+ {
+ t.ClientSSL.OnClientDataReceived += (s, e) =>
+ {
+ t.KillOnTimeout.Restart();
+
+ if (e.Buffer.Length > 0)
+ {
+ // Can't be implement here. Ex: "The WriteAsync method cannot be called when another write operation is pending"
+ //await t.ClientSSL.RemoteStream.WriteAsync(e.Buffer).ConfigureAwait(false);
+
+ lock (Stats)
+ {
+ Stats.AddBytes(e.Buffer.Length, ByteType.Sent);
+ }
+ }
+ t.ClientSSL.OnClientDataReceived -= null;
+ };
+
+ t.ClientSSL.OnRemoteDataReceived += (s, e) =>
+ {
+ t.KillOnTimeout.Restart();
+
+ if (e.Buffer.Length > 0)
+ {
+ // Can't be implement here. Ex: "The WriteAsync method cannot be called when another write operation is pending"
+ //await t.ClientSSL.ClientStream.WriteAsync(e.Buffer).ConfigureAwait(false);
+
+ lock (Stats)
+ {
+ Stats.AddBytes(e.Buffer.Length, ByteType.Received);
+ }
+ }
+ t.ClientSSL.OnRemoteDataReceived -= null;
+ };
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("ProxyTunnel_OnDataReceived: " + ex.Message);
+ }
+ }
+
+ private void Send(byte[] data, ProxyTunnel t)
+ {
+ try
+ {
+ if (t.RemoteClient.Socket_ != null && t.RemoteClient.Socket_.Connected)
+ {
+ if (FragmentProgram.FragmentMode == AgnosticProgram.Fragment.Mode.Disable)
+ {
+ // Static
+ AgnosticProgram.Fragment bp = StaticFragmentProgram;
+ bp.DestHostname = t.Req.Address;
+ bp.DestPort = t.Req.Port;
+ if (bp.FragmentMode == AgnosticProgram.Fragment.Mode.Program)
+ {
+ AgnosticProgram.Fragment.ProgramMode programMode = new(data, t.RemoteClient.Socket_);
+ programMode.Send(bp);
+ }
+ else
+ t.RemoteClient.Socket_.Send(data);
+ }
+ else
+ {
+ // Const
+ AgnosticProgram.Fragment bp = FragmentProgram;
+ bp.DestHostname = t.Req.Address;
+ bp.DestPort = t.Req.Port;
+ AgnosticProgram.Fragment.ProgramMode programMode = new(data, t.RemoteClient.Socket_);
+ programMode.Send(bp);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Send: " + ex.Message);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/DnsRules.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/DnsRules.cs
new file mode 100644
index 0000000..37b254b
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/DnsRules.cs
@@ -0,0 +1,172 @@
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public partial class AgnosticProgram
+{
+ public partial class DnsRules
+ {
+ public enum Mode
+ {
+ File,
+ Text,
+ Disable
+ }
+
+ public Mode RulesMode { get; private set; } = Mode.Disable;
+ public string PathOrText { get; private set; } = string.Empty;
+ public string TextContent { get; private set; } = string.Empty;
+
+ private List Rules_List { get; set; } = new();
+ private List> Variables { get; set; } = new(); // x = domain.com;
+ private string Default_DnsProxyScheme { get; set; } = string.Empty; // dnsproxy:;
+ private string Default_DnsProxyUser { get; set; } = string.Empty; // &user:
+ private string Default_DnsProxyPass { get; set; } = string.Empty; // &pass:
+ private List MainRules_List { get; set; } = new();
+
+ private class DnsMainRules
+ {
+ public string Client { get; set; } = string.Empty;
+ public string Domain { get; set; } = string.Empty;
+ public bool IsBlock { get; set; } = false;
+ public string FakeDns { get; set; } = string.Empty;
+ public List Dnss { get; set; } = new();
+ public string DnsDomain { get; set; } = string.Empty;
+ public string DnsProxyScheme { get; set; } = string.Empty;
+ public string DnsProxyUser { get; set; } = string.Empty;
+ public string DnsProxyPass { get; set; } = string.Empty;
+ }
+
+ public DnsRules() { }
+
+ public void Set(Mode mode, string filePathOrText)
+ {
+ Rules_List.Clear();
+ Variables.Clear();
+ Default_DnsProxyScheme = string.Empty;
+ Default_DnsProxyUser = string.Empty;
+ Default_DnsProxyPass = string.Empty;
+ MainRules_List.Clear();
+
+ RulesMode = mode;
+ PathOrText = filePathOrText;
+
+ if (RulesMode == Mode.Disable) return;
+
+ if (RulesMode == Mode.File)
+ {
+ try
+ {
+ TextContent = File.ReadAllText(Path.GetFullPath(filePathOrText));
+ }
+ catch (Exception) { }
+ }
+ else if (RulesMode == Mode.Text) TextContent = filePathOrText;
+
+ if (string.IsNullOrEmpty(TextContent) || string.IsNullOrWhiteSpace(TextContent)) return;
+
+ TextContent += Environment.NewLine;
+ Rules_List = TextContent.SplitToLines();
+
+ for (int n = 0; n < Rules_List.Count; n++)
+ {
+ string line = Rules_List[n].Trim();
+ if (line.StartsWith("//")) continue; // Support Comment //
+ if (!line.EndsWith(';')) continue; // Must Have ; At The End
+ if (string.IsNullOrEmpty(line) || string.IsNullOrWhiteSpace(line)) continue; // Line Cannot Be Empty
+
+ // Get Variables
+ if (line.Contains('=') && !line.Contains(',') && !line.Contains('&'))
+ {
+ line = line.TrimEnd(';');
+ string[] split = line.Split('=');
+ if (split.Length == 2)
+ {
+ string item1 = split[0].Trim();
+ string item2 = split[1].Trim();
+ if (!string.IsNullOrEmpty(item1) && !string.IsNullOrEmpty(item2))
+ Variables.Add(new Tuple(item1, item2));
+ }
+ }
+
+ // Get Defaults
+ else if (line.StartsWith(Rules.KEYS.DnsProxy, StringComparison.InvariantCultureIgnoreCase))
+ {
+ Default_DnsProxyScheme = Rules.GetValue(line, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.FirstKey, out _, out _, Variables);
+ Default_DnsProxyUser = Rules.GetValue(line, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.User, out _, out _, Variables);
+ Default_DnsProxyPass = Rules.GetValue(line, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.Pass, out _, out _, Variables);
+ }
+
+ // Get DnsMainRules (Client|Domain|DnsRules)
+ else if (line.Contains('|'))
+ {
+ string[] split = line.Split('|');
+ if (split.Length == 2) SetDomainRules(Rules.KEYS.AllClients, split[0].Trim(), split[1].Trim());
+ if (split.Length == 3) SetDomainRules(split[0].Trim(), split[1].Trim(), split[2].Trim());
+ }
+ }
+ }
+
+ private void SetDomainRules(string client, string domain, string rules) // rules Ends With ;
+ {
+ try
+ {
+ DnsMainRules dmr = new()
+ {
+ Client = client, // Client
+ Domain = domain // Domain
+ };
+
+ // Block
+ if (rules.Equals("block;", StringComparison.InvariantCultureIgnoreCase)) dmr.IsBlock = true;
+ if (rules.Equals("-;")) dmr.IsBlock = true;
+
+ if (!dmr.IsBlock)
+ {
+ // Fake DNS
+ string fakeDnsIpStr = Rules.GetValue(rules, Rules.KEYS.FirstKey, null, out _, out _, Variables);
+ bool isIp = NetworkTool.IsIp(fakeDnsIpStr, out _);
+ if (isIp) dmr.FakeDns = fakeDnsIpStr;
+ }
+
+ // Dnss
+ string dnss = Rules.GetValue(rules, Rules.KEYS.Dns, null, out bool isList, out List list, Variables);
+ if (!isList) // One Dns
+ {
+ if (!string.IsNullOrEmpty(dnss))
+ dmr.Dnss.Add(dnss);
+ }
+ else // Multiple Dnss
+ {
+ for (int i = 0; i < list.Count; i++)
+ {
+ string dns = list[i];
+ if (!string.IsNullOrEmpty(dns))
+ dmr.Dnss.Add(dns);
+ }
+ }
+
+ // DnsDomain
+ dmr.DnsDomain = Rules.GetValue(rules, Rules.KEYS.DnsDomain, null, out _, out _, Variables);
+
+ // DnsProxy e.g. socks5://127.0.0.1:6666&user:UserName&pass:PassWord
+ dmr.DnsProxyScheme = Rules.GetValue(rules, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.FirstKey, out _, out _, Variables);
+ dmr.DnsProxyUser = Rules.GetValue(rules, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.User, out _, out _, Variables);
+ dmr.DnsProxyPass = Rules.GetValue(rules, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.Pass, out _, out _, Variables);
+ if (string.IsNullOrEmpty(dmr.DnsProxyScheme))
+ {
+ dmr.DnsProxyScheme = Default_DnsProxyScheme;
+ dmr.DnsProxyUser = Default_DnsProxyUser;
+ dmr.DnsProxyPass = Default_DnsProxyPass;
+ }
+
+ MainRules_List.Add(dmr);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Dns Rules_SetDomainRules: " + ex.Message);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/DnsRules_Get.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/DnsRules_Get.cs
new file mode 100644
index 0000000..3f0e3c4
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/DnsRules_Get.cs
@@ -0,0 +1,129 @@
+using System.Diagnostics;
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public partial class AgnosticProgram
+{
+ public partial class DnsRules
+ {
+ public class DnsRulesResult
+ {
+ public bool IsMatch { get; set; } = false;
+ public bool IsBlackList { get; set; } = false;
+ public string DnsCustomDomain { get; set; } = string.Empty;
+ public string Dns { get; set; } = string.Empty;
+ public List Dnss { get; set; } = new();
+ }
+
+ public async Task GetAsync(string client, string host, AgnosticSettings settings)
+ {
+ DnsRulesResult drr = new();
+ if (string.IsNullOrEmpty(host)) return drr;
+
+ try
+ {
+ for (int n = 0; n < MainRules_List.Count; n++)
+ {
+ DnsMainRules dmr = MainRules_List[n];
+
+ // Check If Match
+ bool isClientMatch = !string.IsNullOrEmpty(dmr.Client) && (dmr.Client.Equals(Rules.KEYS.AllClients) || client.Equals(dmr.Client) || client.EndsWith(dmr.Client));
+ bool isDomainMatch = Rules.IsDomainMatch(host, dmr.Domain, out bool isWildcard, out string hostNoWww, out string ruleHostNoWww);
+ bool isMatch = isClientMatch && isDomainMatch;
+ if (!isMatch) continue;
+
+ // Set Match
+ drr.IsMatch = isMatch;
+
+ // Is Black List
+ drr.IsBlackList = dmr.IsBlock;
+ if (drr.IsBlackList) break;
+
+ // DNS
+ if (!string.IsNullOrEmpty(dmr.FakeDns))
+ {
+ // Fake DNS
+ drr.Dns = dmr.FakeDns;
+ }
+ else
+ {
+ // Get Dns Servers And Upstream Proxy
+ List dnss = dmr.Dnss.Any() ? dmr.Dnss : settings.DNSs;
+ drr.Dnss = dnss;
+ string? dnsProxyScheme = null, dnsProxyUser = null, dnsProxyPass = null;
+ if (!string.IsNullOrEmpty(dmr.DnsProxyScheme))
+ {
+ dnsProxyScheme = dmr.DnsProxyScheme;
+ dnsProxyUser = dmr.DnsProxyUser;
+ dnsProxyPass = dmr.DnsProxyPass;
+ }
+ else
+ {
+ dnsProxyScheme = settings.UpstreamProxyScheme;
+ dnsProxyUser = settings.UpstreamProxyUser;
+ dnsProxyPass = settings.UpstreamProxyPass;
+ }
+
+ // Get IP By Custom DNS
+ if (dnss.Any() && !NetworkTool.IsIp(host, out _))
+ {
+ // Get Custom DNS Domain
+ drr.DnsCustomDomain = host;
+ if (!string.IsNullOrEmpty(dmr.DnsDomain))
+ {
+ if (!dmr.DnsDomain.StartsWith("*."))
+ {
+ drr.DnsCustomDomain = dmr.DnsDomain;
+ }
+ else
+ {
+ // Support: xxxx.example.com -> xxxx.domain.com
+ if (isWildcard) // ruleHostNoWww.StartsWith("*.")
+ {
+ if (hostNoWww.EndsWith(ruleHostNoWww[1..])) // Just In Case
+ {
+ drr.DnsCustomDomain = hostNoWww.Replace(ruleHostNoWww[1..], dmr.DnsDomain[1..]);
+ }
+ }
+ }
+ }
+
+ IPAddress ipv4Addr = IPAddress.None;
+ if (settings.IsIPv4SupportedByISP)
+ {
+ ipv4Addr = await GetIP.GetIpFromDnsAddressAsync(drr.DnsCustomDomain, dnss, settings.AllowInsecure, settings.DnsTimeoutSec, false, settings.BootstrapIpAddress, settings.BootstrapPort, dnsProxyScheme, dnsProxyUser, dnsProxyPass);
+ if (ipv4Addr.Equals(IPAddress.None) && !settings.IsIPv6SupportedByISP) // Retry If IPv6 Is Not Supported
+ ipv4Addr = await GetIP.GetIpFromDnsAddressAsync(drr.DnsCustomDomain, dnss, settings.AllowInsecure, settings.DnsTimeoutSec, false, settings.BootstrapIpAddress, settings.BootstrapPort, dnsProxyScheme, dnsProxyUser, dnsProxyPass);
+ }
+
+ if (ipv4Addr.Equals(IPAddress.None))
+ {
+ IPAddress ipv6Addr = await GetIP.GetIpFromDnsAddressAsync(drr.DnsCustomDomain, dnss, settings.AllowInsecure, settings.DnsTimeoutSec, true, settings.BootstrapIpAddress, settings.BootstrapPort, dnsProxyScheme, dnsProxyUser, dnsProxyPass);
+ if (!ipv6Addr.Equals(IPAddress.IPv6None))
+ drr.Dns = ipv6Addr.ToString();
+ }
+ else
+ {
+ if (string.IsNullOrEmpty(settings.CloudflareCleanIP))
+ drr.Dns = ipv4Addr.ToString();
+ else
+ drr.Dns = CommonTools.IsCfIP(ipv4Addr) ? settings.CloudflareCleanIP : ipv4Addr.ToString();
+ }
+ }
+ }
+
+ // Break If Match
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("DnsRules_GetAsync: " + ex.Message);
+ }
+
+ return drr;
+ }
+
+ }
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/Fragment.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/Fragment.cs
new file mode 100644
index 0000000..f8d6a58
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/Fragment.cs
@@ -0,0 +1,575 @@
+using System.Diagnostics;
+using System.Net.Sockets;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public partial class AgnosticProgram
+{
+ public class Fragment
+ {
+ public Mode FragmentMode { get; private set; } = Mode.Disable;
+ public ChunkMode DPIChunkMode { get; private set; }
+ public int BeforeSniChunks { get; private set; } = 0;
+ public int SniChunks { get; private set; } = 0;
+ public int AntiPatternOffset { get; private set; } = 2;
+ public int FragmentDelay { get; private set; } = 0;
+ public string? DestHostname { get; set; }
+ public int DestPort { get; set; }
+
+ public event EventHandler? OnChunkDetailsReceived;
+
+ public Fragment() { }
+
+ public void Set(Mode mode, int beforeSniChunks, ChunkMode chunkMode, int sniChunks, int antiPatternOffset, int fragmentDelay)
+ {
+ FragmentMode = mode;
+ BeforeSniChunks = beforeSniChunks;
+ DPIChunkMode = chunkMode;
+ SniChunks = sniChunks;
+ AntiPatternOffset = antiPatternOffset;
+ FragmentDelay = fragmentDelay;
+ }
+
+ public enum Mode
+ {
+ Program,
+ Disable
+ }
+
+ public enum ChunkMode
+ {
+ SNI,
+ SniExtension,
+ AllExtensions
+ }
+
+ public class ProgramMode
+ {
+ private byte[] Data { get; set; }
+ private Socket Socket { get; set; }
+
+ public ProgramMode(byte[] data, Socket socket)
+ {
+ Data = data;
+ Socket = socket;
+ }
+
+ public void Send(Fragment bp)
+ {
+ try
+ {
+ int offset = bp.AntiPatternOffset;
+ Random random = new();
+
+ // Anti Pattern Fragment Chunks
+ int beforeSniChunks = random.Next(bp.BeforeSniChunks - offset, bp.BeforeSniChunks + offset);
+ if (beforeSniChunks <= 0) beforeSniChunks = 1;
+ if (beforeSniChunks > Data.Length) beforeSniChunks = Data.Length;
+
+ int sniChunks = random.Next(bp.SniChunks - offset, bp.SniChunks + offset);
+ if (sniChunks <= 0) sniChunks = 1;
+ if (sniChunks > Data.Length) sniChunks = Data.Length;
+
+ //Test(Data, Socket, beforeSniChunks, sniChunks, offset, bp);
+
+ if (bp.DPIChunkMode == ChunkMode.AllExtensions)
+ SendDataInFragmentAllExtensions(Data, Socket, beforeSniChunks, sniChunks, offset, bp);
+ else if (bp.DPIChunkMode == ChunkMode.SniExtension)
+ SendDataInFragmentSniExtension(Data, Socket, beforeSniChunks, sniChunks, offset, bp);
+ else if (bp.DPIChunkMode == ChunkMode.SNI)
+ SendDataInFragmentSNI(Data, Socket, beforeSniChunks, sniChunks, offset, bp);
+ }
+ catch (Exception) { }
+ }
+
+ private static void Test(byte[] data, Socket socket, int beforeSniChunks, int sniChunks, int offset, Fragment bp)
+ {
+ Debug.WriteLine("Send Data in TEST");
+ // Create packets
+ List packets = new();
+ packets.Clear();
+
+ SniModifire sniModifire = new(data);
+ if (sniModifire.HasSni)
+ {
+ packets.Add(sniModifire.ModifiedData);
+ SendPackets(sniModifire.ModifiedData, socket, bp, packets);
+ }
+ else
+ {
+ packets.Add(data);
+ SendPackets(data, socket, bp, packets);
+ }
+
+ }
+
+ private static void SendDataInFragmentAllExtensions(byte[] data, Socket socket, int beforeSniChunks, int sniChunks, int offset, Fragment bp)
+ {
+ //Debug.WriteLine("SendDataInFragmentAllExtensions");
+ // Create packets
+ List packets = new();
+ packets.Clear();
+
+ try
+ {
+ SniReader sniReader = new(data);
+
+ if (beforeSniChunks == 1 && sniChunks == 1)
+ {
+ packets.Add(data);
+ }
+ else
+ {
+ if (sniReader.HasTlsExtensions)
+ {
+ int prevIndex;
+ int pos = 0;
+ SniReader.TlsExtensions allExtensions = sniReader.AllExtensions;
+
+ pos += allExtensions.StartIndex;
+ prevIndex = pos - allExtensions.StartIndex;
+
+ // Create packet before SNI
+ int beforeSniLength = allExtensions.StartIndex - prevIndex;
+ if (beforeSniLength > 0)
+ {
+ byte[] beforeSNI = new byte[beforeSniLength];
+ Buffer.BlockCopy(data, prevIndex, beforeSNI, 0, beforeSniLength);
+
+ List chunkedbeforeSNI = ChunkDataNormal(beforeSNI, beforeSniChunks, offset);
+ packets = packets.Concat(chunkedbeforeSNI).ToList();
+ //Debug.WriteLine($"{prevIndex} ======> {beforeSniLength}");
+ }
+
+ // Create SNI packet
+ List chunkedSNI = ChunkDataNormal(allExtensions.Data, sniChunks, offset);
+ packets = packets.Concat(chunkedSNI).ToList();
+
+ //Debug.WriteLine($"{beforeSniLength} ====== {sni.SniStartIndex}");
+ //Debug.WriteLine($"{sni.SniStartIndex} ======> {sni.SniStartIndex + sni.SniLength}");
+ Debug.WriteLine("==-----== " + (sniReader.AllExtensions.StartIndex + sniReader.AllExtensions.Length) + " of " + data.Length);
+ pos = allExtensions.StartIndex + allExtensions.Length;
+
+ // Create packet after SNI
+ if (pos < data.Length)
+ {
+ int afterSniStartIndex = pos;
+ int afterSniLength = data.Length - pos;
+ byte[] afterSni = new byte[afterSniLength];
+ Buffer.BlockCopy(data, afterSniStartIndex, afterSni, 0, afterSniLength);
+ packets.Add(afterSni);
+
+ //Debug.WriteLine($"{sni.SniStartIndex + sni.SniLength} ====== {afterSniStartIndex}");
+ //Debug.WriteLine($"{afterSniStartIndex} ======> {afterSniStartIndex + afterSniLength}");
+ //Debug.WriteLine($"{afterSniStartIndex + afterSniLength} ====== {data.Length}");
+ }
+ }
+ else
+ {
+ packets.Add(data);
+ }
+ }
+ }
+ catch (Exception) { }
+
+ SendPackets(data, socket, bp, packets);
+ }
+
+ private static void SendDataInFragmentSniExtension(byte[] data, Socket socket, int beforeSniChunks, int sniChunks, int offset, Fragment bp)
+ {
+ //Debug.WriteLine("SendDataInFragmentSniExtension");
+ // Create packets
+ List packets = new();
+ packets.Clear();
+
+ try
+ {
+ SniReader sniReader = new(data);
+ if (sniReader.SniExtensionList.Count > 1) Debug.WriteLine($"=======================> We Have {sniReader.SniExtensionList.Count} SNI Extensions.");
+
+ if (beforeSniChunks == 1 && sniChunks == 1)
+ {
+ packets.Add(data);
+ }
+ else
+ {
+ if (sniReader.HasSniExtension)
+ {
+ int prevIndex;
+ int pos = 0;
+ for (int n = 0; n < sniReader.SniExtensionList.Count; n++)
+ {
+ SniReader.SniExtension sniExtension = sniReader.SniExtensionList[n];
+
+ pos += sniExtension.StartIndex;
+ prevIndex = pos - sniExtension.StartIndex;
+
+ // Create packet before SNI
+ int beforeSniLength = sniExtension.StartIndex - prevIndex;
+ if (beforeSniLength > 0)
+ {
+ byte[] beforeSNI = new byte[beforeSniLength];
+ Buffer.BlockCopy(data, prevIndex, beforeSNI, 0, beforeSniLength);
+
+ List chunkedbeforeSNI = ChunkDataNormal(beforeSNI, beforeSniChunks, offset);
+ packets = packets.Concat(chunkedbeforeSNI).ToList();
+ //Debug.WriteLine($"{prevIndex} ======> {beforeSniLength}");
+ }
+
+ // Create SNI packet
+ List chunkedSNI = ChunkDataNormal(sniExtension.Data, sniChunks, offset);
+ packets = packets.Concat(chunkedSNI).ToList();
+
+ //Debug.WriteLine($"{beforeSniLength} ====== {sni.SniStartIndex}");
+ //Debug.WriteLine($"{sni.SniStartIndex} ======> {sni.SniStartIndex + sni.SniLength}");
+
+ pos = sniExtension.StartIndex + sniExtension.Length;
+
+ // Last round
+ if (n == sniReader.SniExtensionList.Count - 1)
+ {
+ // Create packet after SNI
+ if (pos < data.Length)
+ {
+ int afterSniStartIndex = pos;
+ int afterSniLength = data.Length - pos;
+ byte[] afterSni = new byte[afterSniLength];
+ Buffer.BlockCopy(data, afterSniStartIndex, afterSni, 0, afterSniLength);
+ packets.Add(afterSni);
+
+ //Debug.WriteLine($"{sni.SniStartIndex + sni.SniLength} ====== {afterSniStartIndex}");
+ //Debug.WriteLine($"{afterSniStartIndex} ======> {afterSniStartIndex + afterSniLength}");
+ //Debug.WriteLine($"{afterSniStartIndex + afterSniLength} ====== {data.Length}");
+ }
+ }
+ }
+ }
+ else
+ {
+ packets.Add(data);
+ }
+ }
+ }
+ catch (Exception) { }
+
+ SendPackets(data, socket, bp, packets);
+ }
+
+ private static void SendDataInFragmentSNI(byte[] data, Socket socket, int beforeSniChunks, int sniChunks, int offset, Fragment bp)
+ {
+ //Debug.WriteLine("SendDataInFragmentSNI");
+ // Create packets
+ List packets = new();
+ packets.Clear();
+
+ try
+ {
+ SniReader sniReader = new(data);
+ if (sniReader.SniList.Count > 1) Debug.WriteLine($"=======================> We Have {sniReader.SniList.Count} SNIs.");
+
+ if (beforeSniChunks == 1 && sniChunks == 1)
+ {
+ packets.Add(data);
+ }
+ else
+ {
+ if (sniReader.HasSni)
+ {
+ int prevIndex;
+ int pos = 0;
+ for (int n = 0; n < sniReader.SniList.Count; n++)
+ {
+ SniReader.SNI sni = sniReader.SniList[n];
+
+ pos += sni.StartIndex;
+ prevIndex = pos - sni.StartIndex;
+
+ // Create packet before SNI
+ int beforeSniLength = sni.StartIndex - prevIndex;
+ if (beforeSniLength > 0)
+ {
+ byte[] beforeSNI = new byte[beforeSniLength];
+ Buffer.BlockCopy(data, prevIndex, beforeSNI, 0, beforeSniLength);
+
+ List chunkedbeforeSNI = ChunkDataNormal(beforeSNI, beforeSniChunks, offset);
+ packets = packets.Concat(chunkedbeforeSNI).ToList();
+ //Debug.WriteLine($"{prevIndex} ======> {beforeSniLength}");
+ }
+
+ // Create SNI packet
+ List chunkedSNI = ChunkDataNormal(sni.Data, sniChunks, offset);
+ packets = packets.Concat(chunkedSNI).ToList();
+
+ //Debug.WriteLine($"{beforeSniLength} ====== {sni.SniStartIndex}");
+ //Debug.WriteLine($"{sni.SniStartIndex} ======> {sni.SniStartIndex + sni.SniLength}");
+
+ pos = sni.StartIndex + sni.Length;
+
+ // Last round
+ if (n == sniReader.SniList.Count - 1)
+ {
+ // Create packet after SNI
+ if (pos < data.Length)
+ {
+ int afterSniStartIndex = pos;
+ int afterSniLength = data.Length - pos;
+ byte[] afterSni = new byte[afterSniLength];
+ Buffer.BlockCopy(data, afterSniStartIndex, afterSni, 0, afterSniLength);
+ packets.Add(afterSni);
+
+ //Debug.WriteLine($"{sni.SniStartIndex + sni.SniLength} ====== {afterSniStartIndex}");
+ //Debug.WriteLine($"{afterSniStartIndex} ======> {afterSniStartIndex + afterSniLength}");
+ //Debug.WriteLine($"{afterSniStartIndex + afterSniLength} ====== {data.Length}");
+ }
+ }
+ }
+ }
+ else
+ {
+ packets.Add(data);
+ }
+ }
+ }
+ catch (Exception) { }
+
+ SendPackets(data, socket, bp, packets);
+ }
+
+ private static List ChunkDataNormal(byte[] data, int chunks, int offset)
+ {
+ //Debug.WriteLine("ChunkDataNormal");
+ // Create chunk packets
+ Random random = new();
+ List chunkPackets = new();
+ chunkPackets.Clear();
+
+ int prevIndex;
+ int nn = 0;
+ int sum = 0;
+ for (int n = 0; n < data.Length; n++)
+ {
+ try
+ {
+ // Anti Pattern Fragment Size
+ int fragmentSize = data.Length / chunks;
+
+ int fragmentSizeOut = random.Next(fragmentSize - offset, fragmentSize + offset);
+ if (fragmentSizeOut <= 0) fragmentSizeOut = 1;
+ if (fragmentSizeOut > data.Length) fragmentSizeOut = data.Length;
+ nn += fragmentSizeOut;
+
+ if (nn > data.Length)
+ {
+ fragmentSizeOut = data.Length - (nn - fragmentSizeOut);
+ //Debug.WriteLine(fragmentSizeOut);
+ }
+ //Debug.WriteLine(fragmentSizeOut);
+
+ sum += fragmentSizeOut;
+ byte[] fragmentData = new byte[fragmentSizeOut];
+ prevIndex = sum - fragmentSizeOut;
+ Buffer.BlockCopy(data, prevIndex, fragmentData, 0, fragmentSizeOut);
+ chunkPackets.Add(fragmentData);
+
+ if (sum >= data.Length) break;
+ }
+ catch (Exception ex)
+ {
+ chunkPackets.Clear();
+ string msgEvent = $"Error, Creating normal packets: {ex.Message}";
+ Debug.WriteLine(msgEvent);
+ return chunkPackets;
+ }
+ }
+
+ return chunkPackets;
+ }
+
+ private static List ChunkDataNormal2(byte[] data, int fragmentSize)
+ {
+ Debug.WriteLine("ChunkDataNormal2");
+ // Create chunk packets
+ List chunkPackets = new();
+ chunkPackets.Clear();
+
+ var fragments = data.Chunk(fragmentSize);
+ for (int n = 0; n < fragments.Count(); n++)
+ {
+ try
+ {
+ byte[] fragment = fragments.ToArray()[n];
+ chunkPackets.Add(fragment);
+ }
+ catch (Exception ex)
+ {
+ chunkPackets.Clear();
+ string msgEvent = $"Error, Creating normal2 packets: {ex.Message}";
+ Debug.WriteLine(msgEvent);
+ return chunkPackets;
+ }
+ }
+
+ return chunkPackets;
+ }
+
+ private static List ChunkDataNormal3(byte[] data, int fragmentSize)
+ {
+ Debug.WriteLine("ChunkDataNormal3");
+ // Create chunk packets
+ List chunkPackets = new();
+ chunkPackets.Clear();
+
+ var fragments = ChunkViaMemory(data, fragmentSize);
+ for (int n = 0; n < fragments.Count(); n++)
+ {
+ try
+ {
+ byte[] fragment = fragments.ToArray()[n].ToArray();
+ chunkPackets.Add(fragment);
+ }
+ catch (Exception ex)
+ {
+ chunkPackets.Clear();
+ string msgEvent = $"Error, Creating normal3 packets: {ex.Message}";
+ Debug.WriteLine(msgEvent);
+ return chunkPackets;
+ }
+ }
+
+ return chunkPackets;
+ }
+
+ private static List ChunkDataRandom(byte[] data, int fragmentChunks)
+ {
+ Debug.WriteLine("ChunkDataRandom");
+ //// Calculate fragment chunks from size
+ //int fragmentChunks = data.Length / fragmentSize;
+ //if (fragmentChunks <= 0) fragmentChunks = 1;
+ //if (fragmentChunks > data.Length) fragmentChunks = data.Length;
+
+ // Create chunk packets
+ List packets = new();
+ packets.Clear();
+
+ try
+ {
+ fragmentChunks = Math.Min(fragmentChunks, data.Length);
+ List indices;
+ if (fragmentChunks < data.Length)
+ indices = GenerateRandomIndices(1, data.Length - 1, fragmentChunks - 1);
+ else
+ indices = Enumerable.Range(0, data.Length - 1).ToList();
+ indices.Sort();
+
+ int prevIndex = 0;
+ for (int n = 0; n < indices.Count; n++)
+ {
+ try
+ {
+ int index = indices[n];
+ byte[] fragmentData = new byte[index - prevIndex];
+ Buffer.BlockCopy(data, prevIndex, fragmentData, 0, fragmentData.Length);
+ packets.Add(fragmentData);
+ prevIndex = index;
+ }
+ catch (Exception ex)
+ {
+ packets.Clear();
+ string msgEvent = $"Error, Creating random packets: {ex.Message}";
+ Debug.WriteLine(msgEvent);
+ return packets;
+ }
+ }
+
+ try
+ {
+ byte[] lastFragmentData = new byte[data.Length - prevIndex];
+ Buffer.BlockCopy(data, prevIndex, lastFragmentData, 0, lastFragmentData.Length);
+ packets.Add(lastFragmentData);
+ }
+ catch (Exception ex)
+ {
+ packets.Clear();
+ string msgEvent = $"Error, Creating last random packet: {ex.Message}";
+ Debug.WriteLine(msgEvent);
+ return packets;
+ }
+ }
+ catch (Exception) { }
+
+ return packets;
+ }
+
+ private static void SendPackets(byte[] data, Socket socket, Fragment bp, List packets)
+ {
+ // Check packets
+ int allLength = 0;
+ for (int i = 0; i < packets.Count; i++)
+ allLength += packets[i].Length;
+
+ if (allLength != data.Length)
+ {
+ Debug.WriteLine($"{allLength} == {data.Length}, Chunks: {packets.Count}");
+ packets.Clear();
+ return;
+ }
+
+ // Send packets
+ for (int i = 0; i < packets.Count; i++)
+ {
+ try
+ {
+ byte[] fragmentData = packets[i];
+ if (socket == null) return;
+ socket.Send(fragmentData);
+ if (bp.FragmentDelay > 0)
+ Task.Delay(bp.FragmentDelay).Wait();
+ }
+ catch (Exception ex)
+ {
+ string msgEvent = $"Error, Send Packets: {ex.Message}";
+ Debug.WriteLine(msgEvent);
+ return;
+ }
+ }
+
+ string chunkDetailsEvent = $"{bp.DestHostname}:{bp.DestPort} Length: {data.Length}";
+ if (packets.Count > 1)
+ chunkDetailsEvent += $", Chunks: {packets.Count}";
+ bp.OnChunkDetailsReceived?.Invoke(chunkDetailsEvent, EventArgs.Empty);
+ }
+ }
+
+ private static List GenerateRandomIndices(int minValue, int maxValue, int count)
+ {
+ Random random = new();
+ HashSet indicesSet = new();
+
+ try
+ {
+ while (indicesSet.Count < count)
+ {
+ indicesSet.Add(random.Next(minValue, maxValue));
+ }
+ }
+ catch (Exception) { }
+
+ return new List(indicesSet);
+ }
+
+ private static IEnumerable> ChunkViaMemory(T[] data, int size)
+ {
+ var chunks = data.Length / size;
+ for (int i = 0; i < chunks; i++)
+ {
+ yield return data.AsMemory(i * size, size);
+ }
+ var leftOver = data.Length % size;
+ if (leftOver > 0)
+ {
+ yield return data.AsMemory(chunks * size, leftOver);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/ProxyRules.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/ProxyRules.cs
new file mode 100644
index 0000000..50100a3
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/ProxyRules.cs
@@ -0,0 +1,301 @@
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public partial class AgnosticProgram
+{
+ public partial class ProxyRules
+ {
+ public enum Mode
+ {
+ File,
+ Text,
+ Disable
+ }
+
+ public Mode RulesMode { get; private set; } = Mode.Disable;
+ public string PathOrText { get; private set; } = string.Empty;
+ public string TextContent { get; private set; } = string.Empty;
+
+ private List Rules_List { get; set; } = new();
+ private List> Variables { get; set; } = new(); // x = domain.com;
+ private List Default_BlockPort { get; set; } = new(); // blockport:80,53;
+ private List Default_Dnss { get; set; } = new(); // dns:
+ private string Default_DnsDomain { get; set; } = string.Empty; // dnsdomain:;
+ private string Default_DnsProxyScheme { get; set; } = string.Empty; // dnsproxy:;
+ private string Default_DnsProxyUser { get; set; } = string.Empty; // &user:
+ private string Default_DnsProxyPass { get; set; } = string.Empty; // &pass:
+ private string Default_Sni { get; set; } = string.Empty; // sni:;
+ private string Default_ProxyScheme { get; set; } = string.Empty; // proxy:;
+ private bool Default_ProxyIfBlock { get; set; } = false; // &ifblock:1
+ private string Default_ProxyUser { get; set; } = string.Empty; // &user:
+ private string Default_ProxyPass { get; set; } = string.Empty; // &pass:
+ private List MainRules_List { get; set; } = new();
+
+ private class ProxyMainRules
+ {
+ public string Client { get; set; } = string.Empty;
+ public string Domain { get; set; } = string.Empty;
+ public bool IsBlock { get; set; } = false;
+ public List BlockPort { get; set; } = new();
+ public bool NoBypass { get; set; } = false;
+ public string FakeDns { get; set; } = string.Empty;
+ public List Dnss { get; set; } = new();
+ public string DnsDomain { get; set; } = string.Empty;
+ public string DnsProxyScheme { get; set; } = string.Empty;
+ public string DnsProxyUser { get; set; } = string.Empty;
+ public string DnsProxyPass { get; set; } = string.Empty;
+ public string Sni { get; set; } = string.Empty;
+ public string ProxyScheme { get; set; } = string.Empty;
+ public bool ProxyIfBlock { get; set; } = false;
+ public string ProxyUser { get; set; } = string.Empty;
+ public string ProxyPass { get; set; } = string.Empty;
+ }
+
+ public ProxyRules() { }
+
+ public void Set(Mode mode, string filePathOrText)
+ {
+ Rules_List.Clear();
+ Variables.Clear();
+ Default_BlockPort.Clear();
+ Default_Dnss.Clear();
+ Default_DnsDomain = string.Empty;
+ Default_DnsProxyScheme = string.Empty;
+ Default_DnsProxyUser = string.Empty;
+ Default_DnsProxyPass = string.Empty;
+ Default_Sni = string.Empty;
+ Default_ProxyScheme = string.Empty;
+ Default_ProxyIfBlock = false;
+ Default_ProxyUser = string.Empty;
+ Default_ProxyPass = string.Empty;
+ MainRules_List.Clear();
+
+ RulesMode = mode;
+ PathOrText = filePathOrText;
+
+ if (RulesMode == Mode.Disable) return;
+
+ if (RulesMode == Mode.File)
+ {
+ try
+ {
+ TextContent = File.ReadAllText(Path.GetFullPath(filePathOrText));
+ }
+ catch (Exception) { }
+ }
+ else if (RulesMode == Mode.Text) TextContent = filePathOrText;
+
+ if (string.IsNullOrEmpty(TextContent) || string.IsNullOrWhiteSpace(TextContent)) return;
+
+ TextContent += Environment.NewLine;
+ Rules_List = TextContent.SplitToLines();
+
+ for (int n = 0; n < Rules_List.Count; n++)
+ {
+ string line = Rules_List[n].Trim();
+ if (line.StartsWith("//")) continue; // Support Comment //
+ if (!line.EndsWith(';')) continue; // Must Have ; At The End
+ if (string.IsNullOrEmpty(line) || string.IsNullOrWhiteSpace(line)) continue; // Line Cannot Be Empty
+
+ // Get Variables
+ if (line.Contains('=') && !line.Contains(',') && !line.Contains('&'))
+ {
+ line = line.TrimEnd(';');
+ string[] split = line.Split('=');
+ if (split.Length == 2)
+ {
+ string item1 = split[0].Trim();
+ string item2 = split[1].Trim();
+ if (!string.IsNullOrEmpty(item1) && !string.IsNullOrEmpty(item2))
+ Variables.Add(new Tuple(item1, item2));
+ }
+ }
+
+ // Get Defaults
+ else if (line.StartsWith(Rules.KEYS.BlockPort, StringComparison.InvariantCultureIgnoreCase))
+ {
+ string ports = Rules.GetValue(line, Rules.KEYS.BlockPort, null, out bool isList, out List list, Variables);
+ if (!isList) // One Port
+ {
+ bool success = int.TryParse(ports, out int port);
+ if (success) Default_BlockPort.Add(port);
+ }
+ else // Multiple Ports
+ {
+ for (int i = 0; i < list.Count; i++)
+ {
+ string portStr = list[i];
+ bool success = int.TryParse(portStr, out int port);
+ if (success) Default_BlockPort.Add(port);
+ }
+ }
+ continue;
+ }
+ else if (line.StartsWith(Rules.KEYS.Dns, StringComparison.InvariantCultureIgnoreCase))
+ {
+ string dnss = Rules.GetValue(line, Rules.KEYS.Dns, null, out bool isList, out List list, Variables);
+ if (!isList) // One Dns
+ {
+ if (!string.IsNullOrEmpty(dnss))
+ Default_Dnss.Add(dnss);
+ }
+ else // Multiple Dnss
+ {
+ for (int i = 0; i < list.Count; i++)
+ {
+ string dns = list[i];
+ if (!string.IsNullOrEmpty(dns))
+ Default_Dnss.Add(dns);
+ }
+ }
+ }
+ else if (line.StartsWith(Rules.KEYS.DnsDomain, StringComparison.InvariantCultureIgnoreCase))
+ {
+ Default_DnsDomain = Rules.GetValue(line, Rules.KEYS.DnsDomain, null, out _, out _, Variables);
+ }
+ else if (line.StartsWith(Rules.KEYS.DnsProxy, StringComparison.InvariantCultureIgnoreCase))
+ {
+ Default_DnsProxyScheme = Rules.GetValue(line, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.FirstKey, out _, out _, Variables);
+ Default_DnsProxyUser = Rules.GetValue(line, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.User, out _, out _, Variables);
+ Default_DnsProxyPass = Rules.GetValue(line, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.Pass, out _, out _, Variables);
+ }
+ else if (line.StartsWith(Rules.KEYS.Sni, StringComparison.InvariantCultureIgnoreCase))
+ {
+ Default_Sni = Rules.GetValue(line, Rules.KEYS.Sni, null, out _, out _, Variables);
+ }
+ else if (line.StartsWith(Rules.KEYS.Proxy, StringComparison.InvariantCultureIgnoreCase))
+ {
+ Default_ProxyScheme = Rules.GetValue(line, Rules.KEYS.Proxy, Rules.SUB_KEYS.FirstKey, out _, out _, Variables);
+ string ifBlock = Rules.GetValue(line, Rules.KEYS.Proxy, Rules.SUB_KEYS.IfBlock, out _, out _, Variables).ToLower().Trim();
+ if (!string.IsNullOrEmpty(ifBlock))
+ Default_ProxyIfBlock = ifBlock.Equals("1") || ifBlock.Equals("true");
+ Default_ProxyUser = Rules.GetValue(line, Rules.KEYS.Proxy, Rules.SUB_KEYS.User, out _, out _, Variables);
+ Default_ProxyPass = Rules.GetValue(line, Rules.KEYS.Proxy, Rules.SUB_KEYS.Pass, out _, out _, Variables);
+ }
+
+ // Get ProxyMainRules (Client|Domain|ProxyRules)
+ else if (line.Contains('|'))
+ {
+ string[] split = line.Split('|');
+ if (split.Length == 2) SetDomainRules(Rules.KEYS.AllClients, split[0].Trim(), split[1].Trim());
+ if (split.Length == 3) SetDomainRules(split[0].Trim(), split[1].Trim(), split[2].Trim());
+ }
+ }
+ }
+
+ private void SetDomainRules(string client, string domain, string rules) // rules Ends With ;
+ {
+ try
+ {
+ ProxyMainRules pmr = new()
+ {
+ Client = client, // Client
+ Domain = domain // Domain
+ };
+
+ // Block
+ if (rules.Equals("block;", StringComparison.InvariantCultureIgnoreCase)) pmr.IsBlock = true;
+ if (rules.Equals("-;")) pmr.IsBlock = true;
+
+ if (!pmr.IsBlock)
+ {
+ // No Bypass
+ if (rules.Contains("nobypass;", StringComparison.InvariantCultureIgnoreCase)) pmr.NoBypass = true;
+ if (rules.Contains("--;")) pmr.NoBypass = true;
+
+ // Fake DNS
+ string fakeDnsIpStr = Rules.GetValue(rules, Rules.KEYS.FirstKey, null, out _, out _, Variables);
+ bool isIp = NetworkTool.IsIp(fakeDnsIpStr, out _);
+ if (isIp) pmr.FakeDns = fakeDnsIpStr;
+
+ // BlockPort
+ string ports = Rules.GetValue(rules, Rules.KEYS.BlockPort, null, out bool isList, out List list, Variables);
+ if (!isList) // One Port
+ {
+ bool success = int.TryParse(ports, out int port);
+ if (success) pmr.BlockPort.Add(port);
+ }
+ else // Multiple Ports
+ {
+ for (int i = 0; i < list.Count; i++)
+ {
+ string portStr = list[i];
+ bool success = int.TryParse(portStr, out int port);
+ if (success) pmr.BlockPort.Add(port);
+ }
+ }
+ if (Default_BlockPort.Any())
+ {
+ try
+ {
+ pmr.BlockPort = pmr.BlockPort.Concat(Default_BlockPort).ToList();
+ pmr.BlockPort = pmr.BlockPort.Distinct().ToList();
+ }
+ catch (Exception) { }
+ }
+
+ // Dnss
+ string dnss = Rules.GetValue(rules, Rules.KEYS.Dns, null, out isList, out list, Variables);
+ if (!isList) // One Dns
+ {
+ if (!string.IsNullOrEmpty(dnss))
+ pmr.Dnss.Add(dnss);
+ }
+ else // Multiple Dnss
+ {
+ for (int i = 0; i < list.Count; i++)
+ {
+ string dns = list[i];
+ if (!string.IsNullOrEmpty(dns))
+ pmr.Dnss.Add(dns);
+ }
+ }
+
+ if (!pmr.Dnss.Any() && Default_Dnss.Any()) pmr.Dnss = Default_Dnss;
+
+ // DnsDomain
+ pmr.DnsDomain = Rules.GetValue(rules, Rules.KEYS.DnsDomain, null, out _, out _, Variables);
+ if (string.IsNullOrEmpty(pmr.DnsDomain)) pmr.DnsDomain = Default_DnsDomain;
+
+ // DnsProxy e.g. socks5://127.0.0.1:6666&user:UserName&pass:PassWord
+ pmr.DnsProxyScheme = Rules.GetValue(rules, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.FirstKey, out _, out _, Variables);
+ pmr.DnsProxyUser = Rules.GetValue(rules, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.User, out _, out _, Variables);
+ pmr.DnsProxyPass = Rules.GetValue(rules, Rules.KEYS.DnsProxy, Rules.SUB_KEYS.Pass, out _, out _, Variables);
+ if (string.IsNullOrEmpty(pmr.DnsProxyScheme))
+ {
+ pmr.DnsProxyScheme = Default_DnsProxyScheme;
+ pmr.DnsProxyUser = Default_DnsProxyUser;
+ pmr.DnsProxyPass = Default_DnsProxyPass;
+ }
+
+ // SNI
+ pmr.Sni = Rules.GetValue(rules, Rules.KEYS.Sni, null, out _, out _, Variables);
+ if (string.IsNullOrEmpty(pmr.Sni)) pmr.Sni = Default_Sni;
+
+ // Proxy e.g. socks5://127.0.0.1:6666&ifblock:1&user:UserName&pass:PassWord
+ pmr.ProxyScheme = Rules.GetValue(rules, Rules.KEYS.Proxy, Rules.SUB_KEYS.FirstKey, out _, out _, Variables);
+ string ifBlock = Rules.GetValue(rules, Rules.KEYS.Proxy, Rules.SUB_KEYS.IfBlock, out _, out _, Variables).ToLower().Trim();
+ if (!string.IsNullOrEmpty(ifBlock))
+ pmr.ProxyIfBlock = ifBlock.Equals("1") || ifBlock.Equals("true");
+ pmr.ProxyUser = Rules.GetValue(rules, Rules.KEYS.Proxy, Rules.SUB_KEYS.User, out _, out _, Variables);
+ pmr.ProxyPass = Rules.GetValue(rules, Rules.KEYS.Proxy, Rules.SUB_KEYS.Pass, out _, out _, Variables);
+ if (string.IsNullOrEmpty(pmr.ProxyScheme))
+ {
+ pmr.ProxyScheme = Default_ProxyScheme;
+ pmr.ProxyIfBlock = Default_ProxyIfBlock;
+ pmr.ProxyUser = Default_ProxyUser;
+ pmr.ProxyPass = Default_ProxyPass;
+ }
+ }
+
+ MainRules_List.Add(pmr);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Proxy Rules_SetDomainRules: " + ex.Message);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/ProxyRules_ConnectToUpStream.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/ProxyRules_ConnectToUpStream.cs
new file mode 100644
index 0000000..f833f6d
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/ProxyRules_ConnectToUpStream.cs
@@ -0,0 +1,26 @@
+using MsmhToolsClass.ProxifiedClients;
+using System.Net.Sockets;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public partial class AgnosticProgram
+{
+ public partial class ProxyRules
+ {
+ public async Task ConnectToUpStream(ProxyRequest req)
+ {
+ ProxyRulesResult prr = req.RulesResult;
+ string destHostname = req.Address;
+ int destHostPort = req.Port;
+
+ if (!prr.ApplyUpStreamProxy) return null;
+ if (string.IsNullOrEmpty(prr.ProxyScheme)) return null;
+
+ ProxifiedTcpClient proxifiedTcpClient = new(prr.ProxyScheme, prr.ProxyUser, prr.ProxyPass);
+ var upstream = await proxifiedTcpClient.TryGetConnectedProxifiedTcpClient(destHostname, destHostPort);
+ if (upstream.isSuccess && upstream.proxifiedTcpClient != null) return upstream.proxifiedTcpClient;
+
+ return null;
+ }
+ }
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/ProxyRules_Get.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/ProxyRules_Get.cs
new file mode 100644
index 0000000..9297a61
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/ProxyRules_Get.cs
@@ -0,0 +1,186 @@
+using System.Diagnostics;
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public partial class AgnosticProgram
+{
+ public partial class ProxyRules
+ {
+ public class ProxyRulesResult
+ {
+ public bool IsMatch { get; set; } = false;
+ public bool IsBlackList { get; set; } = false;
+ public bool IsPortBlock { get; set; } = false;
+ public bool ApplyDpiBypass { get; set; } = true;
+ public string DnsCustomDomain { get; set; } = string.Empty;
+ public string Dns { get; set; } = string.Empty;
+ public List Dnss { get; set; } = new();
+ public string Sni { get; set; } = string.Empty;
+ public bool ApplyUpStreamProxy { get; set; } = false;
+ public string ProxyScheme { get; set; } = string.Empty;
+ public bool ApplyUpStreamProxyToBlockedIPs { get; set; } = false;
+ public string ProxyUser { get; set; } = string.Empty;
+ public string ProxyPass { get; set; } = string.Empty;
+ }
+
+ public async Task GetAsync(string client, string host, int port, AgnosticSettings settings)
+ {
+ ProxyRulesResult prr = new();
+ if (string.IsNullOrEmpty(host)) return prr;
+
+ try
+ {
+ prr.Dns = host;
+
+ for (int n = 0; n < MainRules_List.Count; n++)
+ {
+ ProxyMainRules pmr = MainRules_List[n];
+
+ // Check If Match
+ bool isClientMatch = !string.IsNullOrEmpty(pmr.Client) && (pmr.Client.Equals(Rules.KEYS.AllClients) || client.Equals(pmr.Client) || client.EndsWith(pmr.Client));
+ bool isDomainMatch = Rules.IsDomainMatch(host, pmr.Domain, out bool isWildcard, out string hostNoWww, out string ruleHostNoWww);
+ bool isMatch = isClientMatch && isDomainMatch;
+ if (!isMatch) continue;
+
+ // Set Match
+ prr.IsMatch = isMatch;
+
+ // Is Black List
+ prr.IsBlackList = pmr.IsBlock;
+ if (prr.IsBlackList) break;
+
+ // Is Port Block
+ List blockedPorts = pmr.BlockPort.ToList();
+ for (int i = 0; i < blockedPorts.Count; i++)
+ {
+ int blockedPort = blockedPorts[i];
+ if (port == blockedPort)
+ {
+ prr.IsPortBlock = true;
+ break;
+ }
+ }
+ if (prr.IsPortBlock) break;
+
+ // Apply DPI Bypass (Fragment & Change SNI)
+ prr.ApplyDpiBypass = !pmr.NoBypass;
+
+ // DNS
+ if (!string.IsNullOrEmpty(pmr.FakeDns))
+ {
+ // Fake DNS
+ prr.Dns = pmr.FakeDns;
+ }
+ else
+ {
+ // Get Dns Servers And Upstream Proxy
+ List dnss = pmr.Dnss.Any() ? pmr.Dnss : settings.DNSs;
+ prr.Dnss = dnss;
+ string? dnsProxyScheme = null, dnsProxyUser = null, dnsProxyPass = null;
+ if (!string.IsNullOrEmpty(pmr.DnsProxyScheme))
+ {
+ dnsProxyScheme = pmr.DnsProxyScheme;
+ dnsProxyUser = pmr.DnsProxyUser;
+ dnsProxyPass = pmr.DnsProxyPass;
+ }
+ else
+ {
+ dnsProxyScheme = settings.UpstreamProxyScheme;
+ dnsProxyUser = settings.UpstreamProxyUser;
+ dnsProxyPass = settings.UpstreamProxyPass;
+ }
+
+ // Get IP By Custom DNS
+ if (dnss.Any() && !NetworkTool.IsIp(host, out _))
+ {
+ // Get Custom DNS Domain
+ prr.DnsCustomDomain = host;
+ if (!string.IsNullOrEmpty(pmr.DnsDomain))
+ {
+ if (!pmr.DnsDomain.StartsWith("*."))
+ {
+ prr.DnsCustomDomain = pmr.DnsDomain;
+ }
+ else
+ {
+ // Support: xxxx.example.com -> xxxx.domain.com
+ if (isWildcard) // ruleHostNoWww.StartsWith("*.")
+ {
+ if (hostNoWww.EndsWith(ruleHostNoWww[1..])) // Just In Case
+ {
+ prr.DnsCustomDomain = hostNoWww.Replace(ruleHostNoWww[1..], pmr.DnsDomain[1..]);
+ }
+ }
+ }
+ }
+
+ IPAddress ipv4Addr = IPAddress.None;
+ if (settings.IsIPv4SupportedByISP)
+ {
+ ipv4Addr = await GetIP.GetIpFromDnsAddressAsync(prr.DnsCustomDomain, dnss, settings.AllowInsecure, settings.DnsTimeoutSec, false, settings.BootstrapIpAddress, settings.BootstrapPort, dnsProxyScheme, dnsProxyUser, dnsProxyPass);
+ if (ipv4Addr.Equals(IPAddress.None) && !settings.IsIPv6SupportedByISP) // Retry If IPv6 Is Not Supported
+ ipv4Addr = await GetIP.GetIpFromDnsAddressAsync(prr.DnsCustomDomain, dnss, settings.AllowInsecure, settings.DnsTimeoutSec, false, settings.BootstrapIpAddress, settings.BootstrapPort, dnsProxyScheme, dnsProxyUser, dnsProxyPass);
+ }
+
+ if (ipv4Addr.Equals(IPAddress.None))
+ {
+ IPAddress ipv6Addr = await GetIP.GetIpFromDnsAddressAsync(prr.DnsCustomDomain, dnss, settings.AllowInsecure, settings.DnsTimeoutSec, true, settings.BootstrapIpAddress, settings.BootstrapPort, dnsProxyScheme, dnsProxyUser, dnsProxyPass);
+ if (!ipv6Addr.Equals(IPAddress.IPv6None))
+ prr.Dns = ipv6Addr.ToString();
+ }
+ else
+ {
+ if (string.IsNullOrEmpty(settings.CloudflareCleanIP))
+ prr.Dns = ipv4Addr.ToString();
+ else
+ prr.Dns = CommonTools.IsCfIP(ipv4Addr) ? settings.CloudflareCleanIP : ipv4Addr.ToString();
+ }
+ }
+ }
+
+ // SNI
+ prr.Sni = pmr.Sni;
+ if (!string.IsNullOrEmpty(prr.Sni) && prr.Sni.StartsWith("*."))
+ {
+ // Support: xxxx.example.com -> xxxx.domain.com
+ if (isWildcard) // ruleHostNoWww.StartsWith("*.")
+ {
+ if (hostNoWww.EndsWith(ruleHostNoWww[1..])) // Just In Case
+ {
+ prr.Sni = hostNoWww.Replace(ruleHostNoWww[1..], prr.Sni[1..]);
+ }
+ }
+ }
+ if (string.IsNullOrEmpty(prr.Sni)) prr.Sni = host; // Set SNI To Original Host If Not Defined
+
+ // Upstream Proxy
+ if (!string.IsNullOrEmpty(pmr.ProxyScheme))
+ {
+ pmr.ProxyScheme = pmr.ProxyScheme.ToLower().Trim();
+ if (pmr.ProxyScheme.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
+ pmr.ProxyScheme.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
+ pmr.ProxyScheme.StartsWith("socks5://", StringComparison.OrdinalIgnoreCase))
+ {
+ prr.ApplyUpStreamProxy = true;
+ prr.ProxyScheme = pmr.ProxyScheme;
+ prr.ApplyUpStreamProxyToBlockedIPs = pmr.ProxyIfBlock;
+ prr.ProxyUser = pmr.ProxyUser;
+ prr.ProxyPass = pmr.ProxyPass;
+ }
+ }
+
+ // Break If Match
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("ProxyRules_GetAsync: " + ex.Message);
+ }
+
+ return prr;
+ }
+
+ }
+}
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/Rules.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/Rules.cs
new file mode 100644
index 0000000..5c015b5
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/Programs/Rules.cs
@@ -0,0 +1,174 @@
+using System.Diagnostics;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public partial class AgnosticProgram
+{
+ public static class Rules
+ {
+ public readonly struct KEYS
+ {
+ public static readonly string FirstKey = "FirstKey";
+ public static readonly string AllClients = "AllClients";
+ public static readonly string BlockPort = "blockport:";
+ public static readonly string Dns = "dns:";
+ public static readonly string DnsDomain = "dnsdomain:";
+ public static readonly string DnsProxy = "dnsproxy:";
+ public static readonly string Sni = "sni:";
+ public static readonly string Proxy = "proxy:";
+ }
+
+ public readonly struct SUB_KEYS
+ {
+ public static readonly string FirstKey = "FirstKey";
+ public static readonly string IfBlock = "&ifblock:";
+ public static readonly string User = "&user:";
+ public static readonly string Pass = "&pass:";
+ }
+
+ public static string GetValue(string line, string key, string? subKey, out bool isList, out List list, List> variables)
+ {
+ string result = line.Trim();
+ isList = false;
+ list = new();
+
+ try
+ {
+ if (result.Contains(key, StringComparison.InvariantCultureIgnoreCase) || key == KEYS.FirstKey)
+ {
+ try
+ {
+ if (key == KEYS.FirstKey)
+ {
+ result = result.Remove(result.IndexOf(';'));
+ result = result.Trim();
+ }
+ else
+ {
+ result = result.Remove(0, result.IndexOf(key, StringComparison.InvariantCultureIgnoreCase) + key.Length);
+ result = result.Remove(result.IndexOf(';'));
+ result = result.Trim();
+ }
+ }
+ catch (Exception) { }
+
+ if (!string.IsNullOrEmpty(subKey))
+ {
+ if (subKey.Equals(SUB_KEYS.FirstKey, StringComparison.InvariantCultureIgnoreCase))
+ {
+ if (result.Contains('&'))
+ {
+ try
+ {
+ result = result.Remove(result.IndexOf('&'));
+ }
+ catch (Exception) { }
+ }
+ }
+ else
+ {
+ if (result.Contains(subKey, StringComparison.InvariantCultureIgnoreCase))
+ {
+ try
+ {
+ result = result.Remove(0, result.IndexOf(subKey, StringComparison.InvariantCultureIgnoreCase) + subKey.Length);
+ }
+ catch (Exception) { }
+
+ if (result.Contains('&') && result.Contains(':'))
+ {
+ try
+ {
+ result = result.Remove(result.IndexOf('&'));
+ }
+ catch (Exception) { }
+ }
+ }
+ }
+ }
+
+ if (!result.Contains(','))
+ {
+ // Not A List
+ return ApplyVariables(result, variables);
+ }
+ else
+ {
+ // It's A List
+ isList = true;
+ string[] split = result.Split(',');
+ for (int n = 0; n < split.Length; n++)
+ {
+ string value = split[n].Trim();
+ list.Add(ApplyVariables(value, variables));
+ }
+ if (list.Any()) return list[0];
+ }
+ }
+ }
+ catch (Exception) { }
+
+ return string.Empty;
+ }
+
+ private static string ApplyVariables(string vari, List> variables)
+ {
+ string result = vari;
+ try
+ {
+ variables = variables.ToList();
+ for (int n = 0; n < variables.Count; n++)
+ {
+ Tuple tuple = variables[n];
+ if (vari.Equals(tuple.Item1))
+ {
+ result = tuple.Item2; break;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Rules_ApplyVariables: " + ex.Message);
+ }
+ return result;
+ }
+
+ public static bool IsDomainMatch(string host, string ruleHost, out bool isWildcard, out string hostNoWWW, out string ruleHostNoWWW)
+ {
+ isWildcard = false;
+ hostNoWWW = host.ToLower().Trim();
+ ruleHostNoWWW = ruleHost.ToLower().Trim();
+
+ try
+ {
+ if (hostNoWWW.StartsWith("www."))
+ hostNoWWW = hostNoWWW.TrimStart("www.");
+ if (hostNoWWW.EndsWith('/')) hostNoWWW = hostNoWWW[0..^1];
+
+ if (ruleHostNoWWW.StartsWith("www."))
+ ruleHostNoWWW = ruleHostNoWWW.TrimStart("www.");
+ if (ruleHostNoWWW.EndsWith('/')) ruleHostNoWWW = ruleHostNoWWW[0..^1];
+
+ if (!string.IsNullOrEmpty(ruleHostNoWWW))
+ {
+ if (ruleHostNoWWW.Equals("*")) return true; // No Wildcard
+
+ if (!ruleHostNoWWW.StartsWith("*."))
+ {
+ // No Wildcard
+ if (ruleHostNoWWW.Equals(hostNoWWW)) return true;
+ }
+ else
+ {
+ // Wildcard
+ isWildcard = true;
+ if (!hostNoWWW.Equals(ruleHostNoWWW[2..]) && hostNoWWW.EndsWith(ruleHostNoWWW[1..])) return true;
+ }
+ }
+ }
+ catch (Exception) { }
+
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/ProxyServer/ApplyPrograms.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/ProxyServer/ApplyPrograms.cs
new file mode 100644
index 0000000..117ddc3
--- /dev/null
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/ProxyServer/ApplyPrograms.cs
@@ -0,0 +1,368 @@
+using System.Diagnostics;
+using System.Net;
+
+namespace MsmhToolsClass.MsmhAgnosticServer;
+
+public partial class MsmhAgnosticServer
+{
+ public async Task ApplyPrograms(IPAddress clientIP, ProxyRequest? req)
+ {
+ try
+ {
+ if (Cancel) return null;
+ if (req == null) return null;
+ if (string.IsNullOrEmpty(req.Address)) return null;
+ if (req.Address.Equals("0.0.0.0")) return null;
+ if (req.Address.StartsWith("10.")) return null;
+ req.ClientIP = clientIP;
+
+ // Event
+ string msgReqEvent = $"[{req.ClientIP}] [{req.ProxyName}] ";
+
+ if (req.ProxyName == Proxy.Name.HTTP || req.ProxyName == Proxy.Name.HTTPS)
+ msgReqEvent += $"[{req.HttpMethod}] ";
+
+ if (req.ProxyName == Proxy.Name.Socks4 || req.ProxyName == Proxy.Name.Socks4A || req.ProxyName == Proxy.Name.Socks5)
+ msgReqEvent += $"[{req.Command}] ";
+
+ // Count Max Requests
+ string checkRequest = $"{clientIP}_{req.AddressOrig}";
+ bool isKeyExist = DelinquentRequests.TryGetValue(checkRequest, out DateTime lastTime);
+ if (isKeyExist)
+ {
+ DateTime now = DateTime.UtcNow;
+ TimeSpan ts = now - lastTime;
+ if (ts <= TimeSpan.FromMilliseconds(0.4))
+ {
+ DelinquentRequests.TryUpdate(checkRequest, now, lastTime);
+
+ // Event
+ msgReqEvent += $"Delinquent Request Detected - Request Denied ({req.AddressOrig}:{req.Port})";
+ OnRequestReceived?.Invoke(msgReqEvent, EventArgs.Empty);
+ return null;
+ }
+ else DelinquentRequests.TryRemove(checkRequest, out _);
+ }
+ else DelinquentRequests.TryAdd(checkRequest, DateTime.UtcNow);
+
+ // Cache Requests
+ var cachedReq = ProxyRequestsCaches.Get(checkRequest, req);
+ if (cachedReq != null)
+ {
+ OnRequestReceived?.Invoke(cachedReq.Value.eventMsg, EventArgs.Empty);
+ return cachedReq.Value.pReq;
+ }
+
+ // Test Requests
+ bool isTestRequestExist = TestRequests.TryGetValue(req.AddressOrig, out (DateTime dt, bool applyFakeSNI, bool applyFragment) testReq);
+ if (isTestRequestExist)
+ {
+ DateTime now = DateTime.UtcNow;
+ TimeSpan ts = now - testReq.dt;
+ if (ts >= TimeSpan.FromMinutes(2))
+ {
+ TestRequests.TryRemove(req.AddressOrig, out _);
+ isTestRequestExist = false;
+ }
+ }
+
+ // Apply Programs
+ req.TimeoutSec = Settings_.ProxyTimeoutSec;
+
+ // Block Port 80
+ if (Settings_.BlockPort80 && req.Port == 80)
+ {
+ // Event
+ msgReqEvent += $"Block Port 80: {req.Address}:{req.Port}, Request Denied.";
+ OnRequestReceived?.Invoke(msgReqEvent, EventArgs.Empty);
+ return null;
+ }
+
+ //// ProxyRules Program
+ AgnosticProgram.ProxyRules.ProxyRulesResult rr = new();
+ if (ProxyRulesProgram.RulesMode != AgnosticProgram.ProxyRules.Mode.Disable)
+ {
+ rr = await ProxyRulesProgram.GetAsync(req.ClientIP.ToString(), req.Address, req.Port, Settings_);
+ }
+
+ if (rr.IsMatch)
+ {
+ // Black List
+ if (rr.IsBlackList)
+ {
+ // Event
+ msgReqEvent += $"Black List: {req.Address}:{req.Port}, Request Denied.";
+ OnRequestReceived?.Invoke(msgReqEvent, EventArgs.Empty);
+ return null;
+ }
+
+ // Block Port
+ if (rr.IsPortBlock)
+ {
+ // Event
+ msgReqEvent += $"Block Port {req.Port}: {req.Address}:{req.Port}, Request Denied.";
+ OnRequestReceived?.Invoke(msgReqEvent, EventArgs.Empty);
+ return null;
+ }
+
+ // Apply DPI Bypass If Is Match
+ req.ApplyFragment = rr.ApplyDpiBypass && IsFragmentActive;
+ req.ApplyChangeSNI = rr.ApplyDpiBypass && IsFakeSniActive;
+
+ // Fake DNS Or Custom DNS
+ bool isDnsIp = NetworkTool.IsIp(rr.Dns, out _);
+ if (isDnsIp) req.Address = rr.Dns;
+ }
+ else
+ {
+ // Apply DPI Bypass If Is Not Match
+ req.ApplyFragment = IsFragmentActive;
+ req.ApplyChangeSNI = IsFakeSniActive;
+ }
+
+ // UDP Does Not Support Fragmentation
+ if (req.ProxyName == Proxy.Name.Socks5 && req.Command == Socks.Commands.UDP)
+ req.ApplyFragment = false;
+
+ // Override For Test
+ if (req.ProxyName == Proxy.Name.Test && isTestRequestExist)
+ {
+ req.ApplyFragment = testReq.applyFragment;
+ req.ApplyChangeSNI = testReq.applyFakeSNI;
+ }
+
+ //// FakeSNI Program
+ if (req.ApplyChangeSNI)
+ {
+ if (rr.IsMatch)
+ if (!string.IsNullOrEmpty(rr.Sni) && !rr.Sni.Equals(req.AddressOrig))
+ req.AddressSNI = rr.Sni;
+
+ if (req.AddressSNI.Equals(req.AddressOrig))
+ {
+ string defaultSni = SettingsSSL_.DefaultSni;
+ if (!string.IsNullOrWhiteSpace(defaultSni))
+ {
+ req.AddressSNI = defaultSni;
+ }
+ }
+
+ if (string.IsNullOrWhiteSpace(req.AddressSNI) || req.AddressSNI.Equals(req.AddressOrig))
+ req.ApplyChangeSNI = false;
+ }
+
+ // Check If Address Is An IP
+ bool isIp = NetworkTool.IsIp(req.Address, out _);
+
+ //// Apply DNS To Proxy Request
+ if (req.AddressOrig.Equals(req.Address) && !isIp)
+ {
+ string dnsServer = Settings_.ServerUdpDnsAddress;
+
+ IPAddress ipv4Addr = IPAddress.None;
+ if (Settings_.IsIPv4SupportedByISP)
+ {
+ ipv4Addr = await GetIP.GetIpFromDnsAddressAsync(req.Address, dnsServer, false, Settings_);
+ if (ipv4Addr.Equals(IPAddress.None) && !Settings_.IsIPv6SupportedByISP) // Retry If IPv6 Is Not Supported
+ ipv4Addr = await GetIP.GetIpFromDnsAddressAsync(req.Address, dnsServer, false, Settings_);
+ }
+
+ if (ipv4Addr.Equals(IPAddress.None))
+ {
+ IPAddress ipv6Addr = await GetIP.GetIpFromDnsAddressAsync(req.Address, dnsServer, true, Settings_);
+ if (!ipv6Addr.Equals(IPAddress.IPv6None))
+ req.Address = ipv6Addr.ToString();
+ }
+ else
+ {
+ if (string.IsNullOrEmpty(Settings_.CloudflareCleanIP))
+ req.Address = ipv4Addr.ToString();
+ else
+ req.Address = CommonTools.IsCfIP(ipv4Addr) ? Settings_.CloudflareCleanIP : ipv4Addr.ToString();
+ }
+ }
+
+ // Event
+ if (req.AddressOrig.Equals(req.Address))
+ msgReqEvent += $"{req.AddressOrig}:{req.Port}";
+ else
+ {
+ if (!string.IsNullOrEmpty(rr.DnsCustomDomain) && !req.AddressOrig.Equals(rr.DnsCustomDomain))
+ msgReqEvent += $"{req.AddressOrig}:{req.Port} => {rr.DnsCustomDomain} => {req.Address}";
+ else
+ msgReqEvent += $"{req.AddressOrig}:{req.Port} => {req.Address}";
+ }
+
+ HttpStatusCode httpStatus = HttpStatusCode.RequestTimeout;
+ if (req.ApplyChangeSNI)
+ {
+ msgReqEvent += $" => {req.AddressSNI}";
+ if (req.ProxyName != Proxy.Name.Test)
+ {
+ HttpStatusCode hsc = await NetworkTool.GetHttpStatusCode($"https://{req.AddressOrig}:{req.Port}", null, 5000, false, Settings_.ServerHttpProxyAddress).ConfigureAwait(false);
+ if (hsc == HttpStatusCode.Forbidden || hsc == HttpStatusCode.MisdirectedRequest || hsc == HttpStatusCode.InternalServerError || hsc == HttpStatusCode.RequestTimeout)
+ {
+ HttpStatusCode hsc2 = await NetworkTool.GetHttpStatusCode($"https://{req.AddressOrig}:{req.Port}", null, 5000, false).ConfigureAwait(false);
+ if (hsc2 != HttpStatusCode.RequestTimeout)
+ {
+ req.ApplyChangeSNI = false;
+ req.ApplyFragment = false;
+ msgReqEvent += $" ({hsc2}) => OFF";
+ httpStatus = hsc2;
+ }
+ else if (hsc != HttpStatusCode.RequestTimeout)
+ {
+ req.ApplyChangeSNI = false;
+ msgReqEvent += $" ({hsc})";
+ TestRequests.AddOrUpdate(req.AddressOrig, (DateTime.UtcNow, req.ApplyChangeSNI, req.ApplyFragment));
+ httpStatus = hsc;
+ }
+ }
+ }
+ }
+
+ if (!req.ApplyChangeSNI && req.ApplyFragment)
+ {
+ msgReqEvent += " => Fragmented";
+ if (req.ProxyName != Proxy.Name.Test)
+ {
+ HttpStatusCode hsc = await NetworkTool.GetHttpStatusCode($"https://{req.AddressOrig}:{req.Port}", null, 3000, false, Settings_.ServerHttpProxyAddress).ConfigureAwait(false);
+ if (hsc != HttpStatusCode.OK && hsc != HttpStatusCode.NotFound && hsc != HttpStatusCode.RequestTimeout)
+ {
+ req.ApplyFragment = false;
+ msgReqEvent += $" ({hsc}) => OFF";
+ TestRequests.AddOrUpdate(req.AddressOrig, (DateTime.UtcNow, req.ApplyChangeSNI, req.ApplyFragment));
+ httpStatus = hsc;
+ }
+ }
+ }
+
+ // Check If IP Is Blocked
+ isIp = NetworkTool.IsIp(req.Address, out IPAddress? ip);
+ bool isIpv6 = false;
+ if (isIp && ip != null)
+ {
+ isIpv6 = NetworkTool.IsIPv6(ip);
+ if ((!isIpv6 && Settings_.IsIPv4SupportedByISP) || (isIpv6 && Settings_.IsIPv6SupportedByISP))
+ {
+ if (req.ProxyName != Proxy.Name.Test)
+ {
+ if (httpStatus == HttpStatusCode.RequestTimeout || httpStatus == HttpStatusCode.Forbidden)
+ {
+ httpStatus = await NetworkTool.GetHttpStatusCode($"https://{req.AddressOrig}:{req.Port}", null, 5000, false, Settings_.ServerHttpProxyAddress).ConfigureAwait(false);
+ req.IsDestBlocked = httpStatus == HttpStatusCode.RequestTimeout || httpStatus == HttpStatusCode.Forbidden;
+
+ //if (req.ProxyName == Proxy.Name.Socks5 && req.Command == Socks.Commands.UDP)
+ // req.IsDestBlocked = !await NetworkTool.CanUdpConnect(req.Address, req.Port, 5000);
+ //else
+ //{
+ // bool canPing = await NetworkTool.CanPing(req.Address, 5000);
+ // bool canTcpConnect = await NetworkTool.CanTcpConnect(req.Address, req.Port, 5000);
+ // Debug.WriteLine("===--> " + req.AddressOrig + " -> " + httpStatus + " -> " + canPing + " -> " + canTcpConnect);
+ // req.IsDestBlocked = !canPing || !canTcpConnect;
+ //}
+
+ if (req.IsDestBlocked) msgReqEvent += $" ({httpStatus})";
+ }
+ }
+ }
+ else
+ {
+ req.IsDestBlocked = true; // IP Protocol Is Not Supported
+ string ipP = isIpv6 ? "IPv6" : "IPv4";
+ msgReqEvent += $" (Your Network Does Not Support {ipP})";
+ }
+ }
+
+ // Apply Upstream?
+ if ((rr.IsMatch && rr.ApplyUpStreamProxy && !rr.ApplyUpStreamProxyToBlockedIPs) ||
+ (rr.IsMatch && rr.ApplyUpStreamProxy && rr.ApplyUpStreamProxyToBlockedIPs && req.IsDestBlocked))
+ {
+ if (!IsUpstreamEqualToServerAddress(rr.ProxyScheme))
+ {
+ req.ApplyUpStreamProxy = true;
+ req.ApplyChangeSNI = false;
+ req.ApplyFragment = false;
+ msgReqEvent += $" (Using Upstream: {rr.ProxyScheme})";
+ }
+ }
+
+ if (!req.ApplyUpStreamProxy && !string.IsNullOrWhiteSpace(Settings_.UpstreamProxyScheme))
+ {
+ if ((!Settings_.ApplyUpstreamOnlyToBlockedIps) ||
+ (Settings_.ApplyUpstreamOnlyToBlockedIps && req.IsDestBlocked))
+ {
+ if (!IsUpstreamEqualToServerAddress(Settings_.UpstreamProxyScheme))
+ {
+ req.ApplyUpStreamProxy = true;
+ req.ApplyChangeSNI = false;
+ req.ApplyFragment = false;
+ msgReqEvent += $" (Using Upstream: {Settings_.UpstreamProxyScheme.ToLower()})";
+ }
+ }
+ }
+
+ // Block Request Without DNS IP
+ bool blockReq = !req.AddressIsIp && req.AddressOrig.Equals(req.Address) && !req.ApplyUpStreamProxy;
+ if (blockReq) msgReqEvent += " Request Denied (Has No DNS IP)";
+
+ //Debug.WriteLine(msgReqEvent);
+ if (req.ProxyName != Proxy.Name.Test)
+ OnRequestReceived?.Invoke(msgReqEvent, EventArgs.Empty);
+
+ if (blockReq) return null;
+
+ // Change AddressType Based On DNS
+ if (req.ProxyName == Proxy.Name.HTTP ||
+ req.ProxyName == Proxy.Name.HTTPS ||
+ (req.ProxyName == Proxy.Name.Socks4A && req.AddressType == Socks.AddressType.Domain) ||
+ (req.ProxyName == Proxy.Name.Socks5 && req.AddressType == Socks.AddressType.Domain))
+ {
+ if (isIp && ip != null)
+ {
+ if (isIpv6) req.AddressType = Socks.AddressType.Ipv6;
+ else
+ req.AddressType = Socks.AddressType.Ipv4;
+ }
+ }
+
+ // Add To Cache
+ ProxyRequestsCaches.Add(checkRequest, req, msgReqEvent);
+
+ return req;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("AgnosticServer ApplyPrograms: " + ex.Message);
+ return null;
+ }
+ }
+
+ private bool IsUpstreamEqualToServerAddress(string? proxyScheme)
+ {
+ bool result = false;
+
+ try
+ {
+ if (!string.IsNullOrEmpty(proxyScheme))
+ {
+ NetworkTool.GetUrlDetails(proxyScheme, 443, out _, out string host, out _, out _, out int port, out _, out _);
+ if (Settings_.ListenerPort == port)
+ {
+ bool isIP = NetworkTool.IsIp(host, out IPAddress? ip);
+ if (isIP && ip != null)
+ {
+ if (IPAddress.IsLoopback(ip)) result = true;
+ }
+ else
+ {
+ if (host.ToLower().Equals("localhost")) result = true;
+ }
+ }
+ }
+ }
+ catch (Exception) { }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhProxyServer/EventArgs.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/ProxyServer/EventArgs.cs
similarity index 91%
rename from MsmhToolsClass/MsmhToolsClass/MsmhProxyServer/EventArgs.cs
rename to MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/ProxyServer/EventArgs.cs
index 378bf0b..84cd892 100644
--- a/MsmhToolsClass/MsmhToolsClass/MsmhProxyServer/EventArgs.cs
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/ProxyServer/EventArgs.cs
@@ -1,4 +1,4 @@
-namespace MsmhToolsClass.MsmhProxyServer;
+namespace MsmhToolsClass.MsmhAgnosticServer;
public class DataEventArgs : EventArgs
{
diff --git a/MsmhToolsClass/MsmhToolsClass/MsmhProxyServer/ProxyClient.cs b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/ProxyServer/ProxyClient.cs
similarity index 77%
rename from MsmhToolsClass/MsmhToolsClass/MsmhProxyServer/ProxyClient.cs
rename to MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/ProxyServer/ProxyClient.cs
index abb7e9f..6c2c743 100644
--- a/MsmhToolsClass/MsmhToolsClass/MsmhProxyServer/ProxyClient.cs
+++ b/MsmhToolsClass/MsmhToolsClass/MsmhAgnosticServer/ProxyServer/ProxyClient.cs
@@ -1,11 +1,10 @@
using System.Diagnostics;
using System.Net.Sockets;
-namespace MsmhToolsClass.MsmhProxyServer;
+namespace MsmhToolsClass.MsmhAgnosticServer;
public class ProxyClient
{
- private byte[] Buffer_ { get; set; }
private bool Disposed_ { get; set; } = false;
public Socket Socket_ { get; set; }
@@ -16,9 +15,7 @@ public ProxyClient(Socket socket)
{
// Start Data Exchange.
Socket_ = socket;
- int packetSize = MsmhProxyServer.MaxDataSize;
- Buffer_ = new byte[packetSize];
- Socket_.ReceiveBufferSize = packetSize;
+ Socket_.ReceiveBufferSize = MsmhAgnosticServer.MaxDataSize;
}
public async Task StartReceiveAsync()
@@ -27,8 +24,9 @@ public async Task StartReceiveAsync()
{
if (Disposed_ || Socket_ is null) return;
+ byte[] buffer = new byte[MsmhAgnosticServer.MaxDataSize];
int received = 0;
- try { received = await Socket_.ReceiveAsync(Buffer_, SocketFlags.None); } catch (Exception) { /* HSTS / Timeout / Done */ }
+ try { received = await Socket_.ReceiveAsync(buffer, SocketFlags.None).ConfigureAwait(false); } catch (Exception) { /* HSTS / Timeout / Done */ }
if (received <= 0)
{
@@ -36,8 +34,7 @@ public async Task StartReceiveAsync()
return;
}
- byte[] buffer = new byte[received];
- Buffer.BlockCopy(Buffer_, 0, buffer, 0, received);
+ buffer = buffer[..received];
DataEventArgs data = new(this, buffer);
OnDataReceived?.Invoke(this, data);
@@ -49,11 +46,11 @@ public async Task StartReceiveAsync()
}
}
- public async Task ReceiveAsync(byte[] data)
+ public async Task