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.Tasks;
namespace FastGithub.Scanner.DomainAddressProviders
{
///
/// Github公开的域名与ip关系提供者
///
[Service(ServiceLifetime.Singleton, ServiceType = typeof(IDomainAddressProvider))]
sealed class GithubMetaProvider : IDomainAddressProvider
{
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> CreateDomainAddressesAsync()
{
var setting = this.options.CurrentValue.DominAddressProviders.GithubMetaProvider;
if (setting.Enable == false)
{
return Enumerable.Empty();
}
try
{
var httpClient = this.httpClientFactory.CreateClient(nameof(FastGithub));
var meta = await this.GetMetaAsync(httpClient, setting.MetaUri);
if (meta != null)
{
return meta.ToDomainAddresses();
}
}
catch (Exception ex)
{
this.logger.LogWarning($"加载远程的ip列表异常:{ex.Message}");
}
return Enumerable.Empty();
}
///
/// 尝试获取meta
///
///
///
///
private async Task GetMetaAsync(HttpClient httpClient, Uri metaUri)
{
try
{
return await httpClient.GetFromJsonAsync(META_URI);
}
catch (Exception)
{
return await httpClient.GetFromJsonAsync(metaUri);
}
}
///
/// 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()
{
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.com", address);
}
}
}
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("api.github.com", address);
}
}
}
}
}
}
}