backpressures fixes and recieve/send sync
This commit is contained in:
@@ -16,11 +16,10 @@ use crate::{
|
||||
rawcast::{LocalProtocol, RawCastAdapter, RawCastFrame},
|
||||
tlseng::{BrowserProfile, ServerProfile},
|
||||
};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use bytes::{Buf, Bytes, BytesMut};
|
||||
use dashmap::DashMap;
|
||||
use netrunner_logger::{debug, error, info, warn};
|
||||
use rand::Rng;
|
||||
use socket2::{Domain, Protocol, Socket, Type};
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::{
|
||||
@@ -95,20 +94,21 @@ impl ClientHandler {
|
||||
let mut addrs = tokio::net::lookup_host(remote_proxy_addr)
|
||||
.await
|
||||
.map_err(|e| format!("DNS resolution failed: {}", e))?;
|
||||
|
||||
let addr = addrs
|
||||
.next()
|
||||
.ok_or_else(|| format!("No IPs found for {}", remote_proxy_addr))?;
|
||||
|
||||
let domain = if addr.is_ipv4() {
|
||||
Domain::IPV4
|
||||
// 🔥 ФИКС: Используем Tokio TcpSocket вместо socket2.
|
||||
// Это позволяет корректно отменять подключение при зависании ОС.
|
||||
let socket = if addr.is_ipv4() {
|
||||
tokio::net::TcpSocket::new_v4()
|
||||
} else {
|
||||
Domain::IPV6
|
||||
};
|
||||
let socket =
|
||||
Socket::new(domain, Type::STREAM, Some(Protocol::TCP)).map_err(|e| e.to_string())?;
|
||||
tokio::net::TcpSocket::new_v6()
|
||||
}
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
socket.set_nonblocking(true).map_err(|e| e.to_string())?;
|
||||
socket.set_nodelay(true).map_err(|e| e.to_string())?;
|
||||
socket.set_nodelay(true).unwrap_or_default();
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
@@ -117,7 +117,7 @@ impl ClientHandler {
|
||||
target_os = "macos"
|
||||
))]
|
||||
unsafe {
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
let lowat: libc::c_int = 16384;
|
||||
let _ = libc::setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
@@ -128,9 +128,12 @@ impl ClientHandler {
|
||||
);
|
||||
}
|
||||
|
||||
let _ = socket.connect(&addr.into());
|
||||
let std_stream: std::net::TcpStream = socket.into();
|
||||
let stream = TcpStream::from_std(std_stream).map_err(|e| e.to_string())?;
|
||||
// 🔥 ФИКС: Добавляем Timeout на подключение (5 секунд)!
|
||||
// Если при смене сети пакеты идут в никуда, мы быстро прерываем попытку и пробуем снова.
|
||||
let stream = tokio::time::timeout(std::time::Duration::from_secs(5), socket.connect(addr))
|
||||
.await
|
||||
.map_err(|_| "Connection timed out (Network changed?)".to_string())?
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let mut conn = Connection::new(stream);
|
||||
let mut session_keys = SessionKeys::new(true);
|
||||
@@ -151,13 +154,18 @@ impl ClientHandler {
|
||||
break;
|
||||
}
|
||||
Ok(None) => {
|
||||
let n = conn
|
||||
.inbound
|
||||
.read_buf(&mut conn.read_buf)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
if n == 0 {
|
||||
return Err(format!("EOF on {}", leg_name));
|
||||
// Также добавляем Timeout на чтение ответа от сервера
|
||||
let res = tokio::time::timeout(
|
||||
std::time::Duration::from_secs(5),
|
||||
conn.inbound.read_buf(&mut conn.read_buf),
|
||||
)
|
||||
.await;
|
||||
|
||||
match res {
|
||||
Ok(Ok(0)) => return Err(format!("EOF on {}", leg_name)),
|
||||
Ok(Ok(_)) => continue,
|
||||
Ok(Err(e)) => return Err(format!("Read error: {}", e)),
|
||||
Err(_) => return Err("Handshake read timeout".into()),
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(format!("TLS error on {}: {:?}", leg_name, e)),
|
||||
@@ -221,7 +229,7 @@ impl ClientHandler {
|
||||
tokio::time::sleep(LEG_STAGGER_DELAY * id).await;
|
||||
loop {
|
||||
if let Err(e) = Self::establish_leg(&addr, id, m.clone(), &sid).await {
|
||||
error!("Leg {} disconnected: {}. Reconnecting in 3s...", id, e);
|
||||
error!("Leg {} disconnected: {}. Reconnecting in 2s...", id, e);
|
||||
tokio::time::sleep(LEG_RECONNECT_DELAY).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ impl IdGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 Клонируем сообщения для Retry-логики
|
||||
#[derive(Clone)]
|
||||
pub struct MuxMessage {
|
||||
pub stream_id: u32,
|
||||
@@ -108,8 +107,6 @@ impl Muxer {
|
||||
|
||||
pub fn remove_leg(&self, leg_id: u32) {
|
||||
self.legs.remove(&leg_id);
|
||||
// Мы не чистим stream_bindings полностью,
|
||||
// балансировщик сам переназначит мертвые привязки
|
||||
info!(
|
||||
leg_id,
|
||||
"MUXER: Leg removed, streams will re-balance dynamically"
|
||||
@@ -125,7 +122,6 @@ impl Muxer {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Если привязка есть и нога жива — используем её
|
||||
if let Some(leg_id_ref) = self.stream_bindings.get(&stream_id) {
|
||||
let leg_id = *leg_id_ref;
|
||||
if let Some(leg) = self.legs.get(&leg_id) {
|
||||
@@ -133,8 +129,6 @@ impl Muxer {
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 ФИКС: Убрано разделение на TCP/UDP ноги.
|
||||
// Все физические ноги у нас TCP, поэтому мы балансируем трафик по всем ногам!
|
||||
let mut candidates: Vec<(u32, MuxLeg)> = self
|
||||
.legs
|
||||
.iter()
|
||||
@@ -155,7 +149,6 @@ impl Muxer {
|
||||
let pool_size = std::cmp::min(candidates.len(), MUXER_POOL_SIZE);
|
||||
let (selected_id, selected_leg) = candidates[stream_id as usize % pool_size].clone();
|
||||
|
||||
// Обновляем привязку
|
||||
self.stream_bindings.insert(stream_id, selected_id);
|
||||
|
||||
Some((selected_id, selected_leg))
|
||||
@@ -185,7 +178,6 @@ impl Muxer {
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 ФИКС: Добавлена логика повторной отправки (Retry) при обрыве связи!
|
||||
pub async fn send_to_network(&self, message: MuxMessage) -> Result<(), String> {
|
||||
let size = message.data.len() as u64;
|
||||
let mut attempts = 0;
|
||||
@@ -209,21 +201,42 @@ impl Muxer {
|
||||
_ => &leg.data_tx,
|
||||
};
|
||||
|
||||
if target_tx.send(message.clone()).await.is_err() {
|
||||
// Нога умерла! Выкидываем её и пробуем отправить пакет в ДРУГУЮ ногу (continue)
|
||||
self.remove_leg(leg_id);
|
||||
continue;
|
||||
// 🔥 ФИКС: Если канал данных забит, дропаем пакет, чтобы не ломать туннель
|
||||
match target_tx.try_send(message.clone()) {
|
||||
Ok(_) => {
|
||||
leg.stats.tx_bytes.fetch_add(size, Ordering::Relaxed);
|
||||
if let Some(stream_ref) = self.streams.get(&message.stream_id) {
|
||||
stream_ref
|
||||
.value()
|
||||
.1
|
||||
.tx_bytes
|
||||
.fetch_add(size, Ordering::Relaxed);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
|
||||
if matches!(message.frame_type, FrameType::Data | FrameType::UdpData) {
|
||||
// Канал данных физически забит (слабый интернет). СБРАСЫВАЕМ пакет.
|
||||
// TCP smoltcp на клиенте не получит ACK и сделает ретрансмиссию.
|
||||
trace!(
|
||||
message.stream_id,
|
||||
"Physical TX full! Dropped packet to prevent deadlock."
|
||||
);
|
||||
return Ok(());
|
||||
} else {
|
||||
// Контрольные фреймы (Connect, Close, Ping) обязательны к доставке.
|
||||
if target_tx.send(message.clone()).await.is_err() {
|
||||
self.remove_leg(leg_id);
|
||||
continue;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
|
||||
self.remove_leg(leg_id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
leg.stats.tx_bytes.fetch_add(size, Ordering::Relaxed);
|
||||
if let Some(stream_ref) = self.streams.get(&message.stream_id) {
|
||||
stream_ref
|
||||
.value()
|
||||
.1
|
||||
.tx_bytes
|
||||
.fetch_add(size, Ordering::Relaxed);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +281,6 @@ impl Muxer {
|
||||
pub fn remove_stream(&self, stream_id: u32) {
|
||||
self.streams.remove(&stream_id);
|
||||
self.stream_bindings.remove(&stream_id);
|
||||
trace!(stream_id, "MUXER: Stream and bindings removed");
|
||||
}
|
||||
|
||||
pub async fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
|
||||
@@ -276,12 +288,26 @@ impl Muxer {
|
||||
.streams
|
||||
.get(&stream_id)
|
||||
.map(|s| (s.value().0.clone(), s.value().1.clone()));
|
||||
|
||||
if let Some((tx, stats)) = stream_opt {
|
||||
let size = data.len() as u64;
|
||||
if tx.send(data).await.is_err() {
|
||||
self.remove_stream(stream_id);
|
||||
} else {
|
||||
stats.rx_bytes.fetch_add(size, Ordering::Relaxed);
|
||||
|
||||
// 🔥 ФИКС: Используем try_send вместо await!
|
||||
// Если локальный сокет тупит, а очередь полна (2MB), мы сбрасываем пакет.
|
||||
// Внешний сервер увидит потерю пакета и замедлит передачу.
|
||||
match tx.try_send(data) {
|
||||
Ok(_) => {
|
||||
stats.rx_bytes.fetch_add(size, Ordering::Relaxed);
|
||||
}
|
||||
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
|
||||
trace!(
|
||||
stream_id,
|
||||
"Local RX queue full. Dropped packet for inner-TCP backpressure."
|
||||
);
|
||||
}
|
||||
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
|
||||
self.remove_stream(stream_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,9 +352,7 @@ impl Muxer {
|
||||
}
|
||||
|
||||
match tokio::time::timeout(HEALTH_CHECK_TIMEOUT, probe_rx.recv()).await {
|
||||
Ok(Some(_)) => {
|
||||
debug!(leg_id, "✅ Leg Health Check OK");
|
||||
}
|
||||
Ok(Some(_)) => debug!(leg_id, "✅ Leg Health Check OK"),
|
||||
_ => {
|
||||
warn!(leg_id, "❌ Leg Health Check Timeout - Evicting leg");
|
||||
self.remove_leg(leg_id);
|
||||
|
||||
+15
-20
@@ -1,37 +1,32 @@
|
||||
use std::time::Duration;
|
||||
|
||||
// --- Лимиты системы ---
|
||||
pub const MAX_SOCKETS: usize = 2048; // Лимит сокетов в SocketSet (Anti-DDoS)
|
||||
pub const MAX_TUNNEL_LEGS: u32 = 6; // Максимальное кол-во физических соединений (Legs)
|
||||
pub const MUXER_POOL_SIZE: usize = 3; // Из скольких лучших Leg-ов выбирать для балансировки
|
||||
pub const MAX_SOCKETS: usize = 2048;
|
||||
pub const MAX_TUNNEL_LEGS: u32 = 6;
|
||||
pub const MUXER_POOL_SIZE: usize = 3;
|
||||
|
||||
// --- Таймауты ---
|
||||
pub const TCP_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(20); // Время на установку SYN/ACK
|
||||
pub const UDP_IDLE_TIMEOUT: Duration = Duration::from_secs(60); // Смерть UDP сессии без данных
|
||||
pub const GLOBAL_IDLE_TIMEOUT: Duration = Duration::from_secs(120); // Очистка Tracker-ом
|
||||
pub const HEALTH_CHECK_INTERVAL: Duration = Duration::from_secs(7); // Частота пинга Leg-ов
|
||||
pub const HEALTH_CHECK_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
pub const LEG_RECONNECT_DELAY: Duration = Duration::from_secs(3); // Пауза перед реконнектом Leg
|
||||
pub const BRIDGE_IDLE_TIMEOUT: Duration = Duration::from_secs(30); // Таймаут задач-бриджей
|
||||
pub const TCP_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(20);
|
||||
pub const UDP_IDLE_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
pub const GLOBAL_IDLE_TIMEOUT: Duration = Duration::from_secs(120);
|
||||
|
||||
// 🔥 ФИКС: Ускоряем обнаружение мертвой сети при переключении Wi-Fi -> LTE
|
||||
pub const HEALTH_CHECK_INTERVAL: Duration = Duration::from_secs(3); // Было 7
|
||||
pub const HEALTH_CHECK_TIMEOUT: Duration = Duration::from_secs(4); // Было 10
|
||||
pub const LEG_RECONNECT_DELAY: Duration = Duration::from_secs(2); // Было 3
|
||||
pub const BRIDGE_IDLE_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
// --- Сетевые порты ---
|
||||
pub const DNS_PORT: u16 = 53;
|
||||
pub const HTTP_PORT: u16 = 80;
|
||||
pub const HTTPS_PORT: u16 = 443;
|
||||
pub const NETBIOS_PORTS: [u16; 2] = [137, 138];
|
||||
|
||||
// --- Настройки безопасности ---
|
||||
pub const AUTH_TIME_STEP: u64 = 60; // Шаг генерации токена (секунды)
|
||||
pub const AUTH_WINDOW_SIZE: u64 = 2; // Допуск шагов времени (current +/- 2)
|
||||
pub const AUTH_TIME_STEP: u64 = 60;
|
||||
pub const AUTH_WINDOW_SIZE: u64 = 2;
|
||||
|
||||
// --- Настройки Multipath (Legs) ---
|
||||
pub const LEG_STAGGER_DELAY: Duration = Duration::from_millis(1500);
|
||||
pub const LEG_STAGGER_DELAY: Duration = Duration::from_millis(1000); // Чуть ускорили старт
|
||||
pub const TOPOLOGY_PRINT_INTERVAL: Duration = Duration::from_secs(15);
|
||||
|
||||
// --- Настройки Stealth Fallback (Маскировка) ---
|
||||
pub const STEALTH_FALLBACK_HOST: &str = "ubuntu.com:443";
|
||||
pub const FALLBACK_CONNECT_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
// --- Таймауты Handshake ---
|
||||
pub const TLS_HELLO_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
pub const SECURE_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(20);
|
||||
|
||||
Reference in New Issue
Block a user