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.PacketIntercept.Dns
{
///
/// 代理冲突解决者
///
[SupportedOSPlatform("windows")]
sealed class ProxyConflictSolver : IDnsConflictSolver
{
private const int INTERNET_OPTION_REFRESH = 37;
private const int INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 95;
private const char PROXYOVERRIDE_SEPARATOR = ';';
private const string PROXYOVERRIDE_KEY = "ProxyOverride";
private const string INTERNET_SETTINGS = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
private readonly IOptions options;
private readonly ILogger logger;
[DllImport("wininet.dll")]
private static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
///
/// 代理冲突解决者
///
///
///
public ProxyConflictSolver(
IOptions options,
ILogger logger)
{
this.options = options;
this.logger = logger;
}
///
/// 解决冲突
///
///
///
public Task SolveAsync(CancellationToken cancellationToken)
{
this.SetToProxyOvride();
this.CheckProxyConflict();
return Task.CompletedTask;
}
///
/// 恢复冲突
///
///
///
public Task RestoreAsync(CancellationToken cancellationToken)
{
this.RemoveFromProxyOvride();
return Task.CompletedTask;
}
///
/// 添加到ProxyOvride
///
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);
}
///
/// 从ProxyOvride移除
///
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);
}
///
/// 检测代理冲突
///
private void CheckProxyConflict()
{
var systemProxy = HttpClient.DefaultProxy;
if (systemProxy == null)
{
return;
}
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)
{
this.logger.LogError($"由于系统设置了代理{proxyServer},{nameof(FastGithub)}无法加速{domain}");
}
}
}
///
/// 获取ProxyOverride
///
///
///
private static string[] GetProxyOvride(RegistryKey registryKey)
{
var value = registryKey.GetValue(PROXYOVERRIDE_KEY, null)?.ToString();
if (value == null)
{
return Array.Empty();
}
return value
.Split(PROXYOVERRIDE_SEPARATOR, StringSplitOptions.RemoveEmptyEntries)
.Select(item => item.Trim())
.ToArray();
}
///
/// 设置ProxyOverride
///
///
///
private static void SetProxyOvride(RegistryKey registryKey, IEnumerable items)
{
var value = string.Join(PROXYOVERRIDE_SEPARATOR, 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);
}
}
}