Task
Two implementations of relay endpoint parsing, fed the same input string, disagreeing on every failure case. dig-node's fails open.
The defect
- Canonical --
modules/crates/10-primitives/dig-nat/src/relay.rs:1066 (currently private): rejects an unknown scheme (:1073, asserted :1573), rejects an unparsable port (:1095, asserted :1574), strips userinfo (:1077), splits on #.
- Copy --
crates/dig-node-core/src/seams/dig_peer/net.rs:306 and :437: accepts any scheme; a bad port becomes None via .parse().ok() (net.rs:447), which relay_socket_addr then turns into .unwrap_or(443) (net.rs:429). It also returns user@host when userinfo is present, and leaves #frag in the host.
So for one malformed endpoint string, the canonical parser refuses to dial and the shipped one silently dials 443 -- at whatever host survived its looser parse, which may not be the host the operator wrote.
Both are fed the same string from peer.rs:2296 into :2360 / :2467, so the divergence is reachable, not theoretical.
Why the canonical direction is right
A relay endpoint is operator configuration. A value that does not parse means the operator's intent is unknown, and the safe reading of unknown intent is not to invent a port and connect anyway -- particularly for a relay, which is a component that sees traffic.
Survivor
dig_nat::parse_relay_endpoint.
Scope
Make parse_relay_endpoint and RelayEndpoint pub -- additive, breaks nobody -- then delete parse_relay_host and relay_port from dig-node. dig-node-core already depends on dig-nat (Cargo.toml:199), so there is no dependency question.
Evidence
Tests that fail without the fix: an unknown scheme is refused; a malformed port is refused rather than defaulted to 443; userinfo is stripped from the host; a fragment does not end up in the host.
Parent
Rival-centralization epic: https://github.com/DIG-Network/dig_ecosystem/issues/3140
Task
Two implementations of relay endpoint parsing, fed the same input string, disagreeing on every failure case. dig-node's fails open.
The defect
modules/crates/10-primitives/dig-nat/src/relay.rs:1066(currently private): rejects an unknown scheme (:1073, asserted:1573), rejects an unparsable port (:1095, asserted:1574), strips userinfo (:1077), splits on#.crates/dig-node-core/src/seams/dig_peer/net.rs:306and:437: accepts any scheme; a bad port becomesNonevia.parse().ok()(net.rs:447), whichrelay_socket_addrthen turns into.unwrap_or(443)(net.rs:429). It also returnsuser@hostwhen userinfo is present, and leaves#fragin the host.So for one malformed endpoint string, the canonical parser refuses to dial and the shipped one silently dials 443 -- at whatever host survived its looser parse, which may not be the host the operator wrote.
Both are fed the same string from
peer.rs:2296into:2360/:2467, so the divergence is reachable, not theoretical.Why the canonical direction is right
A relay endpoint is operator configuration. A value that does not parse means the operator's intent is unknown, and the safe reading of unknown intent is not to invent a port and connect anyway -- particularly for a relay, which is a component that sees traffic.
Survivor
dig_nat::parse_relay_endpoint.Scope
Make
parse_relay_endpointandRelayEndpointpub-- additive, breaks nobody -- then deleteparse_relay_hostandrelay_portfrom dig-node.dig-node-corealready depends ondig-nat(Cargo.toml:199), so there is no dependency question.Evidence
Tests that fail without the fix: an unknown scheme is refused; a malformed port is refused rather than defaulted to 443; userinfo is stripped from the host; a fragment does not end up in the host.
Parent
Rival-centralization epic: https://github.com/DIG-Network/dig_ecosystem/issues/3140