增加快速扫描功能

This commit is contained in:
陈国伟 2021-06-23 11:49:37 +08:00
parent 01b91b801c
commit 4b025a60c1
5 changed files with 170 additions and 6 deletions

View File

@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>1.0.0</Version>
<Version>1.0.1</Version>
<Nullable>enable</Nullable>
<Description>github定制版的dns服务解析github最优的ip</Description>
<Copyright>https://github.com/xljiulang/FastGithub</Copyright>

View File

@ -34,6 +34,7 @@ namespace FastGithub
/// <returns></returns>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await githubScanService.ScanFastAsync(stoppingToken);
while (stoppingToken.IsCancellationRequested == false)
{
await githubScanService.ScanAllAsync(stoppingToken);

View File

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Linq;
@ -15,6 +16,7 @@ namespace FastGithub.Scanner
{
private readonly IEnumerable<IGithubLookupProvider> providers;
private readonly IOptionsMonitor<GithubLookupFactoryOptions> options;
private readonly ILogger<GithubLookupFacotry> logger;
/// <summary>
/// 域名与ip关系工厂
@ -23,10 +25,12 @@ namespace FastGithub.Scanner
/// <param name="options"></param>
public GithubLookupFacotry(
IEnumerable<IGithubLookupProvider> providers,
IOptionsMonitor<GithubLookupFactoryOptions> options)
IOptionsMonitor<GithubLookupFactoryOptions> options,
ILogger<GithubLookupFacotry> logger)
{
this.providers = providers.OrderBy(item => item.Order);
this.options = options;
this.logger = logger;
}
/// <summary>
@ -35,6 +39,7 @@ namespace FastGithub.Scanner
/// <returns></returns>
public async Task<IEnumerable<DomainAddress>> LookupAsync(CancellationToken cancellationToken)
{
this.logger.LogInformation($"开始查找各域名的ip..");
var hashSet = new HashSet<DomainAddress>();
var domains = this.options.CurrentValue.Domains;
@ -46,6 +51,8 @@ namespace FastGithub.Scanner
hashSet.Add(item);
}
}
this.logger.LogInformation($"查找到{hashSet.Count}条域名ip记录");
return hashSet;
}
}

View File

@ -2,6 +2,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -54,23 +55,68 @@ namespace FastGithub.Scanner
.Build();
}
/// <summary>
/// 快速扫描所有的ip
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<bool> ScanFastAsync(CancellationToken cancellationToken)
{
if (RawSocketPing.IsSupported == false)
{
this.logger.LogWarning($"{Environment.OSVersion.Platform}不支持快速扫描功能");
return false;
}
try
{
this.logger.LogInformation("快速扫描开始..");
var domainAddresses = await this.lookupFactory.LookupAsync(cancellationToken);
// ping快速过滤可用的ip
var destAddresses = domainAddresses.Select(item => item.Address);
var hashSet = await RawSocketPing.PingAsync(destAddresses, TimeSpan.FromSeconds(3d), cancellationToken);
var results = domainAddresses.Where(item => hashSet.Contains(item.Address)).ToArray();
this.logger.LogInformation($"快速扫描到{hashSet.Count}条ip{results.Length}条域名ip记录");
var successCount = await this.ScanAsync(results, cancellationToken);
this.logger.LogInformation($"快速扫描结束,成功{successCount}条共{domainAddresses.Count()}条");
return true;
}
catch (Exception ex)
{
this.logger.LogWarning($"快速扫描失败:{ex.Message}");
return false;
}
}
/// <summary>
/// 扫描所有的ip
/// </summary>
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task ScanAllAsync(CancellationToken cancellationToken)
{
this.logger.LogInformation("完整扫描开始..");
var domainAddresses = await this.lookupFactory.LookupAsync(cancellationToken);
var successCount = await this.ScanAsync(domainAddresses, cancellationToken);
this.logger.LogInformation($"完整扫描结束,成功{successCount}条共{domainAddresses.Count()}条");
}
/// <summary>
/// 扫描记录
/// </summary>
/// <param name="domainAddresses"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task<int> ScanAsync(IEnumerable<DomainAddress> domainAddresses, CancellationToken cancellationToken)
{
var scanTasks = domainAddresses
.Select(item => new GithubContext(item.Domain, item.Address, cancellationToken))
.Select(ctx => ScanAsync(ctx));
var results = await Task.WhenAll(scanTasks);
var successCount = results.Count(item => item);
this.logger.LogInformation($"完整扫描结束,成功{successCount}条共{results.Length}条");
return results.Count(item => item);
async Task<bool> ScanAsync(GithubContext context)
{

View File

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace FastGithub.Scanner
{
/// <summary>
/// RawSocket的ping功能
/// </summary>
static class RawSocketPing
{
private static readonly byte[] echoRequestPacket = Convert.FromHexString("0800F6FF0100000000000000");
private static readonly byte[] icmpReceiveBuffer = new byte[72];
/// <summary>
/// 获取是否支持
/// </summary>
public static bool IsSupported { get; private set; }
/// <summary>
/// RawSocket的ping功能
/// </summary>
static RawSocketPing()
{
try
{
new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp).Dispose();
IsSupported = true;
}
catch (Exception)
{
IsSupported = false;
}
}
/// <summary>
/// ping目标ip
/// </summary>
/// <param name="destAddresses"></param>
/// <param name="timeWait">等待时间</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>ping通的ip</returns>
public static async Task<HashSet<IPAddress>> PingAsync(IEnumerable<IPAddress> destAddresses, TimeSpan timeWait, CancellationToken cancellationToken = default)
{
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp)
{
Ttl = 128,
DontFragment = false
};
socket.Bind(new IPEndPoint(IPAddress.Any, 0));
using var cancellationTokenSource = new CancellationTokenSource();
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cancellationTokenSource.Token);
var receiveTask = ReceiveAsync(socket, linkedTokenSource.Token);
var distinctDestAddresses = destAddresses.Distinct();
foreach (var address in distinctDestAddresses)
{
var remoteEndPoint = new IPEndPoint(address, 0);
await socket.SendToAsync(echoRequestPacket, SocketFlags.None, remoteEndPoint);
}
await Task.Delay(timeWait, cancellationToken);
cancellationTokenSource.Cancel();
socket.Close();
var hashSet = await receiveTask;
hashSet.IntersectWith(distinctDestAddresses);
return hashSet;
}
/// <summary>
/// 循环接收任务
/// </summary>
/// <param name="socket"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private static async Task<HashSet<IPAddress>> ReceiveAsync(Socket socket, CancellationToken cancellationToken)
{
await Task.Yield();
var hashSet = new HashSet<IPAddress>();
var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
while (cancellationToken.IsCancellationRequested == false)
{
try
{
var result = await socket.ReceiveFromAsync(icmpReceiveBuffer, SocketFlags.None, remoteEndPoint);
if (result.RemoteEndPoint is IPEndPoint ipEndPoint)
{
hashSet.Add(ipEndPoint.Address);
}
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
{
break;
}
catch (Exception)
{
}
}
return hashSet;
}
}
}