From 9fb07a119f2422d472a58d189b72af83bcc69ef8 Mon Sep 17 00:00:00 2001 From: Jon Kinney Date: Tue, 5 May 2026 01:18:32 -0500 Subject: [PATCH] fix(version-exchange): also store peer commit on the listen side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the Hello handler in `ListenTask` echoed our local commit back but deliberately threw away the peer's, on the assumption that the outgoing connect-side path (`connect.rs:278-279` → `set_peer_commit`) would always populate the visible state for any bidirectionally-configured peer. That assumption breaks any time the *outgoing* TCP/DTLS direction is broken even though the inbound direction is fine — happened just now when the peer Mac's daemon stopped listening on 4242 (DHCP-renewed IP, daemon crashed, asymmetric NAT, …). Mac was still happily connecting in the other direction and sending events, including the initial Hello, but Linux silently displayed "peer version unknown" because the listen side dropped Mac's commit on the floor. Add a `PeerHello { addr, commit }` EmulationEvent variant fired from the listen-side Hello handler. The service maps `addr → ClientHandle` via `client_manager.get_client(addr)` and calls `set_peer_commit` + `broadcast_client` exactly like the connect path does. The connect path remains the primary source for symmetric setups; this is the defensive fallback so version visibility doesn't depend on outbound reachability. Skips silently when no outgoing client is configured for the peer's addr (incoming-only setup) — there's no UI row to update in that case anyway. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/emulation.rs | 36 ++++++++++++++++++++++++++---------- src/service.rs | 11 +++++++++++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/emulation.rs b/src/emulation.rs index d2ff60f..923bf99 100644 --- a/src/emulation.rs +++ b/src/emulation.rs @@ -53,6 +53,17 @@ pub(crate) enum EmulationEvent { EmulationEnabled, /// capture should be released ReleaseNotify, + /// peer sent us a Hello with its build commit hash. Used to + /// populate `client_manager.peer_commit` from the listen side + /// too — without this, peer-version visibility silently fails + /// whenever the outgoing connection in the *other* direction is + /// broken (one-way setups, asymmetric NAT, peer's TCP listener + /// down). The connect-side path stays as the primary source; + /// this is the defensive fallback. + PeerHello { + addr: SocketAddr, + commit: [u8; 8], + }, } enum EmulationRequest { @@ -151,16 +162,21 @@ impl ListenTask { } ProtoEvent::Input(event) => self.emulation_proxy.consume(event, addr), ProtoEvent::Ping => self.listener.reply(addr, ProtoEvent::Pong(self.emulation_proxy.emulation_active.get())).await, - // Mirror the peer's version handshake. The - // outgoing connect side initiated this with - // its own Hello; we echo ours back so the - // peer's connect-side receive_loop can - // populate `peer_commit`. We don't store - // the peer's commit on the listen side — - // the user-visible state lives on outgoing - // connections, where the same peer is also - // configured as a `[[clients]]` entry. - ProtoEvent::Hello { .. } => self.listener.reply(addr, ProtoEvent::Hello { commit: local_commit() }).await, + // Peer's version handshake. Echo our own + // commit back so the peer's connect-side + // receive_loop populates its `peer_commit`, + // AND publish a PeerHello upward so our + // service can populate ours from the listen + // side too — the connect side is the primary + // path, but if the outbound direction is + // broken (one-way setup, NAT, peer's TCP + // listener down) the version display would + // otherwise silently say "unknown" while + // the peer is in fact happily talking to us. + ProtoEvent::Hello { commit } => { + self.listener.reply(addr, ProtoEvent::Hello { commit: local_commit() }).await; + self.event_tx.send(EmulationEvent::PeerHello { addr, commit }).expect("channel closed"); + } _ => {} } } diff --git a/src/service.rs b/src/service.rs index d0772f6..46f3693 100644 --- a/src/service.rs +++ b/src/service.rs @@ -319,6 +319,17 @@ impl Service { EmulationEvent::Connected { addr, fingerprint } => { self.notify_frontend(FrontendEvent::DeviceConnected { addr, fingerprint }); } + EmulationEvent::PeerHello { addr, commit } => { + // Map the peer's source addr back to its client handle + // and stamp the commit. Skip if we don't have an + // outgoing client configured for this peer (incoming- + // only setup) — there's nowhere to display the version + // in that case anyway. + if let Some(handle) = self.client_manager.get_client(addr) { + self.client_manager.set_peer_commit(handle, Some(commit)); + self.broadcast_client(handle); + } + } } }