自动解决冲突问题
This commit is contained in:
parent
df1ee8cc46
commit
a1284a4013
@ -13,19 +13,47 @@ namespace FastGithub.Dns
|
||||
sealed class DnsInterceptHostedService : BackgroundService
|
||||
{
|
||||
private readonly DnsInterceptor dnsInterceptor;
|
||||
private readonly IEnumerable<IConflictValidator> conflictValidators;
|
||||
private readonly IEnumerable<IConflictSolver> conflictSolvers;
|
||||
|
||||
/// <summary>
|
||||
/// dns拦截后台服务
|
||||
/// </summary>
|
||||
/// <param name="dnsInterceptor"></param>
|
||||
/// <param name="conflictValidators"></param>
|
||||
/// <param name="conflictSolvers"></param>
|
||||
public DnsInterceptHostedService(
|
||||
DnsInterceptor dnsInterceptor,
|
||||
IEnumerable<IConflictValidator> conflictValidators)
|
||||
IEnumerable<IConflictSolver> conflictSolvers)
|
||||
{
|
||||
this.dnsInterceptor = dnsInterceptor;
|
||||
this.conflictValidators = conflictValidators;
|
||||
this.conflictSolvers = conflictSolvers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动时处理冲突
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var solver in this.conflictSolvers)
|
||||
{
|
||||
await solver.SolveAsync(cancellationToken);
|
||||
}
|
||||
await base.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止时恢复冲突
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var solver in this.conflictSolvers)
|
||||
{
|
||||
await solver.RestoreAsync(cancellationToken);
|
||||
}
|
||||
await base.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -36,10 +64,6 @@ namespace FastGithub.Dns
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Yield();
|
||||
foreach (var item in this.conflictValidators)
|
||||
{
|
||||
await item.ValidateAsync();
|
||||
}
|
||||
this.dnsInterceptor.Intercept(stoppingToken);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="DNS" Version="6.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="WinDivertSharp" Version="1.4.3.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
92
FastGithub.Dns/HostsConflictSolver.cs
Normal file
92
FastGithub.Dns/HostsConflictSolver.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using FastGithub.Configuration;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastGithub.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// host文件冲解决者
|
||||
/// </summary>
|
||||
[SupportedOSPlatform("windows")]
|
||||
sealed class HostsConflictSolver : IConflictSolver
|
||||
{
|
||||
private readonly FastGithubConfig fastGithubConfig;
|
||||
|
||||
/// <summary>
|
||||
/// host文件冲解决者
|
||||
/// </summary>
|
||||
/// <param name="fastGithubConfig"></param>
|
||||
public HostsConflictSolver(
|
||||
FastGithubConfig fastGithubConfig)
|
||||
{
|
||||
this.fastGithubConfig = fastGithubConfig;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解决冲突
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SolveAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var hostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "drivers/etc/hosts");
|
||||
if (File.Exists(hostsPath) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var hostsBuilder = new StringBuilder();
|
||||
var lines = await File.ReadAllLinesAsync(hostsPath, cancellationToken);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (this.IsConflictingLine(line))
|
||||
{
|
||||
hostsBuilder.AppendLine($"# {line}");
|
||||
}
|
||||
else
|
||||
{
|
||||
hostsBuilder.AppendLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
File.SetAttributes(hostsPath, FileAttributes.Normal);
|
||||
await File.WriteAllTextAsync(hostsPath, hostsBuilder.ToString(), cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复冲突
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public Task RestoreAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为冲突的行
|
||||
/// </summary>
|
||||
/// <param name="line"></param>
|
||||
/// <returns></returns>
|
||||
private bool IsConflictingLine(string line)
|
||||
{
|
||||
if (line.TrimStart().StartsWith("#"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var items = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (items.Length < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var domain = items[1];
|
||||
return this.fastGithubConfig.IsMatch(domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,117 +0,0 @@
|
||||
using FastGithub.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastGithub.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// host文件冲突验证器
|
||||
/// </summary>
|
||||
sealed class HostsConflictValidator : IConflictValidator
|
||||
{
|
||||
private readonly FastGithubConfig fastGithubConfig;
|
||||
private readonly ILogger<HostsConflictValidator> logger;
|
||||
|
||||
/// <summary>
|
||||
/// host文件冲突验证器
|
||||
/// </summary>
|
||||
/// <param name="fastGithubConfig"></param>
|
||||
/// <param name="logger"></param>
|
||||
public HostsConflictValidator(
|
||||
FastGithubConfig fastGithubConfig,
|
||||
ILogger<HostsConflictValidator> logger)
|
||||
{
|
||||
this.fastGithubConfig = fastGithubConfig;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证冲突
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task ValidateAsync()
|
||||
{
|
||||
var hostsPath = @"/etc/hosts";
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
hostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), $"drivers/{hostsPath}");
|
||||
}
|
||||
|
||||
if (File.Exists(hostsPath) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var lines = await File.ReadAllLinesAsync(hostsPath);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (HostsRecord.TryParse(line, out var record) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (IPAddress.Loopback.Equals(record.Address) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (this.fastGithubConfig.IsMatch(record.Domain))
|
||||
{
|
||||
this.logger.LogError($"由于你的hosts文件设置了{record},{nameof(FastGithub)}无法加速此域名");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// hosts文件记录
|
||||
/// </summary>
|
||||
private class HostsRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取域名
|
||||
/// </summary>
|
||||
public string Domain { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取地址
|
||||
/// </summary>
|
||||
public IPAddress Address { get; }
|
||||
|
||||
private HostsRecord(string domain, IPAddress address)
|
||||
{
|
||||
this.Domain = domain;
|
||||
this.Address = address;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{this.Domain}->{this.Address}]";
|
||||
}
|
||||
|
||||
public static bool TryParse(string record, [MaybeNullWhen(false)] out HostsRecord value)
|
||||
{
|
||||
value = null;
|
||||
if (record.TrimStart().StartsWith("#"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var items = record.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (items.Length < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IPAddress.TryParse(items[0], out var address) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = new HostsRecord(items[1], address);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
FastGithub.Dns/IConflictSolver.cs
Normal file
25
FastGithub.Dns/IConflictSolver.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastGithub.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// Dns冲突解决者
|
||||
/// </summary>
|
||||
interface IConflictSolver
|
||||
{
|
||||
/// <summary>
|
||||
/// 解决冲突
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task SolveAsync(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 恢复冲突
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task RestoreAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastGithub.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// Dns冲突验证器
|
||||
/// </summary>
|
||||
interface IConflictValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证冲突
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task ValidateAsync();
|
||||
}
|
||||
}
|
||||
151
FastGithub.Dns/ProxyConflictSolver.cs
Normal file
151
FastGithub.Dns/ProxyConflictSolver.cs
Normal file
@ -0,0 +1,151 @@
|
||||
using FastGithub.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastGithub.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// 代理冲突解决者
|
||||
/// </summary>
|
||||
[SupportedOSPlatform("windows")]
|
||||
sealed class ProxyConflictSolver : IConflictSolver
|
||||
{
|
||||
private const int INTERNET_OPTION_REFRESH = 37;
|
||||
private const int INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 95;
|
||||
|
||||
private const string PROXYOVERRIDE_KEY = "ProxyOverride";
|
||||
private const string INTERNET_SETTINGS = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
|
||||
|
||||
private readonly IOptions<FastGithubOptions> options;
|
||||
private readonly ILogger<ProxyConflictSolver> logger;
|
||||
|
||||
[DllImport("wininet.dll")]
|
||||
private static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 代理冲突解决者
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="logger"></param>
|
||||
public ProxyConflictSolver(
|
||||
IOptions<FastGithubOptions> options,
|
||||
ILogger<ProxyConflictSolver> logger)
|
||||
{
|
||||
this.options = options;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解决冲突
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public Task SolveAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
this.SetToProxyOvride();
|
||||
this.CheckProxyConflict();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复冲突
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public Task RestoreAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
this.RemoveFromProxyOvride();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加到ProxyOvride
|
||||
/// </summary>
|
||||
private void SetToProxyOvride()
|
||||
{
|
||||
using var settings = Registry.CurrentUser.OpenSubKey(INTERNET_SETTINGS, writable: true);
|
||||
if (settings == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var items = this.options.Value.DomainConfigs.Keys.ToHashSet();
|
||||
foreach (var item in GetProxyOvride(settings))
|
||||
{
|
||||
items.Add(item);
|
||||
}
|
||||
SetProxyOvride(settings, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从ProxyOvride移除
|
||||
/// </summary>
|
||||
private void RemoveFromProxyOvride()
|
||||
{
|
||||
using var settings = Registry.CurrentUser.OpenSubKey(INTERNET_SETTINGS, writable: true);
|
||||
if (settings == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var proxyOvride = GetProxyOvride(settings);
|
||||
var items = proxyOvride.Except(this.options.Value.DomainConfigs.Keys);
|
||||
SetProxyOvride(settings, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测代理冲突
|
||||
/// </summary>
|
||||
private void CheckProxyConflict()
|
||||
{
|
||||
if (HttpClient.DefaultProxy == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var domain in this.options.Value.DomainConfigs.Keys)
|
||||
{
|
||||
var destination = new Uri($"https://{domain.Replace('*', 'a')}");
|
||||
var proxyServer = HttpClient.DefaultProxy.GetProxy(destination);
|
||||
if (proxyServer != null)
|
||||
{
|
||||
this.logger.LogError($"由于系统配置了{proxyServer}代理{domain},{nameof(FastGithub)}无法加速相关域名");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取ProxyOverride
|
||||
/// </summary>
|
||||
/// <param name="registryKey"></param>
|
||||
/// <returns></returns>
|
||||
private static string[] GetProxyOvride(RegistryKey registryKey)
|
||||
{
|
||||
var value = registryKey.GetValue(PROXYOVERRIDE_KEY, null)?.ToString();
|
||||
return value == null ? Array.Empty<string>() : value.Split(';', StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置ProxyOverride
|
||||
/// </summary>
|
||||
/// <param name="registryKey"></param>
|
||||
/// <param name="items"></param>
|
||||
private static void SetProxyOvride(RegistryKey registryKey, IEnumerable<string> items)
|
||||
{
|
||||
var value = string.Join(';', items);
|
||||
registryKey.SetValue(PROXYOVERRIDE_KEY, value, RegistryValueKind.String);
|
||||
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_PROXY_SETTINGS_CHANGED, IntPtr.Zero, 0);
|
||||
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
using FastGithub.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastGithub.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// 代理冲突验证
|
||||
/// </summary>
|
||||
sealed class ProxyConflictValidtor : IConflictValidator
|
||||
{
|
||||
private readonly IOptions<FastGithubOptions> options;
|
||||
private readonly ILogger<ProxyConflictValidtor> logger;
|
||||
|
||||
public ProxyConflictValidtor(
|
||||
IOptions<FastGithubOptions> options,
|
||||
ILogger<ProxyConflictValidtor> logger)
|
||||
{
|
||||
this.options = options;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证冲突
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task ValidateAsync()
|
||||
{
|
||||
var systemProxy = HttpClient.DefaultProxy;
|
||||
if (systemProxy == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var httpProxyPort = this.options.Value.HttpProxyPort;
|
||||
foreach (var domain in this.options.Value.DomainConfigs.Keys)
|
||||
{
|
||||
var destination = new Uri($"https://{domain.Replace('*', 'a')}");
|
||||
var proxyServer = systemProxy.GetProxy(destination);
|
||||
|
||||
if (proxyServer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (await IsFastGithubProxyAsync(proxyServer, httpProxyPort) == false)
|
||||
{
|
||||
this.logger.LogError($"由于系统配置了{proxyServer}代理{domain},{nameof(FastGithub)}无法加速相关域名");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为fastgithub代理
|
||||
/// </summary>
|
||||
/// <param name="proxyServer"></param>
|
||||
/// <param name="httpProxyPort"></param>
|
||||
/// <returns></returns>
|
||||
private static async Task<bool> IsFastGithubProxyAsync(Uri proxyServer, int httpProxyPort)
|
||||
{
|
||||
if (proxyServer.Port != httpProxyPort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IPAddress.TryParse(proxyServer.Host, out var address))
|
||||
{
|
||||
return address.Equals(IPAddress.Loopback);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var addresses = await System.Net.Dns.GetHostAddressesAsync(proxyServer.Host);
|
||||
return addresses.Contains(IPAddress.Loopback);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,8 +19,8 @@ namespace FastGithub
|
||||
public static IServiceCollection AddDnsInterceptor(this IServiceCollection services)
|
||||
{
|
||||
services.TryAddSingleton<DnsInterceptor>();
|
||||
services.AddSingleton<IConflictValidator, HostsConflictValidator>();
|
||||
services.AddSingleton<IConflictValidator, ProxyConflictValidtor>();
|
||||
services.AddSingleton<IConflictSolver, HostsConflictSolver>();
|
||||
services.AddSingleton<IConflictSolver, ProxyConflictSolver>();
|
||||
return services.AddHostedService<DnsInterceptHostedService>();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user