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);
}
}
}
}
}
}
}
}