me-web? 0.5.3: the cards no longer contradict each other
- From the publisher's own network, the tests aimed at its server stay inside the house, so the type of address translation, the ports, the hairpin, how long a connection stays open, the speed and the round trip described the home network: a report said "hard NAT" next to "ports kept". These cards now say the test can't run there, and those messages are not sent. - Who holds your public IPv4 address is now decided by the same evidence as the address sharing card, so a home router behind a provider's shared address no longer reads "your router" on one card and "shared with other customers" on the next. - When your connection differs from what the provider notes say is usual (a shared address, port 25), the provider card adds "On this line" with what was found, right under the note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
This commit is contained in:
1 parent
5a7fbc4c52
commit
c72a6be433
117 files changed
+192
-21
No files matched your search
@@ -25,6 +25,7 @@ public class RunReport
|
||||
public DnsReport Dns { get; set; }
|
||||
public DeviceReport DeviceReport { get; set; }
|
||||
public CellularReport Cellular { get; set; }
|
||||
public bool OwnNetwork { get; set; }
|
||||
public SpeedReport Speed { get; set; }
|
||||
public List<UseFinding> Uses { get; set; } = [];
|
||||
public HistorySummary History { get; set; }
|
||||
@@ -84,12 +85,20 @@ public class CheckRunner(IPlatform platform, ServerProbe server, KnowledgeBase k
|
||||
Settle(Verdicts.Online(run.Online, run.Local));
|
||||
});
|
||||
|
||||
var router = Step(CheckId.Router, async () =>
|
||||
{
|
||||
await connection;
|
||||
run.Router = run.Local?.Active?.Kind == LinkKind.Mobile ? new RouterReport() : await Gateway.Inspect(platform, run.Local?.GatewayV4, ct);
|
||||
Settle(Verdicts.Router(run.Router, run.Local));
|
||||
});
|
||||
|
||||
var addresses = Step(CheckId.Addresses, async () =>
|
||||
{
|
||||
run.PublicV4 = await v4;
|
||||
run.PublicV6 = await v6;
|
||||
await connection;
|
||||
Settle(Verdicts.Addresses(run.PublicV4, run.PublicV6, run.Local));
|
||||
await Task.WhenAll(connection, router);
|
||||
run.OwnNetwork = Addresses.SameNetwork(await ServerProbe.ServerAddresses(ct), run.PublicV4, run.PublicV6);
|
||||
Settle(Verdicts.Addresses(run.PublicV4, run.PublicV6, run.Local, run.Router));
|
||||
});
|
||||
|
||||
var provider = Step(CheckId.Provider, async () =>
|
||||
@@ -102,13 +111,6 @@ public class CheckRunner(IPlatform platform, ServerProbe server, KnowledgeBase k
|
||||
Settle(Verdicts.Provider(knowledge, run.NetworkV4, run.NetworkV6, run.PublicV4 is not null || run.PublicV6 is not null, run.Local?.Active?.Kind == LinkKind.Mobile));
|
||||
});
|
||||
|
||||
var router = Step(CheckId.Router, async () =>
|
||||
{
|
||||
await connection;
|
||||
run.Router = run.Local?.Active?.Kind == LinkKind.Mobile ? new RouterReport() : await Gateway.Inspect(platform, run.Local?.GatewayV4, ct);
|
||||
Settle(Verdicts.Router(run.Router, run.Local));
|
||||
});
|
||||
|
||||
var nat = Step(CheckId.Nat, async () =>
|
||||
{
|
||||
await Task.WhenAll(router, addresses);
|
||||
@@ -122,15 +124,20 @@ public class CheckRunner(IPlatform platform, ServerProbe server, KnowledgeBase k
|
||||
{
|
||||
await connection;
|
||||
run.Stun = await Stun.Compare((Shared.Endpoints.SecondStunHost, Shared.Endpoints.SecondStunPort), (Shared.Endpoints.StunHost, Shared.Endpoints.StunPort), run.Local, ct);
|
||||
run.Stun.Ports = await Stun.Ports((Shared.Endpoints.StunHost, Shared.Endpoints.StunPort), 16, ct);
|
||||
await addresses;
|
||||
if (!run.OwnNetwork) run.Stun.Ports = await Stun.Ports((Shared.Endpoints.StunHost, Shared.Endpoints.StunPort), 16, ct);
|
||||
await nat;
|
||||
var (low, high) = history.PortRange(run.PublicV4);
|
||||
Stun.Classify(run.Stun.Ports, low, high);
|
||||
Settle(Verdicts.Behaviour(run.Stun, run.Nat));
|
||||
if (run.Stun.Ports is not null)
|
||||
{
|
||||
var (low, high) = history.PortRange(run.PublicV4);
|
||||
Stun.Classify(run.Stun.Ports, low, high);
|
||||
}
|
||||
Settle(Verdicts.Behaviour(run.Stun, run.Nat, run.OwnNetwork));
|
||||
if (run.Stun.Ports is null) return;
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Stun.Lifetime((Shared.Endpoints.StunHost, Shared.Endpoints.StunPort), run.Stun.Ports, [30, 120], ct);
|
||||
Settle(Verdicts.Behaviour(run.Stun, run.Nat));
|
||||
Settle(Verdicts.Behaviour(run.Stun, run.Nat, run.OwnNetwork));
|
||||
}, ct);
|
||||
});
|
||||
|
||||
@@ -163,6 +170,11 @@ public class CheckRunner(IPlatform platform, ServerProbe server, KnowledgeBase k
|
||||
Settle(new Finding { Id = CheckId.Speed, Outcome = Outcome.Unknown, Summary = "SpeedOffline" });
|
||||
return;
|
||||
}
|
||||
if (run.OwnNetwork)
|
||||
{
|
||||
Settle(new Finding { Id = CheckId.Speed, Outcome = Outcome.Unknown, Summary = "ReachOwnNetwork" });
|
||||
return;
|
||||
}
|
||||
run.Speed = await SpeedCheck.Run(https, run.Local?.Active?.Kind ?? LinkKind.Unknown, ct);
|
||||
Settle(Verdicts.Speed(run.Speed));
|
||||
});
|
||||
@@ -183,6 +195,7 @@ public class CheckRunner(IPlatform platform, ServerProbe server, KnowledgeBase k
|
||||
PortLow = run.Stun?.Ports?.Pattern is PortPattern.InOrder or PortPattern.Block ? run.Stun.Ports.Low : 0,
|
||||
PortHigh = run.Stun?.Ports?.Pattern is PortPattern.InOrder or PortPattern.Block ? run.Stun.Ports.High : 0,
|
||||
});
|
||||
Settle(Verdicts.Provider(knowledge, run.NetworkV4, run.NetworkV6, run.PublicV4 is not null || run.PublicV6 is not null, run.Local?.Active?.Kind == LinkKind.Mobile, run));
|
||||
Settle(Verdicts.History(run.History));
|
||||
run.Uses = Uses.Decide(run);
|
||||
});
|
||||
|
||||
@@ -127,6 +127,18 @@ public class ServerProbe(HttpClient https, HttpClient plain)
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<IPAddress[]> ServerAddresses(CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await Dns.GetHostAddressesAsync(Endpoints.Host, ct);
|
||||
}
|
||||
catch (Exception) when (!ct.IsCancellationRequested)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static async Task<bool> Resolves(string host, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -73,7 +73,7 @@ public static class Verdicts
|
||||
return finding;
|
||||
}
|
||||
|
||||
public static Finding Addresses(string v4, string v6, LocalSnapshot local)
|
||||
public static Finding Addresses(string v4, string v6, LocalSnapshot local, RouterReport router)
|
||||
{
|
||||
var finding = new Finding { Id = CheckId.Addresses };
|
||||
(finding.Outcome, finding.Summary) = (v4, v6) switch
|
||||
@@ -86,9 +86,7 @@ public static class Verdicts
|
||||
if (v4 is not null)
|
||||
{
|
||||
finding.With("FactPublicV4", v4);
|
||||
var own = local.Active?.Addresses.Any(a => Net.Addresses.SameAddress(a, v4)) == true;
|
||||
var shared = local.Active?.Kind == LinkKind.Mobile || Net.Addresses.Parse(local.LocalV4) is { } address && Net.Addresses.Classify(address) == AddressKind.Shared;
|
||||
finding.WithKey("FactWhoHasIt", own ? "ValueOnThisDevice" : shared ? "ValueSharedProvider" : "ValueSharedAtHome");
|
||||
finding.WithKey("FactWhoHasIt", Holder(NatOf(local, router, v4)));
|
||||
}
|
||||
if (v6 is not null)
|
||||
{
|
||||
@@ -99,7 +97,7 @@ public static class Verdicts
|
||||
return finding;
|
||||
}
|
||||
|
||||
public static Finding Provider(KnowledgeBase knowledge, AsnEntry v4, AsnEntry v6, bool anyAddress, bool mobile)
|
||||
public static Finding Provider(KnowledgeBase knowledge, AsnEntry v4, AsnEntry v6, bool anyAddress, bool mobile, RunReport run = null)
|
||||
{
|
||||
var finding = new Finding { Id = CheckId.Provider };
|
||||
if (!anyAddress)
|
||||
@@ -131,9 +129,19 @@ public static class Verdicts
|
||||
if (upstreams.Count > 0) finding.With("FactUpstreams", string.Join("\n", upstreams.Select(u => $"{knowledge.NameOf(u) ?? "AS" + u} (AS{u})")));
|
||||
if (profile is null) return finding;
|
||||
AddFact(finding, "FactProviderIpv4", "ProviderIpv4", profile.Ipv4);
|
||||
var usual = profile.Ipv4?.Value is null ? null : Pascal(profile.Ipv4.Value);
|
||||
var sharedHere = run?.Nat is NatKind.Shared or NatKind.Translated;
|
||||
var ownHere = run?.Nat is NatKind.HomeRouter or NatKind.Direct or NatKind.DoubleNat;
|
||||
if ((usual is "Shared" or "SharedPorts" && ownHere) || (usual is "PublicDynamic" && sharedHere))
|
||||
finding.WithKey("FactThisLine", Holder(run.Nat));
|
||||
AddFact(finding, "FactProviderPublic", "ProviderPublic", profile.PublicAddress);
|
||||
AddFact(finding, "FactProviderIpv6", "ProviderIpv6", profile.Ipv6);
|
||||
AddFact(finding, "FactProviderPort25", "ProviderPort25", profile.Port25);
|
||||
var port25 = profile.Port25?.Value is null ? null : Pascal(profile.Port25.Value);
|
||||
if (run?.Mail?.Port25 == Core.Mail.PortState.Open && port25 is "Blocked" or "BlockedOnRequest" or "Closed")
|
||||
finding.WithKey("FactThisLine", "ValuePort25Open", run.Mail.Banner ?? "");
|
||||
else if (run?.Mail?.Port25 == Core.Mail.PortState.Blocked && port25 is "Open")
|
||||
finding.WithKey("FactThisLine", "ValuePort25Blocked");
|
||||
if (profile.Sheet is not null) finding.Facts.Add(new Fact { Label = "FactProviderSheet", Key = "ValueOpenSheet", Link = profile.Sheet });
|
||||
return finding;
|
||||
}
|
||||
@@ -192,6 +200,13 @@ public static class Verdicts
|
||||
return (finding, kind);
|
||||
}
|
||||
|
||||
static string Holder(NatKind nat) => nat switch
|
||||
{
|
||||
NatKind.Direct => "ValueOnThisDevice",
|
||||
NatKind.Shared or NatKind.Translated => "ValueSharedProvider",
|
||||
_ => "ValueSharedAtHome",
|
||||
};
|
||||
|
||||
public static NatKind NatOf(LocalSnapshot local, RouterReport router, string v4)
|
||||
{
|
||||
if (v4 is null) return NatKind.NoIpv4;
|
||||
@@ -212,11 +227,12 @@ public static class Verdicts
|
||||
};
|
||||
}
|
||||
|
||||
public static Finding Behaviour(StunReport stun, NatKind nat)
|
||||
public static Finding Behaviour(StunReport stun, NatKind nat, bool ownNetwork)
|
||||
{
|
||||
var finding = new Finding { Id = CheckId.Behaviour };
|
||||
(finding.Outcome, finding.Summary) = stun.Behaviour switch
|
||||
{
|
||||
_ when ownNetwork => (Outcome.Unknown, "ReachOwnNetwork"),
|
||||
NatBehaviour.None => (Outcome.Good, "BehaviourNone"),
|
||||
NatBehaviour.Easy => (Outcome.Good, "BehaviourEasy"),
|
||||
NatBehaviour.Hard => (Outcome.Caution, "BehaviourHard"),
|
||||
|
||||
@@ -36,6 +36,17 @@ public static class Addresses
|
||||
|
||||
public static IPAddress Parse(string text) => IPAddress.TryParse(text, out var address) ? address : null;
|
||||
|
||||
public static bool SameNetwork(IEnumerable<IPAddress> server, string v4, string v6)
|
||||
{
|
||||
foreach (var address in server)
|
||||
{
|
||||
if (address.AddressFamily == AddressFamily.InterNetwork && SameAddress(address.ToString(), v4)) return true;
|
||||
if (address.AddressFamily != AddressFamily.InterNetworkV6 || Parse(v6) is not { } mine) continue;
|
||||
if (address.GetAddressBytes().AsSpan(0, 6).SequenceEqual(mine.GetAddressBytes().AsSpan(0, 6))) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool SameAddress(string left, string right)
|
||||
{
|
||||
var a = Parse(left);
|
||||
|
||||
Reference in new issue
Block a user