"me-web?" 0.11.0: the name of the block your address belongs to
Build / Desktop, server and tools (push) Successful in 1m4s
Release / Every platform, the server, the release (push) Successful in 21m49s

- 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
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-16 18:10:19 +02:00
1 parent 340b2678ef
commit aa031c6012
132 files changed
+825 -29

No files matched your search

+76
View File
@@ -1,6 +1,8 @@
using System.Buffers.Binary;
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace MeWeb.Core.Knowledge;
@@ -12,6 +14,13 @@ public class Carrier
public static class Sources
{
static readonly HashSet<string> Allocations = ["ALLOCATED PA", "ALLOCATED UNSPECIFIED", "ALLOCATED-BY-RIR"];
static readonly UInt128 WideV4 = 2048;
static readonly UInt128 NarrowV4 = 256;
static readonly UInt128 WideV6 = UInt128.One << 88;
static readonly UInt128 NarrowV6 = UInt128.One << 80;
const int Repeated = 3;
public static IEnumerable<AsnRange> IpToAsn(Stream gzip)
{
var names = new Dictionary<string, string>(StringComparer.Ordinal);
@@ -46,6 +55,73 @@ public static class Sources
return seen.ToDictionary(p => p.Key, p => p.Value.MaxBy(n => n.Value).Key);
}
public static IEnumerable<RegistryRange> RipeBlocks(Stream gzip)
{
using var reader = new StreamReader(new GZipStream(gzip, CompressionMode.Decompress), Encoding.Latin1);
RegistryRange range = null;
string status = null;
string line;
while ((line = reader.ReadLine()) is not null)
{
if (line.Length == 0)
{
if (range?.Name is not null && !Allocations.Contains(status ?? "")) yield return range;
range = null;
status = null;
continue;
}
if (range is null)
{
range = RipeRange(line);
continue;
}
if (line.StartsWith("netname:", StringComparison.Ordinal)) range.Name ??= Value(line);
else if (line.StartsWith("country:", StringComparison.Ordinal)) range.Country ??= Value(line).ToUpperInvariant();
else if (line.StartsWith("status:", StringComparison.Ordinal)) status ??= Value(line);
}
if (range?.Name is not null && !Allocations.Contains(status ?? "")) yield return range;
}
public static List<RegistryRange> RipeSelection(IEnumerable<RegistryRange> blocks)
{
var counts = new Dictionary<string, int>(StringComparer.Ordinal);
foreach (var block in blocks)
if (block.End - block.Start >= (block.V6 ? NarrowV6 : NarrowV4) - 1) counts[block.Name] = counts.GetValueOrDefault(block.Name) + 1;
var names = new Dictionary<string, string>(StringComparer.Ordinal);
var kept = new List<RegistryRange>();
foreach (var block in blocks)
{
var size = block.End - block.Start;
if (size < (block.V6 ? NarrowV6 : NarrowV4) - 1 || (size < (block.V6 ? WideV6 : WideV4) - 1 && counts[block.Name] < Repeated)) continue;
block.Name = Intern(names, block.Name);
block.Country = Intern(names, block.Country ?? "");
kept.Add(block);
}
return kept;
}
static RegistryRange RipeRange(string line)
{
if (line.StartsWith("inetnum:", StringComparison.Ordinal))
{
var parts = Value(line).Split('-', StringSplitOptions.TrimEntries);
if (parts.Length != 2 || !IPAddress.TryParse(parts[0], out var first) || !IPAddress.TryParse(parts[1], out var last)) return null;
if (first.AddressFamily != AddressFamily.InterNetwork || last.AddressFamily != AddressFamily.InterNetwork) return null;
return new RegistryRange { Start = BinaryPrimitives.ReadUInt32BigEndian(first.GetAddressBytes()), End = BinaryPrimitives.ReadUInt32BigEndian(last.GetAddressBytes()) };
}
if (!line.StartsWith("inet6num:", StringComparison.Ordinal) || !IPNetwork.TryParse(Value(line), out var network)) return null;
var start = BinaryPrimitives.ReadUInt128BigEndian(network.BaseAddress.GetAddressBytes());
var span = network.PrefixLength == 0 ? UInt128.MaxValue : (UInt128.One << (128 - network.PrefixLength)) - 1;
return new RegistryRange { V6 = true, Start = start, End = start | span };
}
static string Value(string line)
{
var text = line.AsSpan(line.IndexOf(':') + 1);
var comment = text.IndexOf('#');
return (comment < 0 ? text : text[..comment]).Trim().ToString();
}
public static List<Carrier> Carriers(TextReader textpb)
{
var list = new List<Carrier>();