From c200cc81dfc1bf7b4d002ffeb310ad6ba14d150f Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 19 Sep 2026 22:22:23 +0800 Subject: [PATCH] server: test that the unauthenticated cap is on before the handshake reads hbb_common covers each transport's cap; nothing covered where the connection layer puts it. A header one byte over MAX_UNAUTHORIZED_MESSAGE, written to a connection stalled in the identity handshake, has to end the handshake on the header alone and release the connection's place: the test fails with the cap moved past the handshake or dropped. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab --- src/server/connection.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/server/connection.rs b/src/server/connection.rs index 8aaacd2cb..7ed59b40c 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -7252,6 +7252,41 @@ mod test { assert_eq!(unauthorized_count(), 0, "no handshake outlives the test"); } + // The cap is on before the identity handshake reads. A header declaring one byte over it, + // written to the wire as the codec would read it, ends the handshake on the header alone - + // the payload is neither waited for nor read - and releases the place the connection held. + #[tokio::test] + async fn test_unauthorized_frame_is_refused_on_its_header() { + use hbb_common::tokio::io::AsyncWriteExt; + let _serial = UNAUTHORIZED_TESTS.lock().unwrap_or_else(|e| e.into_inner()); + let server = crate::server::new_for_test(); + let listener = hbb_common::tcp::new_listener("127.0.0.1:0", false) + .await + .unwrap(); + let (controller, accepted) = tokio::join!( + tokio::net::TcpStream::connect(listener.local_addr().unwrap()), + listener.accept() + ); + let (mut controller, (accepted, addr)) = (controller.unwrap(), accepted.unwrap()); + let served = Stream::Tcp(hbb_common::tcp::FramedStream::from(accepted, addr)); + let handshake = tokio::spawn(async move { + crate::server::create_tcp_connection(server, served, addr, true, Default::default()) + .await + }); + let n = MAX_UNAUTHORIZED_MESSAGE + 1; + controller + .write_all(&(((n << 2) | 0x3) as u32).to_le_bytes()) + .await + .unwrap(); + match hbb_common::timeout(2000, handshake).await { + Ok(Ok(Err(e))) => assert!(e.to_string().contains("Too big packet"), "{}", e), + Ok(Ok(Ok(_))) => panic!("a frame over the cap was accepted"), + Ok(Err(e)) => panic!("the handshake task panicked: {}", e), + Err(_) => panic!("the handshake waited for a payload the header should have refused"), + } + assert_eq!(unauthorized_count(), 0); + } + #[cfg(feature = "flutter")] #[cfg(not(any(target_os = "android", target_os = "ios")))] #[test]