dnscrypt-proxy使用随机端口;
支持多个回退DNS上游;
This commit is contained in:
parent
5f425e79ba
commit
bec32d2e35
@ -19,15 +19,9 @@ namespace FastGithub.Configuration
|
||||
private ConcurrentDictionary<string, DomainConfig?> domainConfigCache;
|
||||
|
||||
/// <summary>
|
||||
/// 未污染的dns
|
||||
/// </summary>
|
||||
public IPEndPoint PureDns { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 速度快的dns
|
||||
/// 回退的dns
|
||||
/// </summary>
|
||||
public IPEndPoint FastDns { get; private set; }
|
||||
|
||||
public IPEndPoint[] FallbackDns { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// FastGithub配置
|
||||
@ -41,8 +35,7 @@ namespace FastGithub.Configuration
|
||||
this.logger = logger;
|
||||
var opt = options.CurrentValue;
|
||||
|
||||
this.PureDns = opt.PureDns.ToIPEndPoint();
|
||||
this.FastDns = opt.FastDns.ToIPEndPoint();
|
||||
this.FallbackDns = opt.FallbackDns.Select(item => item.ToIPEndPoint()).ToArray();
|
||||
this.domainConfigs = ConvertDomainConfigs(opt.DomainConfigs);
|
||||
this.domainConfigCache = new ConcurrentDictionary<string, DomainConfig?>();
|
||||
|
||||
@ -57,8 +50,7 @@ namespace FastGithub.Configuration
|
||||
{
|
||||
try
|
||||
{
|
||||
this.PureDns = options.PureDns.ToIPEndPoint();
|
||||
this.FastDns = options.FastDns.ToIPEndPoint();
|
||||
this.FallbackDns = options.FallbackDns.Select(item => item.ToIPEndPoint()).ToArray();
|
||||
this.domainConfigs = ConvertDomainConfigs(options.DomainConfigs);
|
||||
this.domainConfigCache = new ConcurrentDictionary<string, DomainConfig?>();
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FastGithub.Configuration
|
||||
{
|
||||
@ -13,14 +14,9 @@ namespace FastGithub.Configuration
|
||||
public ListenConfig Listen { get; set; } = new ListenConfig();
|
||||
|
||||
/// <summary>
|
||||
/// 未污染的dns
|
||||
/// 回退的dns
|
||||
/// </summary>
|
||||
public DnsConfig PureDns { get; set; } = new DnsConfig { IPAddress = "127.0.0.1", Port = 5533 };
|
||||
|
||||
/// <summary>
|
||||
/// 速度快的dns
|
||||
/// </summary>
|
||||
public DnsConfig FastDns { get; set; } = new DnsConfig { IPAddress = "114.114.114.114", Port = 53 };
|
||||
public DnsConfig[] FallbackDns { get; set; } = Array.Empty<DnsConfig>();
|
||||
|
||||
/// <summary>
|
||||
/// 代理的域名配置
|
||||
|
||||
@ -79,6 +79,45 @@ namespace FastGithub.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取可用的随机端口
|
||||
/// </summary>
|
||||
/// <param name="addressFamily"></param>
|
||||
/// <param name="min">最小值</param>
|
||||
/// <returns></returns>
|
||||
public static int GetAvailablePort(AddressFamily addressFamily, int min = 1024)
|
||||
{
|
||||
var hashSet = new HashSet<int>();
|
||||
var tcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
|
||||
var udpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners();
|
||||
|
||||
foreach (var item in tcpListeners)
|
||||
{
|
||||
if (item.AddressFamily == addressFamily)
|
||||
{
|
||||
hashSet.Add(item.Port);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in udpListeners)
|
||||
{
|
||||
if (item.AddressFamily == addressFamily)
|
||||
{
|
||||
hashSet.Add(item.Port);
|
||||
}
|
||||
}
|
||||
|
||||
for (var port = min; port < ushort.MaxValue; port++)
|
||||
{
|
||||
if (hashSet.Contains(port) == false)
|
||||
{
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
throw new FastGithubException("当前无可用的端口");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以监听指定tcp端口
|
||||
/// </summary>
|
||||
|
||||
@ -100,7 +100,7 @@ namespace FastGithub.Dns
|
||||
/// <returns></returns>
|
||||
protected override Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
return this.dnsOverUdpServer.ListenAsync(stoppingToken);
|
||||
return this.dnsOverUdpServer.HandleAsync(stoppingToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -60,11 +60,11 @@ namespace FastGithub.Dns
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 监听dns请求
|
||||
/// 监听和处理dns请求
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task ListenAsync(CancellationToken cancellationToken)
|
||||
public async Task HandleAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
while (cancellationToken.IsCancellationRequested == false)
|
||||
@ -100,7 +100,7 @@ namespace FastGithub.Dns
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogTrace($"处理DNS异常:{ex.Message}");
|
||||
this.logger.LogWarning($"处理DNS异常:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
using DNS.Protocol;
|
||||
using DNS.Protocol.ResourceRecords;
|
||||
using FastGithub.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@ -58,8 +57,20 @@ namespace FastGithub.Dns
|
||||
return response;
|
||||
}
|
||||
|
||||
var fastResolver = new UdpRequestResolver(fastGithubConfig.FastDns);
|
||||
return await fastResolver.Resolve(request, cancellationToken);
|
||||
// 使用回退dns解析域名
|
||||
foreach (var dns in this.fastGithubConfig.FallbackDns)
|
||||
{
|
||||
try
|
||||
{
|
||||
var resolver = new UdpRequestResolver(dns);
|
||||
return await resolver.Resolve(request, cancellationToken);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
throw new FastGithubException($"无法解析域名{domain}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using FastGithub.Configuration;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -29,10 +30,10 @@ namespace FastGithub.DomainResolve
|
||||
/// <summary>
|
||||
/// DnscryptProxy服务
|
||||
/// </summary>
|
||||
/// <param name="endPoint">监听的节点</param>
|
||||
public DnscryptProxy(IPEndPoint endPoint)
|
||||
public DnscryptProxy()
|
||||
{
|
||||
this.EndPoint = endPoint;
|
||||
var port = LocalMachine.GetAvailablePort(IPAddress.Loopback.AddressFamily, min: 5353);
|
||||
this.EndPoint = new IPEndPoint(IPAddress.Loopback, port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using FastGithub.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Threading;
|
||||
@ -12,20 +11,19 @@ namespace FastGithub.DomainResolve
|
||||
/// </summary>
|
||||
sealed class DnscryptProxyHostedService : IHostedService
|
||||
{
|
||||
private readonly FastGithubConfig fastGithubConfig;
|
||||
private readonly ILogger<DnscryptProxyHostedService> logger;
|
||||
private DnscryptProxy? dnscryptProxy;
|
||||
private readonly DnscryptProxy dnscryptProxy;
|
||||
|
||||
/// <summary>
|
||||
/// DnscryptProxy后台服务
|
||||
/// </summary>
|
||||
/// <param name="fastGithubConfig"></param>
|
||||
/// <param name="dnscryptProxy"></param>
|
||||
/// <param name="logger"></param>
|
||||
public DnscryptProxyHostedService(
|
||||
FastGithubConfig fastGithubConfig,
|
||||
DnscryptProxy dnscryptProxy,
|
||||
ILogger<DnscryptProxyHostedService> logger)
|
||||
{
|
||||
this.fastGithubConfig = fastGithubConfig;
|
||||
this.dnscryptProxy = dnscryptProxy;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@ -36,19 +34,14 @@ namespace FastGithub.DomainResolve
|
||||
/// <returns></returns>
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var pureDns = this.fastGithubConfig.PureDns;
|
||||
if (LocalMachine.ContainsIPAddress(pureDns.Address) == true)
|
||||
try
|
||||
{
|
||||
this.dnscryptProxy = new DnscryptProxy(pureDns);
|
||||
try
|
||||
{
|
||||
await this.dnscryptProxy.StartAsync(cancellationToken);
|
||||
this.logger.LogInformation($"{this.dnscryptProxy}启动成功");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogWarning($"{this.dnscryptProxy}启动失败:{ex.Message}");
|
||||
}
|
||||
await this.dnscryptProxy.StartAsync(cancellationToken);
|
||||
this.logger.LogInformation($"{this.dnscryptProxy}启动成功");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogWarning($"{this.dnscryptProxy}启动失败:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,19 +52,15 @@ namespace FastGithub.DomainResolve
|
||||
/// <returns></returns>
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (this.dnscryptProxy != null)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
this.dnscryptProxy.Stop();
|
||||
this.logger.LogInformation($"{this.dnscryptProxy}已停止");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogWarning($"{this.dnscryptProxy}停止失败:{ex.Message}");
|
||||
}
|
||||
this.dnscryptProxy.Stop();
|
||||
this.logger.LogInformation($"{this.dnscryptProxy}已停止");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogWarning($"{this.dnscryptProxy}停止失败:{ex.Message}");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,13 +21,14 @@ namespace FastGithub.DomainResolve
|
||||
{
|
||||
private readonly IMemoryCache memoryCache;
|
||||
private readonly FastGithubConfig fastGithubConfig;
|
||||
private readonly DnscryptProxy dnscryptProxy;
|
||||
private readonly ILogger<DomainResolver> logger;
|
||||
|
||||
private readonly TimeSpan lookupTimeout = TimeSpan.FromSeconds(2d);
|
||||
private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(2d);
|
||||
private readonly TimeSpan pureResolveCacheTimeSpan = TimeSpan.FromMinutes(5d);
|
||||
private readonly TimeSpan fastResolveCacheTimeSpan = TimeSpan.FromMinutes(1d);
|
||||
private readonly TimeSpan loopbackResolveCacheTimeSpan = TimeSpan.FromSeconds(5d);
|
||||
private readonly TimeSpan dnscryptExpiration = TimeSpan.FromMinutes(5d);
|
||||
private readonly TimeSpan fallbackExpiration = TimeSpan.FromMinutes(1d);
|
||||
private readonly TimeSpan loopbackExpiration = TimeSpan.FromSeconds(5d);
|
||||
private readonly ConcurrentDictionary<DnsEndPoint, SemaphoreSlim> semaphoreSlims = new();
|
||||
|
||||
/// <summary>
|
||||
@ -35,14 +36,17 @@ namespace FastGithub.DomainResolve
|
||||
/// </summary>
|
||||
/// <param name="memoryCache"></param>
|
||||
/// <param name="fastGithubConfig"></param>
|
||||
/// <param name="dnscryptProxy"></param>
|
||||
/// <param name="logger"></param>
|
||||
public DomainResolver(
|
||||
IMemoryCache memoryCache,
|
||||
FastGithubConfig fastGithubConfig,
|
||||
DnscryptProxy dnscryptProxy,
|
||||
ILogger<DomainResolver> logger)
|
||||
{
|
||||
this.memoryCache = memoryCache;
|
||||
this.fastGithubConfig = fastGithubConfig;
|
||||
this.dnscryptProxy = dnscryptProxy;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@ -69,65 +73,86 @@ namespace FastGithub.DomainResolve
|
||||
/// <summary>
|
||||
/// 查找ip
|
||||
/// </summary>
|
||||
/// <param name="endPoint"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<IPAddress> LookupAsync(DnsEndPoint endPoint, CancellationToken cancellationToken)
|
||||
private async Task<IPAddress> LookupAsync(DnsEndPoint target, CancellationToken cancellationToken)
|
||||
{
|
||||
if (this.memoryCache.TryGetValue<IPAddress>(endPoint, out var address))
|
||||
if (this.memoryCache.TryGetValue<IPAddress>(target, out var address))
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
var expiration = this.pureResolveCacheTimeSpan;
|
||||
address = await this.LookupCoreAsync(this.fastGithubConfig.PureDns, endPoint, cancellationToken);
|
||||
var expiration = this.dnscryptExpiration;
|
||||
address = await this.LookupCoreAsync(this.dnscryptProxy.EndPoint, target, cancellationToken);
|
||||
|
||||
if (address == null)
|
||||
{
|
||||
expiration = this.fastResolveCacheTimeSpan;
|
||||
address = await this.LookupCoreAsync(this.fastGithubConfig.FastDns, endPoint, cancellationToken);
|
||||
expiration = this.fallbackExpiration;
|
||||
address = await this.FallbackLookupAsync(target, cancellationToken);
|
||||
}
|
||||
|
||||
if (address == null)
|
||||
{
|
||||
throw new FastGithubException($"当前解析不到{endPoint.Host}可用的ip,请刷新重试");
|
||||
throw new FastGithubException($"当前解析不到{target.Host}可用的ip,请刷新重试");
|
||||
}
|
||||
|
||||
// 往往是被污染的dns
|
||||
if (address.Equals(IPAddress.Loopback) == true)
|
||||
{
|
||||
expiration = this.loopbackResolveCacheTimeSpan;
|
||||
expiration = this.loopbackExpiration;
|
||||
}
|
||||
|
||||
this.logger.LogInformation($"[{endPoint.Host}->{address}]");
|
||||
this.memoryCache.Set(endPoint, address, expiration);
|
||||
this.logger.LogInformation($"[{target.Host}->{address}]");
|
||||
this.memoryCache.Set(target, address, expiration);
|
||||
return address;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回退查找ip
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<IPAddress?> FallbackLookupAsync(DnsEndPoint target, CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var dns in this.fastGithubConfig.FallbackDns)
|
||||
{
|
||||
var address = await this.LookupCoreAsync(dns, target, cancellationToken);
|
||||
if (address != null)
|
||||
{
|
||||
return address;
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 查找ip
|
||||
/// </summary>
|
||||
/// <param name="dns"></param>
|
||||
/// <param name="endPoint"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<IPAddress?> LookupCoreAsync(IPEndPoint dns, DnsEndPoint endPoint, CancellationToken cancellationToken)
|
||||
private async Task<IPAddress?> LookupCoreAsync(IPEndPoint dns, DnsEndPoint target, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dnsClient = new DnsClient(dns);
|
||||
using var timeoutTokenSource = new CancellationTokenSource(this.lookupTimeout);
|
||||
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
|
||||
var addresses = await dnsClient.Lookup(endPoint.Host, RecordType.A, linkedTokenSource.Token);
|
||||
return await this.FindFastValueAsync(addresses, endPoint.Port, cancellationToken);
|
||||
var addresses = await dnsClient.Lookup(target.Host, RecordType.A, linkedTokenSource.Token);
|
||||
return await this.FindFastValueAsync(addresses, target.Port, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogWarning($"dns({dns})无法解析{endPoint.Host}:{ex.Message}");
|
||||
this.logger.LogWarning($"dns({dns})无法解析{target.Host}:{ex.Message}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取最快的ip
|
||||
/// </summary>
|
||||
@ -152,6 +177,7 @@ namespace FastGithub.DomainResolve
|
||||
return await fastTask;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 验证远程节点是否可连接
|
||||
/// </summary>
|
||||
|
||||
@ -12,9 +12,9 @@ namespace FastGithub.DomainResolve
|
||||
/// <summary>
|
||||
/// 解析域名
|
||||
/// </summary>
|
||||
/// <param name="endPoint"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task<IPAddress> ResolveAsync(DnsEndPoint endPoint, CancellationToken cancellationToken = default);
|
||||
Task<IPAddress> ResolveAsync(DnsEndPoint target, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@ namespace FastGithub
|
||||
public static IServiceCollection AddDomainResolve(this IServiceCollection services)
|
||||
{
|
||||
services.AddMemoryCache();
|
||||
services.TryAddSingleton<DnscryptProxy>();
|
||||
services.TryAddSingleton<IDomainResolver, DomainResolver>();
|
||||
return services.AddHostedService<DnscryptProxyHostedService>();
|
||||
}
|
||||
|
||||
@ -5,14 +5,16 @@
|
||||
"SshPort": 22, // ssh监听的端口,修改后重启应用才生效
|
||||
"DnsPort": 53 // dns监听的端口,修改后重启应用才生效
|
||||
},
|
||||
"PureDns": { // 用于解析DomainConfigs的域名
|
||||
"IPAddress": "127.0.0.1",
|
||||
"Port": 5533 // 5533指向dnscrypt-proxy
|
||||
},
|
||||
"FastDns": { // 用于解析不在DomainConfigs的域名
|
||||
"IPAddress": "114.114.114.114",
|
||||
"Port": 53
|
||||
},
|
||||
"FallbackDns": [ // 用于解析不在DomainConfigs的域名
|
||||
{
|
||||
"IPAddress": "114.114.114.114",
|
||||
"Port": 53
|
||||
},
|
||||
{
|
||||
"IPAddress": "8.8.8.8",
|
||||
"Port": 53
|
||||
}
|
||||
],
|
||||
"DomainConfigs": {
|
||||
"*.x.y.z.com": { // 域名的*表示除.之外0到多个任意字符
|
||||
"TlsSni": false, // 指示tls握手时是否发送SNI
|
||||
|
||||
Loading…
Reference in New Issue
Block a user