using System.Text.Json; using System.Text.Json.Serialization; using MeWeb.Core.Net; namespace MeWeb.Core.Knowledge; public class ProviderFact { public string Value { get; set; } public decimal? Price { get; set; } public string Currency { get; set; } public string Basis { get; set; } public string Source { get; set; } public string Date { get; set; } } public class ProviderProfile { public string Name { get; set; } public string Country { get; set; } public List Asns { get; set; } = []; public string Sheet { get; set; } public ProviderFact Ipv4 { get; set; } public ProviderFact PublicAddress { get; set; } public ProviderFact Ipv6 { get; set; } public ProviderFact Port25 { get; set; } } [JsonSourceGenerationOptions(JsonSerializerDefaults.Web)] [JsonSerializable(typeof(ProviderProfile))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] public partial class KnowledgeJsonContext : JsonSerializerContext { } public sealed class KnowledgeBase(IPlatform platform) { readonly Lazy asn = new(LoadAsn); readonly Lazy> providers = new(LoadProviders); readonly Lazy networks = new(LoadNetworks); readonly Lazy registry = new(() => LoadRegistry(platform)); readonly Lazy> carriers = new(LoadCarriers); readonly Lazy> observed = new(LoadObserved); public bool HasAsnTable => typeof(KnowledgeBase).Assembly.GetManifestResourceInfo("ip2asn.bin") is not null; public AsnTable Asn => asn.Value; public IReadOnlyList Providers => providers.Value; public RegistryTable Registry => registry.Value; public ProviderProfile ProviderFor(uint number) => number == 0 ? null : Providers.FirstOrDefault(p => p.Asns.Contains(number)); public ObservedNetwork ObservedFor(uint number, bool mobile) => number == 0 ? null : observed.Value.FirstOrDefault(o => o.Asn == number && o.Line == (mobile ? ObservedNetwork.Mobile : ObservedNetwork.Home)); public NetworkInfo NetworkFor(uint number) => number == 0 ? null : networks.Value?.Find(number); public string CarrierFor(string mccMnc) => string.IsNullOrEmpty(mccMnc) ? null : carriers.Value.GetValueOrDefault(mccMnc); public string NameOf(uint number) => NetworkFor(number)?.Organisation ?? Asn?.Find(number)?.Name; static NetworkTable LoadNetworks() { using var stream = typeof(KnowledgeBase).Assembly.GetManifestResourceStream("networks.bin"); return stream is null ? null : NetworkTable.Read(stream); } static RegistryTable LoadRegistry(IPlatform platform) { var stream = platform.OpenRegistry(); if (stream is null) return null; try { return RegistryTable.Open(stream); } catch (Exception ex) when (ex is IOException or InvalidDataException or EndOfStreamException) { stream.Dispose(); return null; } } static Dictionary LoadCarriers() { using var stream = typeof(KnowledgeBase).Assembly.GetManifestResourceStream("carriers.json"); if (stream is null) return []; var list = JsonSerializer.Deserialize(stream, KnowledgeJsonContext.Default.ListCarrier) ?? []; return list.Where(c => c.MccMnc is not null && c.Name is not null).GroupBy(c => c.MccMnc).ToDictionary(g => g.Key, g => g.First().Name); } static List LoadObserved() { using var stream = typeof(KnowledgeBase).Assembly.GetManifestResourceStream("observed.json"); return stream is null ? [] : JsonSerializer.Deserialize(stream, KnowledgeJsonContext.Default.ListObservedNetwork) ?? []; } static AsnTable LoadAsn() { using var stream = typeof(KnowledgeBase).Assembly.GetManifestResourceStream("ip2asn.bin"); return stream is null ? null : AsnTable.Read(stream); } static List LoadProviders() { var assembly = typeof(KnowledgeBase).Assembly; var list = new List(); foreach (var name in assembly.GetManifestResourceNames().Where(n => n.StartsWith("isp.", StringComparison.Ordinal)).Order(StringComparer.Ordinal)) { using var stream = assembly.GetManifestResourceStream(name); var profile = JsonSerializer.Deserialize(stream, KnowledgeJsonContext.Default.ProviderProfile); if (profile is not null) list.Add(profile); } return list; } }