From 7c3bcc0598d8a12894b916b30d8a4882abd5fee8 Mon Sep 17 00:00:00 2001 From: michel Date: Sun, 19 Jul 2026 17:45:30 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20don't=20panic=20on=20a=20peer's=20obfusc?= =?UTF-8?q?ated=20port=20(u16)=20=E2=80=94=20widen=20the=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GetPeerAddressResponse narrowed obfuscated_port (u16) into a u8 via try_into().unwrap(), panicking for any real obfuscated port (>255) and unwinding the client's single ops-loop thread — hanging all downloads, searches and peer events. Since download() now auto-resolves peer addresses via GetPeerAddress, a download to any obfuscation-enabled peer could crash the whole client. Widen Peer::obfuscated_port to u16 and stop truncating. --- soulseek-rs-lib/src/client.rs | 9 +++++++-- soulseek-rs-lib/src/peer/mod.rs | 24 +++++++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/soulseek-rs-lib/src/client.rs b/soulseek-rs-lib/src/client.rs index 3a55f03..5822848 100644 --- a/soulseek-rs-lib/src/client.rs +++ b/soulseek-rs-lib/src/client.rs @@ -1137,8 +1137,13 @@ impl Client { port, None, 0, - obfuscation_type.try_into().unwrap(), - obfuscated_port.try_into().unwrap(), + // obfuscation_type is a small enum; a + // real obfuscated_port is a full u16 and + // must not be truncated into a u8 (which + // panicked and took down the ops thread). + u8::try_from(obfuscation_type) + .unwrap_or(0), + obfuscated_port, ); let client_context_clone = client_context.clone(); diff --git a/soulseek-rs-lib/src/peer/mod.rs b/soulseek-rs-lib/src/peer/mod.rs index f6cf6fc..7006a0b 100644 --- a/soulseek-rs-lib/src/peer/mod.rs +++ b/soulseek-rs-lib/src/peer/mod.rs @@ -89,7 +89,7 @@ pub struct Peer { pub token: Option, pub privileged: Option, pub unknown: Option, - pub obfuscated_port: Option, + pub obfuscated_port: Option, } impl Peer { #[allow(clippy::too_many_arguments, dead_code)] @@ -102,7 +102,7 @@ impl Peer { token: Option, privileged: u8, unknown: u8, - obfuscated_port: u8, + obfuscated_port: u16, ) -> Self { Self { username, @@ -146,10 +146,28 @@ impl Peer { token: Some(token), privileged: Some(privileged), unknown: Some(unknown), - obfuscated_port: Some(obfuscated_port), + obfuscated_port: Some(u16::from(obfuscated_port)), }) } } +#[test] +fn new_accepts_a_full_range_obfuscated_port() { + // A real obfuscated port is a u16; it must be stored without truncation. + // (The GetPeerAddressResponse path previously narrowed it to u8 and + // panicked for any value > 255, crashing the client's ops thread.) + let peer = Peer::new( + "u".to_string(), + ConnectionType::P, + "1.2.3.4".to_string(), + 1, + None, + 0, + 0, + 51770, + ); + assert_eq!(peer.obfuscated_port, Some(51770)); +} + #[test] fn new_from_message_returns_none_on_invalid_connection_type() { // username "ab", connection_type "X" (not P/F/D) from an untrusted server.