From 8fe4e72bb38228541e5e40b31286e95036666455 Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Tue, 31 Mar 2026 15:47:40 +0400 Subject: [PATCH] Fix TCP multi-conn auth: secondary connections skip auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Secondary connections send [TOKEN_HI, TOKEN_LO, 0x02, 0x00, ...] as their command — they don't do auth. Server verifies the session token matches a pending session from the same IP, sends OK with token, and lets them join. No auth challenge/response needed. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/server.rs | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/server.rs b/src/server.rs index 7f87876..aae0f20 100644 --- a/src/server.rs +++ b/src/server.rs @@ -70,43 +70,37 @@ async fn handle_client( 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. + // Check if this is a secondary TCP connection joining a session. + // Secondary connections send the session token in bytes 0-1 of their "command": + // [TOKEN_HI, TOKEN_LO, 0x02, 0x00, ...] + // They do NOT do auth — just send them AUTH_OK with the token and they join. { - 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() { + let mut map = sessions.lock().await; + let received_token = ((cmd_buf[0] as u16) << 8) | (cmd_buf[1] as u16); + if let Some(session) = map.get_mut(&received_token) { 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, + "Client {} is secondary TCP connection (token={:04x})", + peer, received_token, ); - 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?; + // No auth for secondary connections — just send OK with token + let ok = [0x01, cmd_buf[0], cmd_buf[1], 0x00]; + stream.write_all(&ok).await?; + stream.flush().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(()); - } - } + session.streams.push(stream); + tracing::info!( + "Secondary connection joined ({}/{})", + session.streams.len() + 1, + session.expected, + ); return Ok(()); } } + drop(map); } // Primary connection: parse the command normally