Files
MeWeb/src/MeWeb.Core/Knowledge/Providers.cs
T
thepraandClaude Opus 5.5 aa031c6012
Build / Desktop, server and tools (push) Successful in 1m4s
Release / Every platform, the server, the release (push) Successful in 21m49s
"me-web?" 0.11.0: the name of the block your address belongs to
- The provider card now shows the address block your connection belongs to, as the RIPE Database names it, for
  example "MOBILE-USERS-BLOCK (IT)" on Iliad mobile or "FASTWEB-L3-PAT_NAT (IT)" on a Fastweb shared address. The
  path shows it under your provider too.
- No block is left out: the server keeps every block in the RIPE Database, small and unique ones included, and tells
  the app the block of the address it sees, never anyone else's. The app also carries about 430,000 of the larger
  blocks, for when the server can't be reached.
- Only each block's range, name and country are used, nothing about people. The data comes from the RIPE NCC's RIPE
  Database, used with the RIPE NCC's permission; it may lag behind the live database, and the "What this app
  contacts" sheet gives its date.
- The privacy page says that the server's answer now includes that block name; nothing new is stored or sent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
2026-10-16 18:10:19 +02:00

109 lines
4.1 KiB
C#

using System.Text.Json;
using System.Text.Json.Serialization;
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<uint> 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<Carrier>))]
[JsonSerializable(typeof(List<ObservedNetwork>))]
public partial class KnowledgeJsonContext : JsonSerializerContext
{
}
public sealed class KnowledgeBase
{
readonly Lazy<AsnTable> asn = new(LoadAsn);
readonly Lazy<List<ProviderProfile>> providers = new(LoadProviders);
readonly Lazy<NetworkTable> networks = new(LoadNetworks);
readonly Lazy<RegistryTable> registry = new(LoadRegistry);
readonly Lazy<Dictionary<string, string>> carriers = new(LoadCarriers);
readonly Lazy<List<ObservedNetwork>> observed = new(LoadObserved);
public bool HasAsnTable => typeof(KnowledgeBase).Assembly.GetManifestResourceInfo("ip2asn.bin") is not null;
public AsnTable Asn => asn.Value;
public IReadOnlyList<ProviderProfile> 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()
{
using var stream = typeof(KnowledgeBase).Assembly.GetManifestResourceStream("registry.bin");
return stream is null ? null : RegistryTable.Read(stream);
}
static Dictionary<string, string> 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<ObservedNetwork> 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<ProviderProfile> LoadProviders()
{
var assembly = typeof(KnowledgeBase).Assembly;
var list = new List<ProviderProfile>();
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;
}
}