From 9853d74c4a9c1e07d7fe8fcd20b6da37b4d87fd5 Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Tue, 31 Mar 2026 15:44:51 +0400 Subject: [PATCH] Debug TCP multi-conn: log raw bytes from secondary connections Secondary connections were rejected at recv_command with "Invalid command" because they don't send a standard 16-byte command. Now we read raw bytes first, check if there's a pending session from the same IP, and handle secondary connections before validating the command format. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/server.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/server.rs b/src/server.rs index cd6332e..7f87876 100644 --- a/src/server.rs +++ b/src/server.rs @@ -65,7 +65,56 @@ async fn handle_client( send_hello(&mut stream).await?; - let cmd = recv_command(&mut stream).await?; + // Read 16-byte command (or whatever the client sends) + let mut cmd_buf = [0u8; 16]; + stream.read_exact(&mut cmd_buf).await?; + tracing::debug!("Raw command from {}: {:02x?}", peer, cmd_buf); + + // Check if this looks like a secondary TCP connection joining a session. + // Secondary connections send the session token in bytes 1-2 of what looks + // like a command, but with different/invalid proto+direction values. + { + let map = sessions.lock().await; + // Try to match session token from bytes - secondary connections may + // send the token we gave them in the auth OK response + for (&token, session) in map.iter() { + if session.peer_ip == peer.ip() + && session.streams.len() < session.expected as usize + { + tracing::info!( + "Client {} is secondary TCP connection, raw={:02x?}", + peer, cmd_buf, + ); + drop(map); + + // Secondary connection: authenticate and join session + auth::server_authenticate( + &mut stream, + auth_user.as_deref(), + auth_pass.as_deref(), + &[0x01, (token >> 8) as u8, (token & 0xFF) as u8, 0x00], + ) + .await?; + + let mut map = sessions.lock().await; + for (_t, s) in map.iter_mut() { + if s.peer_ip == peer.ip() && s.streams.len() < s.expected as usize { + s.streams.push(stream); + tracing::info!("Secondary connection joined ({}/{})", s.streams.len() + 1, s.expected); + return Ok(()); + } + } + return Ok(()); + } + } + } + + // Primary connection: parse the command normally + let cmd = Command::deserialize(&cmd_buf); + if cmd.proto > 1 || cmd.direction == 0 || cmd.direction > 3 { + return Err(BtestError::InvalidCommand); + } + tracing::info!( "Client {} command: proto={} dir={} conn_count={} tx_size={} remote_speed={} local_speed={}", peer,