using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Json; using System.Net.Sockets; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; namespace FastGithub.Scanner.LookupProviders { /// /// Github公开的域名与ip关系提供者 /// [Service(ServiceLifetime.Singleton, ServiceType = typeof(IGithubLookupProvider))] sealed class GithubMetaProvider : IGithubLookupProvider { private readonly IOptionsMonitor options; private readonly IHttpClientFactory httpClientFactory; private readonly ILogger logger; private const string META_URI = "https://api.github.com/meta"; /// /// 获取排序 /// public int Order => int.MaxValue; /// /// Github公开的域名与ip关系提供者 /// /// /// public GithubMetaProvider( IOptionsMonitor options, IHttpClientFactory httpClientFactory, ILogger logger) { this.options = options; this.httpClientFactory = httpClientFactory; this.logger = logger; } /// /// 查找域名与ip关系 /// /// /// /// public async Task> LookupAsync(IEnumerable domains, CancellationToken cancellationToken) { var setting = this.options.CurrentValue; if (setting.Enable == false) { return Enumerable.Empty(); } try { var httpClient = this.httpClientFactory.CreateClient(nameof(Scanner)); var meta = await GetMetaAsync(httpClient, setting.MetaUri, cancellationToken); if (meta != null) { return meta.ToDomainAddresses(domains); } } catch (Exception ex) { cancellationToken.ThrowIfCancellationRequested(); this.logger.LogWarning($"加载远程的ip列表异常:{ex.Message}"); } return Enumerable.Empty(); } /// /// 尝试获取meta /// /// /// /// private async Task GetMetaAsync(HttpClient httpClient, Uri metaUri, CancellationToken cancellationToken) { try { return await httpClient.GetFromJsonAsync(META_URI, cancellationToken); } catch (Exception) { cancellationToken.ThrowIfCancellationRequested(); this.logger.LogWarning($"使用{metaUri}的副本数据"); return await httpClient.GetFromJsonAsync(metaUri, cancellationToken); } } /// /// github的meta结构 /// private class Meta { [JsonPropertyName("web")] public string[] Web { get; set; } = Array.Empty(); [JsonPropertyName("api")] public string[] Api { get; set; } = Array.Empty(); /// /// 转换为域名与ip关系 /// /// public IEnumerable ToDomainAddresses(IEnumerable domains) { const string github = "github.com"; const string apiGithub = "api.github.com"; if (domains.Contains(github) == true) { foreach (var range in IPAddressRange.From(this.Web).OrderBy(item => item.Size)) { if (range.AddressFamily == AddressFamily.InterNetwork) { foreach (var address in range) { yield return new DomainAddress(github, address); } } } } if (domains.Contains(apiGithub) == true) { foreach (var range in IPAddressRange.From(this.Api).OrderBy(item => item.Size)) { if (range.AddressFamily == AddressFamily.InterNetwork) { foreach (var address in range) { yield return new DomainAddress(apiGithub, address); } } } } } } } }