UTF-8
This commit is contained in:
parent
1af6a6f4a6
commit
5168b118b1
30
DnsClientForm.Designer.cs
generated
30
DnsClientForm.Designer.cs
generated
@ -49,6 +49,8 @@
|
||||
//
|
||||
// Domain_Text
|
||||
//
|
||||
this.Domain_Text.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.Domain_Text.Location = new System.Drawing.Point(53, 6);
|
||||
this.Domain_Text.Name = "Domain_Text";
|
||||
this.Domain_Text.Size = new System.Drawing.Size(499, 21);
|
||||
@ -65,15 +67,19 @@
|
||||
//
|
||||
// Select_Text
|
||||
//
|
||||
this.Select_Text.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.Select_Text.Location = new System.Drawing.Point(53, 41);
|
||||
this.Select_Text.Name = "Select_Text";
|
||||
this.Select_Text.Size = new System.Drawing.Size(499, 228);
|
||||
this.Select_Text.Size = new System.Drawing.Size(499, 284);
|
||||
this.Select_Text.TabIndex = 3;
|
||||
this.Select_Text.Text = "";
|
||||
//
|
||||
// Start_Button
|
||||
//
|
||||
this.Start_Button.Location = new System.Drawing.Point(12, 386);
|
||||
this.Start_Button.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.Start_Button.Location = new System.Drawing.Point(12, 449);
|
||||
this.Start_Button.Name = "Start_Button";
|
||||
this.Start_Button.Size = new System.Drawing.Size(75, 23);
|
||||
this.Start_Button.TabIndex = 4;
|
||||
@ -83,7 +89,8 @@
|
||||
//
|
||||
// Exit_Button
|
||||
//
|
||||
this.Exit_Button.Location = new System.Drawing.Point(477, 386);
|
||||
this.Exit_Button.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.Exit_Button.Location = new System.Drawing.Point(477, 449);
|
||||
this.Exit_Button.Name = "Exit_Button";
|
||||
this.Exit_Button.Size = new System.Drawing.Size(75, 23);
|
||||
this.Exit_Button.TabIndex = 5;
|
||||
@ -93,26 +100,29 @@
|
||||
//
|
||||
// Result_Text
|
||||
//
|
||||
this.Result_Text.Location = new System.Drawing.Point(53, 275);
|
||||
this.Result_Text.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.Result_Text.Location = new System.Drawing.Point(53, 331);
|
||||
this.Result_Text.Name = "Result_Text";
|
||||
this.Result_Text.Size = new System.Drawing.Size(499, 105);
|
||||
this.Result_Text.Size = new System.Drawing.Size(499, 112);
|
||||
this.Result_Text.TabIndex = 6;
|
||||
this.Result_Text.Text = "";
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(12, 275);
|
||||
this.label1.Location = new System.Drawing.Point(12, 334);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(35, 12);
|
||||
this.label1.TabIndex = 7;
|
||||
this.label1.Text = "结果:";
|
||||
//
|
||||
// DnsClient
|
||||
// DnsClientForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(564, 421);
|
||||
this.ClientSize = new System.Drawing.Size(564, 484);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.Result_Text);
|
||||
this.Controls.Add(this.Exit_Button);
|
||||
@ -121,8 +131,8 @@
|
||||
this.Controls.Add(this.Select_Txt);
|
||||
this.Controls.Add(this.Domain_Text);
|
||||
this.Controls.Add(this.Domain_Txt);
|
||||
this.Name = "DnsClient";
|
||||
this.Text = "Form1";
|
||||
this.Name = "DnsClientForm";
|
||||
this.Text = "DnsClient";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
|
225
DnsClientForm.cs
225
DnsClientForm.cs
@ -12,6 +12,8 @@ using System.Text.Json;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using DnsClientX;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace DnsClient
|
||||
{
|
||||
@ -105,25 +107,16 @@ namespace DnsClient
|
||||
foreach (var answer in answers.EnumerateArray())
|
||||
{
|
||||
// For TXT records, the data might be in quotes, so we'll just display it as-is
|
||||
string txtData = answer.GetProperty("data").GetString();
|
||||
string txtAnswer = answer.GetProperty("data").GetString();
|
||||
string txtData = NormalizeResult(txtAnswer);
|
||||
string result = $"{server} => {txtData} (耗时: {elapsedMs}ms)";
|
||||
results.Add(result);
|
||||
|
||||
if (elapsedMs < fastestDohResult.time)
|
||||
{
|
||||
fastestDohResult = (server, elapsedMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string result = $"{server} => 无 TXT 记录 (耗时: {elapsedMs}ms)";
|
||||
results.Add(result);
|
||||
|
||||
if (fastestDohResult.result == null || elapsedMs < fastestDohResult.time)
|
||||
{
|
||||
fastestDohResult = (result, elapsedMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -143,73 +136,70 @@ namespace DnsClient
|
||||
private async Task QueryDotAsync(NameServer server, string domain, ConcurrentBag<string> results, CancellationToken token)
|
||||
{
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
TcpClient tcpClient = null;
|
||||
try
|
||||
{
|
||||
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(token))
|
||||
{
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
|
||||
using (var tcpClient = new TcpClient())
|
||||
{
|
||||
var connectTask = tcpClient.ConnectAsync(server.Address.ToString(), server.Port);
|
||||
var timeoutTask = Task.Delay(5000, cts.Token);
|
||||
tcpClient = new TcpClient();
|
||||
var connectTask = tcpClient.ConnectAsync(server.Address.ToString(), server.Port);
|
||||
var timeoutTask = Task.Delay(5000, cts.Token);
|
||||
|
||||
if (await Task.WhenAny(connectTask, timeoutTask) == timeoutTask)
|
||||
if (await Task.WhenAny(connectTask, timeoutTask) == timeoutTask)
|
||||
{
|
||||
throw new TimeoutException("连接超时");
|
||||
}
|
||||
|
||||
// 确保连接成功完成
|
||||
await connectTask;
|
||||
|
||||
using (var sslStream = new SslStream(tcpClient.GetStream(), false,
|
||||
(sender, certificate, chain, sslPolicyErrors) => true))
|
||||
{
|
||||
var authTask = sslStream.AuthenticateAsClientAsync(server.Address.ToString());
|
||||
if (await Task.WhenAny(authTask, timeoutTask) == timeoutTask)
|
||||
{
|
||||
throw new TimeoutException("连接超时");
|
||||
throw new TimeoutException("SSL握手超时");
|
||||
}
|
||||
|
||||
using (var sslStream = new SslStream(tcpClient.GetStream(), false,
|
||||
(sender, certificate, chain, sslPolicyErrors) => true))
|
||||
var request = CreateDnsQuery(domain);
|
||||
|
||||
var writeTask = sslStream.WriteAsync(request, 0, request.Length, cts.Token);
|
||||
if (await Task.WhenAny(writeTask, timeoutTask) == timeoutTask)
|
||||
{
|
||||
var authTask = sslStream.AuthenticateAsClientAsync(server.Address.ToString());
|
||||
if (await Task.WhenAny(authTask, timeoutTask) == timeoutTask)
|
||||
throw new TimeoutException("写入请求超时");
|
||||
}
|
||||
|
||||
var response = new byte[512];
|
||||
|
||||
var readTask = sslStream.ReadAsync(response, 0, response.Length, cts.Token);
|
||||
if (await Task.WhenAny(readTask, timeoutTask) == timeoutTask)
|
||||
{
|
||||
throw new TimeoutException("读取响应超时");
|
||||
}
|
||||
|
||||
int bytesRead = await readTask;
|
||||
stopwatch.Stop();
|
||||
var elapsedMs = (int)stopwatch.ElapsedMilliseconds;
|
||||
|
||||
var txtRecords = ParseDnsResponse(response, bytesRead);
|
||||
string serverInfo = $"{server.Address}:{server.Port}";
|
||||
|
||||
if (txtRecords.Any())
|
||||
{
|
||||
foreach (var txt in txtRecords)
|
||||
{
|
||||
throw new TimeoutException("SSL握手超时");
|
||||
}
|
||||
|
||||
var request = CreateDnsQuery(domain);
|
||||
|
||||
var writeTask = sslStream.WriteAsync(request, 0, request.Length, cts.Token);
|
||||
if (await Task.WhenAny(writeTask, timeoutTask) == timeoutTask)
|
||||
{
|
||||
throw new TimeoutException("写入请求超时");
|
||||
}
|
||||
|
||||
var response = new byte[512];
|
||||
|
||||
var readTask = sslStream.ReadAsync(response, 0, response.Length, cts.Token);
|
||||
if (await Task.WhenAny(readTask, timeoutTask) == timeoutTask)
|
||||
{
|
||||
throw new TimeoutException("读取响应超时");
|
||||
}
|
||||
|
||||
int bytesRead = await readTask;
|
||||
stopwatch.Stop();
|
||||
var elapsedMs = (int)stopwatch.ElapsedMilliseconds;
|
||||
|
||||
var txtRecords = ParseDnsResponse(response, bytesRead);
|
||||
string serverInfo = $"{server.Address}:{server.Port}";
|
||||
|
||||
if (txtRecords.Any())
|
||||
{
|
||||
foreach (var txt in txtRecords)
|
||||
{
|
||||
string result = $"{serverInfo} => {txt} (耗时: {elapsedMs}ms)";
|
||||
results.Add(result);
|
||||
|
||||
if (elapsedMs < fastestDotResult.time)
|
||||
{
|
||||
fastestDotResult = (serverInfo, elapsedMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string result = $"{serverInfo} => 无 TXT 记录 (耗时: {elapsedMs}ms)";
|
||||
string result = $"{serverInfo} => {txt} (耗时: {elapsedMs}ms)";
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string result = $"{serverInfo} => 无 TXT 记录 (耗时: {elapsedMs}ms)";
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,9 +208,22 @@ namespace DnsClient
|
||||
stopwatch.Stop();
|
||||
var elapsedMs = (int)stopwatch.ElapsedMilliseconds;
|
||||
string serverInfo = $"{server.Address}:{server.Port}";
|
||||
string result = $"{serverInfo} => 错误: {ex.Message} (耗时: {elapsedMs}ms)";
|
||||
string result = $"{serverInfo} => 错误: {GetErrorMessage(ex)} (耗时: {elapsedMs}ms)";
|
||||
results.Add(result);
|
||||
}
|
||||
finally
|
||||
{
|
||||
tcpClient?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private string GetErrorMessage(Exception ex)
|
||||
{
|
||||
if (ex is AggregateException aggEx)
|
||||
{
|
||||
return string.Join("; ", aggEx.InnerExceptions.Select(iex => iex.Message));
|
||||
}
|
||||
return ex.Message;
|
||||
}
|
||||
|
||||
private byte[] CreateDnsQuery(string domain)
|
||||
@ -324,45 +327,53 @@ namespace DnsClient
|
||||
offset += 2;
|
||||
|
||||
if (type == 16) // TXT record
|
||||
{
|
||||
int end = offset + dataLength;
|
||||
var txtBuilder = new List<string>();
|
||||
{
|
||||
int end = offset + dataLength;
|
||||
var txtBuilder = new List<string>();
|
||||
|
||||
while (offset < end)
|
||||
{
|
||||
int txtLen = response[offset++];
|
||||
if (offset + txtLen > end) break;
|
||||
while (offset < end)
|
||||
{
|
||||
int txtLen = response[offset++];
|
||||
if (offset + txtLen > end) break;
|
||||
|
||||
var txt = System.Text.Encoding.UTF8.GetString(response, offset, txtLen);
|
||||
txtBuilder.Add(txt);
|
||||
offset += txtLen;
|
||||
}
|
||||
var txt = System.Text.Encoding.UTF8.GetString(response, offset, txtLen);
|
||||
txtBuilder.Add(txt);
|
||||
offset += txtLen;
|
||||
}
|
||||
|
||||
txtRecords.Add(string.Join("", txtBuilder));
|
||||
}
|
||||
else
|
||||
{
|
||||
offset += dataLength;
|
||||
}
|
||||
txtRecords.Add(string.Join("", txtBuilder));
|
||||
}
|
||||
else
|
||||
{
|
||||
offset += dataLength;
|
||||
}
|
||||
}
|
||||
|
||||
return txtRecords;
|
||||
}
|
||||
|
||||
private string GetPreferredResult()
|
||||
private async Task<string> GetPreferredResult()
|
||||
{
|
||||
// 只返回成功的解析结果
|
||||
string preferredDoh = fastestDohResult.time != int.MaxValue ?
|
||||
fastestDohResult.result : "无可用DoH结果";
|
||||
var dnsClientTool = new DnsClientTool();
|
||||
string test;
|
||||
try
|
||||
{
|
||||
// 获取 test 结果
|
||||
test = await dnsClientTool.GetNotices("notice.suwin.cc");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
test = $"查询失败: {ex.Message}";
|
||||
}
|
||||
|
||||
string preferredDot = fastestDotResult.time != int.MaxValue ?
|
||||
fastestDotResult.result : "无可用DoT结果";
|
||||
var result = new StringBuilder();
|
||||
result.AppendLine("=== 优选结果 ===");
|
||||
result.AppendLine($"{test}");
|
||||
|
||||
return $"=== 优选结果 ===\n" +
|
||||
$"DoH: {preferredDoh}\n" +
|
||||
$"DoT: {preferredDot}";
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
|
||||
// 辅助类型定义
|
||||
private enum ResultType { Success, NoRecord, Error }
|
||||
|
||||
@ -403,7 +414,7 @@ else
|
||||
Select_Text.AppendText(result + "\n");
|
||||
}
|
||||
|
||||
Result_Text.AppendText(GetPreferredResult());
|
||||
Result_Text.AppendText(await GetPreferredResult());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -416,5 +427,37 @@ else
|
||||
this.Close();
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
public static string DecodeByteValuesToUtf8(string input)
|
||||
{
|
||||
string pattern = @"\\(\d{1,3})";
|
||||
|
||||
string decodedString = Regex.Replace(input, pattern, match =>
|
||||
{
|
||||
byte byteValue = byte.Parse(match.Groups[1].Value);
|
||||
return ((char)byteValue).ToString();
|
||||
});
|
||||
|
||||
byte[] byteArray = new byte[decodedString.Length];
|
||||
for (int i = 0; i < decodedString.Length; i++)
|
||||
{
|
||||
byteArray[i] = (byte)decodedString[i];
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(byteArray);
|
||||
}
|
||||
|
||||
public static string NormalizeResult(string input)
|
||||
{
|
||||
if (!Regex.IsMatch(input, @"\\\d{1,3}"))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
string decodedBytes = DecodeByteValuesToUtf8(input);
|
||||
|
||||
return decodedBytes.Trim();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -136,6 +136,7 @@
|
||||
<Compile Include="DnsClientForm.Designer.cs">
|
||||
<DependentUpon>DnsClientForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="DnsClientTool.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="DnsClientForm.resx">
|
||||
|
263
DnsClientTool.cs
Normal file
263
DnsClientTool.cs
Normal file
@ -0,0 +1,263 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace DnsClient
|
||||
{
|
||||
class DnsClientTool
|
||||
{
|
||||
public async Task<string> GetNotices(string domain)
|
||||
{
|
||||
var dohServers = new List<string>
|
||||
{
|
||||
"https://cloudflare-dns.com/dns-query",
|
||||
"https://dns.cloudflare.com/dns-query",
|
||||
"https://1.1.1.1/dns-query",
|
||||
"https://1.0.0.1/dns-query",
|
||||
"https://dns.google/resolve",
|
||||
"https://sm2.doh.pub/dns-query",
|
||||
"https://doh.pub/dns-query",
|
||||
"https://dns.alidns.com/resolve",
|
||||
"https://223.5.5.5/resolve",
|
||||
"https://223.6.6.6/resolve",
|
||||
"https://doh.360.cn/resolve"
|
||||
};
|
||||
|
||||
var dotServers = new List<NameServer>
|
||||
{
|
||||
new NameServer(IPAddress.Parse("101.226.4.6"), 853),
|
||||
new NameServer(IPAddress.Parse("223.5.5.5"), 853),
|
||||
new NameServer(IPAddress.Parse("223.6.6.6"), 853),
|
||||
new NameServer(IPAddress.Parse("1.12.12.12"), 853),
|
||||
new NameServer(IPAddress.Parse("1.1.1.1"), 853),
|
||||
new NameServer(IPAddress.Parse("1.0.0.1"), 853),
|
||||
new NameServer(IPAddress.Parse("8.8.8.8"), 853),
|
||||
new NameServer(IPAddress.Parse("8.8.4.4"), 853),
|
||||
new NameServer(IPAddress.Parse("9.9.9.9"), 853),
|
||||
new NameServer(IPAddress.Parse("149.112.112.112"), 853)
|
||||
};
|
||||
|
||||
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
|
||||
var results = new List<string>();
|
||||
|
||||
var dohTasks = dohServers.Select(server =>
|
||||
QueryDohAsync(server, domain, cts.Token));
|
||||
var dotTasks = dotServers.Select(server =>
|
||||
QueryDotAsync(server, domain, cts.Token));
|
||||
var dohResults = await Task.WhenAll(dohTasks);
|
||||
var dotResults = await Task.WhenAll(dotTasks);
|
||||
|
||||
foreach (var list in dohResults.Concat(dotResults))
|
||||
{
|
||||
results.AddRange(list);
|
||||
}
|
||||
var recordCounts = results.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
|
||||
var mostFrequentRecord = recordCounts.OrderByDescending(x => x.Value).FirstOrDefault();
|
||||
return mostFrequentRecord.Key ?? "没有 TXT 记录";
|
||||
}
|
||||
|
||||
private async Task<List<string>> QueryDohAsync(string server, string domain, CancellationToken token)
|
||||
{
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
List<string> results = new List<string>();
|
||||
try
|
||||
{
|
||||
var httpClient = new HttpClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(5);
|
||||
httpClient.DefaultRequestHeaders.Add("Accept", "application/dns-json");
|
||||
var response = await httpClient.GetAsync($"{server}?name={domain}&type=16", token);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var responseString = await response.Content.ReadAsStringAsync();
|
||||
var json = JsonDocument.Parse(responseString);
|
||||
stopwatch.Stop();
|
||||
var elapsedMs = (int)stopwatch.ElapsedMilliseconds;
|
||||
|
||||
if (json.RootElement.TryGetProperty("Answer", out var answers))
|
||||
{
|
||||
foreach (var answer in answers.EnumerateArray())
|
||||
{
|
||||
string txtData = NormalizeResult(answer.GetProperty("data").GetString());
|
||||
results.Add(txtData);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
stopwatch.Stop();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private async Task<List<string>> QueryDotAsync(NameServer server, string domain, CancellationToken token)
|
||||
{
|
||||
var results = new List<string>();
|
||||
TcpClient tcpClient = null;
|
||||
SslStream sslStream = null;
|
||||
|
||||
try
|
||||
{
|
||||
tcpClient = new TcpClient();
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(token);
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
|
||||
await ConnectWithTimeoutAsync(tcpClient, server.Address.ToString(), server.Port, cts.Token);
|
||||
|
||||
sslStream = new SslStream(tcpClient.GetStream(), false, (sender, certificate, chain, errors) => true);
|
||||
await sslStream.AuthenticateAsClientAsync(server.Address.ToString());
|
||||
|
||||
byte[] query = BuildDnsQuery(domain);
|
||||
byte[] lengthPrefixed = new byte[query.Length + 2];
|
||||
lengthPrefixed[0] = (byte)(query.Length >> 8);
|
||||
lengthPrefixed[1] = (byte)(query.Length & 0xFF);
|
||||
Buffer.BlockCopy(query, 0, lengthPrefixed, 2, query.Length);
|
||||
|
||||
await sslStream.WriteAsync(lengthPrefixed, 0, lengthPrefixed.Length, cts.Token);
|
||||
|
||||
byte[] lengthBytes = new byte[2];
|
||||
await sslStream.ReadAsync(lengthBytes, 0, 2, cts.Token);
|
||||
int responseLength = (lengthBytes[0] << 8) | lengthBytes[1];
|
||||
|
||||
byte[] response = new byte[responseLength];
|
||||
int totalRead = 0;
|
||||
while (totalRead < responseLength)
|
||||
{
|
||||
int read = await sslStream.ReadAsync(response, totalRead, responseLength - totalRead, cts.Token);
|
||||
if (read == 0) break;
|
||||
totalRead += read;
|
||||
}
|
||||
|
||||
results.AddRange(ParseTxtRecordsFromResponse(response));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error occurred while querying DoT server: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
sslStream?.Dispose();
|
||||
tcpClient?.Close();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private byte[] BuildDnsQuery(string domain)
|
||||
{
|
||||
var rnd = new Random();
|
||||
var id = (ushort)rnd.Next(0, ushort.MaxValue);
|
||||
|
||||
var message = new List<byte>();
|
||||
message.AddRange(BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)id))); // Transaction ID
|
||||
message.AddRange(new byte[] { 0x01, 0x00 });
|
||||
message.AddRange(new byte[] { 0x00, 0x01 });
|
||||
message.AddRange(new byte[] { 0x00, 0x00 });
|
||||
message.AddRange(new byte[] { 0x00, 0x00 });
|
||||
message.AddRange(new byte[] { 0x00, 0x00 });
|
||||
|
||||
foreach (var label in domain.Split('.'))
|
||||
{
|
||||
message.Add((byte)label.Length);
|
||||
message.AddRange(Encoding.ASCII.GetBytes(label));
|
||||
}
|
||||
message.Add(0);
|
||||
|
||||
message.AddRange(new byte[] { 0x00, 0x10 });
|
||||
message.AddRange(new byte[] { 0x00, 0x01 });
|
||||
|
||||
return message.ToArray();
|
||||
}
|
||||
|
||||
private List<string> ParseTxtRecordsFromResponse(byte[] response)
|
||||
{
|
||||
var results = new List<string>();
|
||||
|
||||
int answerCount = (response[6] << 8) | response[7];
|
||||
|
||||
int position = 12;
|
||||
while (response[position] != 0)
|
||||
{
|
||||
position += response[position] + 1;
|
||||
}
|
||||
position += 5;
|
||||
|
||||
for (int i = 0; i < answerCount; i++)
|
||||
{
|
||||
position += 2;
|
||||
ushort type = (ushort)((response[position] << 8) | response[position + 1]);
|
||||
position += 8;
|
||||
ushort rdLength = (ushort)((response[position] << 8) | response[position + 1]);
|
||||
position += 2;
|
||||
if (type == 16)
|
||||
{
|
||||
int txtLength = response[position];
|
||||
var txt = NormalizeResult(Encoding.UTF8.GetString(response, position + 1, txtLength));
|
||||
results.Add(txt);
|
||||
}
|
||||
position += rdLength;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private async Task ConnectWithTimeoutAsync(TcpClient client, string address, int port, CancellationToken token)
|
||||
{
|
||||
var connectTask = client.ConnectAsync(address, port);
|
||||
var timeoutTask = Task.Delay(Timeout.InfiniteTimeSpan, token);
|
||||
|
||||
var completedTask = await Task.WhenAny(connectTask, timeoutTask);
|
||||
if (completedTask == timeoutTask)
|
||||
{
|
||||
client?.Close();
|
||||
throw new TimeoutException("TCP 连接超时或已取消。");
|
||||
}
|
||||
|
||||
await connectTask;
|
||||
}
|
||||
|
||||
public static string DecodeByteValuesToUtf8(string input)
|
||||
{
|
||||
string pattern = @"\\(\d{1,3})";
|
||||
|
||||
string decodedString = Regex.Replace(input, pattern, match =>
|
||||
{
|
||||
byte byteValue = byte.Parse(match.Groups[1].Value);
|
||||
return ((char)byteValue).ToString();
|
||||
});
|
||||
|
||||
byte[] byteArray = new byte[decodedString.Length];
|
||||
for (int i = 0; i < decodedString.Length; i++)
|
||||
{
|
||||
byteArray[i] = (byte)decodedString[i];
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(byteArray);
|
||||
}
|
||||
|
||||
public static string NormalizeResult(string input)
|
||||
{
|
||||
if (!Regex.IsMatch(input, @"\\\d{1,3}"))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
string decodedBytes = DecodeByteValuesToUtf8(input);
|
||||
|
||||
return decodedBytes.Trim();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user