aboutsummaryrefslogtreecommitdiffstats
path: root/src/transport/wire.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-06 04:35:12 +0100
committertv <tv@krebsco.de>2026-11-06 04:35:12 +0100
commit9d6d7913ce8bf76717f75d7d4845a61f4889da80 (patch)
tree463bf53102c66f002b760002e86c7ef94ce2caa5 /src/transport/wire.rs
parentceeec6f661e13a2d9fc7d28f88450f6d45af5c43 (diff)
other wire versions warned
Diffstat (limited to 'src/transport/wire.rs')
-rw-r--r--src/transport/wire.rs46
1 files changed, 46 insertions, 0 deletions
diff --git a/src/transport/wire.rs b/src/transport/wire.rs
index 769545c..ef692b3 100644
--- a/src/transport/wire.rs
+++ b/src/transport/wire.rs
@@ -90,6 +90,38 @@ pub(crate) fn encode(message: &ParsedMessage) -> Result<Vec<u8>, WireError> {
}
/// The message in `payload` (as made by [`encode`]).
+/// How many senders on another wire version are remembered as already logged
+/// (`OtherVersions`).
+const MAX_OTHER_VERSIONS: usize = 1024;
+
+/// Senders whose messages are of another wire version (`WireError::Version`): each
+/// logged once, at warn level, so two builds that don't speak to each other say so,
+/// instead of silently retrying their HELLOs for good. Bounded: anyone can send us
+/// anything.
+#[derive(Default)]
+pub(crate) struct OtherVersions(std::sync::Mutex<std::collections::HashSet<String>>);
+
+impl OtherVersions {
+ /// A message from `sender` (on the transport `scheme`) was of `version`.
+ pub(crate) fn note(&self, scheme: &'static str, sender: &str, version: u8) {
+ let mut known = self
+ .0
+ .lock()
+ .unwrap_or_else(|poisoned| poisoned.into_inner());
+ if known.len() >= MAX_OTHER_VERSIONS {
+ known.clear();
+ }
+ if known.insert(sender.to_owned()) {
+ tracing::warn!(
+ from = %super::Addressed(scheme, &sender),
+ version,
+ ours = VERSION,
+ "Peer speaks another wire version (another build); ignoring its messages"
+ );
+ }
+ }
+}
+
pub(crate) fn decode(payload: &[u8]) -> Result<ParsedMessage, WireError> {
let (&version, payload) = payload.split_first().ok_or(WireError::Empty)?;
if version != VERSION {
@@ -492,6 +524,20 @@ mod tests {
ListedPeer, MAX_LISTED_HEADS, Status,
};
+ /// Each sender on another wire version is remembered once, and no more than
+ /// `MAX_OTHER_VERSIONS` of them.
+ #[test]
+ fn other_versions_are_noted_once_each() {
+ let other = OtherVersions::default();
+ other.note("s", "a", VERSION + 1);
+ other.note("s", "a", VERSION + 1);
+ assert_eq!(other.0.lock().unwrap().len(), 1);
+ for i in 0..MAX_OTHER_VERSIONS + 1 {
+ other.note("s", &i.to_string(), VERSION + 1);
+ }
+ assert!(other.0.lock().unwrap().len() <= MAX_OTHER_VERSIONS);
+ }
+
/// Every kind decodes to what was encoded: re-encoded, the same bytes.
#[test]
fn every_kind_round_trips() {