Peering client dials port 443 for plain-HTTP peer URLs without an explicit port #140

Closed
opened 2026-08-09 00:43:56 +00:00 by Cordy · 0 comments
Owner

Found live during the first real A↔B dogfood transfer (#106). Sending from cairn-enc to a peer registered as http://192.168.10.247 fails:

peering: hello: rpc error: code = Unavailable desc = connection error:
desc = "transport: Error while dialing: dial tcp 192.168.10.247:443: connect: no route to host"

Root cause: Client.dial passes u.Host straight to grpc.NewClient. For a URL without an explicit port, u.Host has no port, and gRPC's default resolver appends 443 regardless of scheme — the insecure-credentials branch changes the transport security but not the target port. So every plain-HTTP LAN peer (exactly the case isPrivateHost deliberately permits) dials 443 unless the admin happens to write :80.

Why tests missed it: the loopback test injects Client.Dial (in-process listener), so the URL→target derivation was never exercised against a real address.

Fix: derive the target explicitly — port from the URL if present, else 80 for http and 443 for https — in a small grpcTarget(u) helper with a unit test over the four cases (both schemes × with/without explicit port).

Same patch transparently fixes the reverse direction (beta→enc) and any future LAN peer.

Found live during the first real A↔B dogfood transfer (#106). Sending from `cairn-enc` to a peer registered as `http://192.168.10.247` fails: ``` peering: hello: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 192.168.10.247:443: connect: no route to host" ``` **Root cause:** `Client.dial` passes `u.Host` straight to `grpc.NewClient`. For a URL without an explicit port, `u.Host` has no port, and gRPC's default resolver appends **443 regardless of scheme** — the insecure-credentials branch changes the transport security but not the target port. So every plain-HTTP LAN peer (exactly the case `isPrivateHost` deliberately permits) dials 443 unless the admin happens to write `:80`. **Why tests missed it:** the loopback test injects `Client.Dial` (in-process listener), so the URL→target derivation was never exercised against a real address. **Fix:** derive the target explicitly — port from the URL if present, else 80 for `http` and 443 for `https` — in a small `grpcTarget(u)` helper with a unit test over the four cases (both schemes × with/without explicit port). Same patch transparently fixes the reverse direction (beta→enc) and any future LAN peer.
Cordy closed this issue 2026-08-09 00:46:18 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#140
No description provided.