120 lines
3.8 KiB
C#
120 lines
3.8 KiB
C#
using System.Net;
|
|||
|
|
using System.Net.Http.Json;
|
||
|
|
using System.Net.Sockets;
|
||
|
|
|
||
|
|
using MeWeb.Shared;
|
||
|
|
|
||
|
|
namespace MeWeb.Core.Checks;
|
||
|
|
|
||
|
|
public enum OnlineState
|
||
|
|
{
|
||
|
|
Online,
|
||
|
|
Captive,
|
||
|
|
Rewritten,
|
||
|
|
NoNames,
|
||
|
|
Offline,
|
||
|
|
}
|
||
|
|
|
||
|
|
public class OnlineReport
|
||
|
|
{
|
||
|
|
public OnlineState State { get; set; }
|
||
|
|
public ViewInfo Server { get; set; }
|
||
|
|
public string Portal { get; set; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public class ServerProbe(HttpClient https, HttpClient plain)
|
||
|
|
{
|
||
|
|
static readonly TimeSpan Patience = TimeSpan.FromSeconds(6);
|
||
|
|
|
||
|
|
public async Task<OnlineReport> Online(CancellationToken ct)
|
||
|
|
{
|
||
|
|
var names = Resolves(Endpoints.Host, ct);
|
||
|
|
var info = Info(ct);
|
||
|
|
var web = Plain(ct);
|
||
|
|
await Task.WhenAll(names, info, web);
|
||
|
|
var (plainClean, portal) = web.Result;
|
||
|
|
|
||
|
|
if (info.Result is not null)
|
||
|
|
return new OnlineReport { State = plainClean is false ? OnlineState.Rewritten : OnlineState.Online, Server = info.Result, Portal = portal };
|
||
|
|
if (plainClean is false) return new OnlineReport { State = OnlineState.Captive, Portal = portal };
|
||
|
|
return new OnlineReport { State = names.Result ? OnlineState.Offline : OnlineState.NoNames };
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<string> Echo(bool v6, CancellationToken ct)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||
|
|
timeout.CancelAfter(Patience);
|
||
|
|
var origin = v6 ? Endpoints.BaseV6 : Endpoints.BaseV4;
|
||
|
|
var answer = await https.GetFromJsonAsync(origin + Endpoints.Server.GET_Ip, MeWebJsonContext.Default.ViewIp, timeout.Token);
|
||
|
|
return answer?.Address;
|
||
|
|
}
|
||
|
|
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException or System.Text.Json.JsonException && !ct.IsCancellationRequested)
|
||
|
|
{
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static async Task<IPAddress> ResolveV4(CancellationToken ct)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var addresses = await Dns.GetHostAddressesAsync(Endpoints.HostV4, AddressFamily.InterNetwork, ct);
|
||
|
|
return addresses.FirstOrDefault();
|
||
|
|
}
|
||
|
|
catch (SocketException)
|
||
|
|
{
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static async Task<bool> Resolves(string host, CancellationToken ct)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
return (await Dns.GetHostAddressesAsync(host, ct)).Length > 0;
|
||
|
|
}
|
||
|
|
catch (SocketException)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task<ViewInfo> Info(CancellationToken ct)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||
|
|
timeout.CancelAfter(Patience);
|
||
|
|
return await https.GetFromJsonAsync(Endpoints.Base + Endpoints.Server.GET_Info, MeWebJsonContext.Default.ViewInfo, timeout.Token);
|
||
|
|
}
|
||
|
|
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException or System.Text.Json.JsonException && !ct.IsCancellationRequested)
|
||
|
|
{
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task<(bool? Clean, string Portal)> Plain(CancellationToken ct)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||
|
|
timeout.CancelAfter(Patience);
|
||
|
|
using var response = await plain.GetAsync($"http://{Endpoints.Host}{Endpoints.Server.GET_Info}", HttpCompletionOption.ResponseHeadersRead, timeout.Token);
|
||
|
|
var location = response.Headers.Location;
|
||
|
|
if ((int)response.StatusCode is >= 300 and < 400 && location is not null)
|
||
|
|
{
|
||
|
|
var target = location.IsAbsoluteUri ? location : new Uri(new Uri($"http://{Endpoints.Host}"), location);
|
||
|
|
var clean = target.Scheme == Uri.UriSchemeHttps && string.Equals(target.Host, Endpoints.Host, StringComparison.OrdinalIgnoreCase);
|
||
|
|
return (clean, clean ? null : target.Host);
|
||
|
|
}
|
||
|
|
if (response.IsSuccessStatusCode && response.Content.Headers.ContentType?.MediaType == "application/json") return (true, null);
|
||
|
|
return (false, null);
|
||
|
|
}
|
||
|
|
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException && !ct.IsCancellationRequested)
|
||
|
|
{
|
||
|
|
return (null, null);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|