mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-07-07 07:44:47 +03:00
feat: peer version exchange with soft-warn UI indicator
Adds a one-shot Hello message to the lan-mouse wire protocol so each
peer can display the other end's build commit hash and warn on
version mismatch. Soft-warn only — mismatched versions never refuse
traffic.
Wire change (lan-mouse-proto)
* `ProtoEvent::Hello { commit: [u8; 8] }` carries the 8-byte ASCII
short commit from shadow_rs's `SHORT_COMMIT`. Encoded/decoded
alongside the existing event variants.
* `EventType::Hello` is appended to the enum so existing IDs are
untouched. Old peers receive the event, hit `InvalidEventId`, and
silently skip it via the forward-compat handler in
`connect.rs::receive_loop` — the connection is unaffected.
Daemon
* Connect side sends one Hello immediately after the DTLS handshake
authenticates and before the ping_pong loop starts. Best-effort,
fire-and-forget — `log::debug!` on send error.
* Listen side mirrors the peer's Hello with its own (same shape as
the existing Ping → Pong reply), so the peer's connect-side
receive_loop populates `ClientState::peer_commit` for that
handle.
* The disconnect path clears `peer_commit` so a stale hash isn't
shown after the connection drops.
IPC
* `ClientState::peer_commit: Option<[u8; 8]>`. `None` means the
peer hasn't sent Hello yet — either fresh connection or older
build that predates the event.
GTK
* `ClientObject` exposes `peer-commit` as an `Option<String>`
property; `peer_commit_to_string` converts the wire `[u8; 8]` to
the displayable hex.
* `lan_mouse_gtk::run` now takes the local commit and stashes it in
a `OnceLock` so per-row UI can compare against each peer's hash.
* `ClientRow::refresh_version_status` re-renders the collapsed
subtitle with Pango markup whenever the property changes:
- matched → green "peer version: <hex> · matched"
- mismatch → orange "peer version: <hex> · ours: <hex>"
- unknown → orange "peer version: unknown · ours: <hex>"
* Window invokes `refresh_version_status` from
`update_client_state` after writing the new property, and
`bind` calls it once on row construction so the initial
subtitle isn't blank.
Known limitation: state-change broadcasts from the network side
(set_alive / set_active_addr / set_peer_commit) don't currently
trigger a `FrontendEvent::State` directly; the UI picks up the
latest values on the next user-driven broadcast. Same pre-existing
behavior as the alive/active_addr fields.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Ferdinand Schober
parent
82766cdc87
commit
72c86c0d83
@@ -63,6 +63,15 @@ pub enum ProtoEvent {
|
||||
Ping,
|
||||
/// Response to [`ProtoEvent::Ping`], true if emulation is enabled / available
|
||||
Pong(bool),
|
||||
/// Build identification for the sending peer. Sent by the
|
||||
/// connect side once after the connection authenticates, and
|
||||
/// echoed back by the listen side in reply, so each end can
|
||||
/// display the peer's build hash and warn (soft) on mismatch.
|
||||
/// `commit` is the 8-byte ASCII short commit hash from
|
||||
/// `shadow_rs`'s `SHORT_COMMIT`. Old peers that don't
|
||||
/// recognize the event type silently skip it per the
|
||||
/// forward-compat handling in the receive loop.
|
||||
Hello { commit: [u8; 8] },
|
||||
}
|
||||
|
||||
impl Display for ProtoEvent {
|
||||
@@ -80,6 +89,10 @@ impl Display for ProtoEvent {
|
||||
if *alive { "alive" } else { "not available" }
|
||||
)
|
||||
}
|
||||
ProtoEvent::Hello { commit } => {
|
||||
let s = std::str::from_utf8(commit).unwrap_or("????????");
|
||||
write!(f, "Hello({s})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,6 +111,7 @@ pub enum EventType {
|
||||
Enter,
|
||||
Leave,
|
||||
Ack,
|
||||
Hello,
|
||||
}
|
||||
|
||||
impl ProtoEvent {
|
||||
@@ -120,6 +134,7 @@ impl ProtoEvent {
|
||||
ProtoEvent::Enter(_) => EventType::Enter,
|
||||
ProtoEvent::Leave(_) => EventType::Leave,
|
||||
ProtoEvent::Ack(_) => EventType::Ack,
|
||||
ProtoEvent::Hello { .. } => EventType::Hello,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,6 +189,13 @@ impl TryFrom<[u8; MAX_EVENT_SIZE]> for ProtoEvent {
|
||||
EventType::Enter => Ok(Self::Enter(decode_u8(&mut buf)?.try_into()?)),
|
||||
EventType::Leave => Ok(Self::Leave(decode_u32(&mut buf)?)),
|
||||
EventType::Ack => Ok(Self::Ack(decode_u32(&mut buf)?)),
|
||||
EventType::Hello => {
|
||||
let mut commit = [0u8; 8];
|
||||
for b in commit.iter_mut() {
|
||||
*b = decode_u8(&mut buf)?;
|
||||
}
|
||||
Ok(Self::Hello { commit })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,6 +260,11 @@ impl From<ProtoEvent> for ([u8; MAX_EVENT_SIZE], usize) {
|
||||
ProtoEvent::Enter(pos) => encode_u8(buf, len, pos as u8),
|
||||
ProtoEvent::Leave(serial) => encode_u32(buf, len, serial),
|
||||
ProtoEvent::Ack(serial) => encode_u32(buf, len, serial),
|
||||
ProtoEvent::Hello { commit } => {
|
||||
for b in commit.iter() {
|
||||
encode_u8(buf, len, *b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(buf, len)
|
||||
|
||||
Reference in New Issue
Block a user