From 1cff291fdda9d516620c3fb2cf072575ba5a5931 Mon Sep 17 00:00:00 2001 From: ospab Date: Thu, 21 May 2026 15:15:56 +0300 Subject: [PATCH] fix: noise-read in UoT handshake (single attempt, 4s timeout); add TCP rate limiter against bots --- ostp-client/src/bridge.rs | 12 +++++++++--- ostp-server/src/lib.rs | 31 ++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/ostp-client/src/bridge.rs b/ostp-client/src/bridge.rs index 981c5b1..5b6a0a7 100644 --- a/ostp-client/src/bridge.rs +++ b/ostp-client/src/bridge.rs @@ -886,15 +886,21 @@ impl Bridge { let mut size = 0; let mut success = false; - // Retransmit handshake up to 4 times with 1200ms timeout to survive packet loss on mobile - for attempt in 0..4 { + // For UoT: TCP is reliable so we don't retry on the same connection. + // Multiple retries would cause stale Noise responses to queue in the mpsc channel + // and break the Noise state machine (noise-read error). + // For UDP: retry up to 4x with 1200ms timeout to survive packet loss. + let is_uot = matches!(socket, crate::transport::Transport::Uot { .. }); + let (attempt_limit, attempt_timeout_ms) = if is_uot { (1, 4000) } else { (4, 1200) }; + + for attempt in 0..attempt_limit { if attempt > 0 { tx.send(UiEvent::Log(format!("Handshake attempt {} lost. Retransmitting...", attempt))).await.ok(); } send_datagram(&socket, &handshake_frame, self.turn_enabled).await?; self.metrics.bytes_sent.fetch_add(handshake_frame.len() as u64, Ordering::Relaxed); - match timeout(Duration::from_millis(1200), socket.recv(&mut buf)).await { + match timeout(Duration::from_millis(attempt_timeout_ms), socket.recv(&mut buf)).await { Ok(Ok(n)) => { size = n; success = true; diff --git a/ostp-server/src/lib.rs b/ostp-server/src/lib.rs index 87fae8d..2b9a3be 100644 --- a/ostp-server/src/lib.rs +++ b/ostp-server/src/lib.rs @@ -274,12 +274,41 @@ async fn run_server_loop( let tcp_map_clone = tcp_map.clone(); let shared_keys_clone = shared_keys.clone(); let udp_tx_clone = udp_tx.clone(); - + tokio::spawn(async move { if let Ok(listener) = tokio::net::TcpListener::bind(&addr).await { tracing::info!("TCP (UoT) listener bound to {}", addr); + + // Rate limiter: track connection attempts per IP + // Map + let rate_map: std::sync::Arc>> = + std::sync::Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())); + const RATE_WINDOW_SECS: u64 = 10; + const RATE_MAX_CONNS: u32 = 10; + loop { if let Ok((stream, peer_addr)) = listener.accept().await { + // Rate limit check + let peer_ip = peer_addr.ip(); + let allowed = { + let mut map = rate_map.lock().await; + let now = std::time::Instant::now(); + let entry = map.entry(peer_ip).or_insert((0, now)); + if now.duration_since(entry.1).as_secs() >= RATE_WINDOW_SECS { + // Reset window + *entry = (1, now); + true + } else { + entry.0 += 1; + entry.0 <= RATE_MAX_CONNS + } + }; + + if !allowed { + tracing::debug!("UoT rate limit exceeded for {}, dropping connection", peer_ip); + continue; + } + let tm = tcp_map_clone.clone(); let keys = shared_keys_clone.clone(); let tx = udp_tx_clone.clone();