stable connection

This commit is contained in:
2026-04-03 18:53:13 +07:00
parent dfac8ebc23
commit 2db4ba1e9d
14 changed files with 125 additions and 204 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ impl SessionManager {
cache_dir: String, cache_dir: String,
) -> Arc<Session> { ) -> Arc<Session> {
netrunner_logger::Logger::init(None); netrunner_logger::Logger::init(None);
netrunner_logger::Logger::global().set_level("info"); // Поменял на info для дебага netrunner_logger::Logger::global().set_level("info");
let runtime = get_runtime(); let runtime = get_runtime();
let cancel_token = CancellationToken::new(); let cancel_token = CancellationToken::new();
+1 -11
View File
@@ -209,7 +209,6 @@ impl TcpConnection {
tx_tunnel: mpsc::Sender<RawCastFrame>, tx_tunnel: mpsc::Sender<RawCastFrame>,
) { ) {
tokio::spawn(async move { tokio::spawn(async move {
// 1. Формируем и отправляем кадр на установку соединения
let mut frame = RawCastFrame::connect(LocalProtocol::Tcp, socket_id, dst_ip, dst_port); let mut frame = RawCastFrame::connect(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
frame.payload = Bytes::from(target); frame.payload = Bytes::from(target);
@@ -218,10 +217,8 @@ impl TcpConnection {
return; return;
} }
// 2. Сигнализируем ConnectionManager, что запрос в туннель ушел успешно
let _ = handshake_tx.send(()); let _ = handshake_tx.send(());
// 3. Цикл пересылки данных из smoltcp -> туннель
while let Some(data) = rx_smol.recv().await { while let Some(data) = rx_smol.recv().await {
let data_frame = RawCastFrame::data( let data_frame = RawCastFrame::data(
LocalProtocol::Tcp, LocalProtocol::Tcp,
@@ -232,7 +229,7 @@ impl TcpConnection {
); );
if tx_tunnel.send(data_frame).await.is_err() { if tx_tunnel.send(data_frame).await.is_err() {
break; // Туннель закрыт break;
} }
} }
@@ -328,7 +325,6 @@ impl UdpConnection {
tokio::spawn(async move { tokio::spawn(async move {
debug!("📡 [UDP {}] Task started for {}", socket_id, target); debug!("📡 [UDP {}] Task started for {}", socket_id, target);
// 1. Регистрация UDP сессии в туннеле
let mut frame = RawCastFrame::connect(LocalProtocol::Udp, socket_id, dst_ip, dst_port); let mut frame = RawCastFrame::connect(LocalProtocol::Udp, socket_id, dst_ip, dst_port);
frame.payload = Bytes::from(target); frame.payload = Bytes::from(target);
@@ -337,8 +333,6 @@ impl UdpConnection {
return; return;
} }
// 2. Цикл пересылки пакетов
// rx_smol отдает кортеж (данные, ip, порт)
while let Some((data, ip, port)) = rx_smol.recv().await { while let Some((data, ip, port)) = rx_smol.recv().await {
let data_frame = let data_frame =
RawCastFrame::data(LocalProtocol::Udp, socket_id, ip, port, data.to_vec()); RawCastFrame::data(LocalProtocol::Udp, socket_id, ip, port, data.to_vec());
@@ -366,12 +360,9 @@ impl IcmpResponder {
return; return;
} }
// 1. Сначала достаем данные и адрес
let result = socket.recv(); let result = socket.recv();
if let Ok((data, src_addr)) = result { if let Ok((data, src_addr)) = result {
// 2. Копируем данные в Vec, чтобы разорвать связь с буфером сокета.
// Теперь заимствование `socket` от метода .recv() закончилось.
let payload = data.to_vec(); let payload = data.to_vec();
match src_addr { match src_addr {
@@ -381,7 +372,6 @@ impl IcmpResponder {
} }
} }
// Принимаем Vec<u8>, чтобы не делать to_vec() второй раз внутри
fn reply_v4(socket: &mut icmp::Socket, mut payload: Vec<u8>, src: IpAddress) { fn reply_v4(socket: &mut icmp::Socket, mut payload: Vec<u8>, src: IpAddress) {
if let Ok(pkt) = Icmpv4Packet::new_checked(&payload) { if let Ok(pkt) = Icmpv4Packet::new_checked(&payload) {
if pkt.msg_type() == Icmpv4Message::EchoRequest { if pkt.msg_type() == Icmpv4Message::EchoRequest {
-3
View File
@@ -162,7 +162,6 @@ impl DnsHandler {
.set_recursion_available(true) .set_recursion_available(true)
.add_query(query.clone()); .add_query(query.clone());
// 1. Проверка блокировок
if self.forbidden_suffixes.iter().any(|s| name.ends_with(s)) if self.forbidden_suffixes.iter().any(|s| name.ends_with(s))
|| self.block_list.contains(&name) || self.block_list.contains(&name)
{ {
@@ -171,7 +170,6 @@ impl DnsHandler {
return res.to_vec().ok(); return res.to_vec().ok();
} }
// 2. Генерация Fake IP для A-записей
if query.query_type() == RecordType::A { if query.query_type() == RecordType::A {
let fake_ip = store.get_or_assign(&name); let fake_ip = store.get_or_assign(&name);
res.add_answer(Record::from_rdata( res.add_answer(Record::from_rdata(
@@ -181,7 +179,6 @@ impl DnsHandler {
)); ));
res.set_response_code(ResponseCode::NoError); res.set_response_code(ResponseCode::NoError);
} else { } else {
// Для остальных типов записей (AAAA и т.д.) просто возвращаем пустой NoError или NXDomain
res.set_response_code(ResponseCode::NoError); res.set_response_code(ResponseCode::NoError);
} }
+1 -4
View File
@@ -289,7 +289,7 @@ impl EngineConfig {
pub struct EngineBuilder { pub struct EngineBuilder {
config: EngineConfig, config: EngineConfig,
tun_device: Option<Tun>, tun_device: Option<Tun>,
// Добавляем возможность прокинуть кастомную фабрику сокетов
socket_factory: Option<Arc<dyn SocketProvider>>, socket_factory: Option<Arc<dyn SocketProvider>>,
} }
@@ -344,13 +344,10 @@ impl EngineBuilder {
.map_err(|e| format!("Failed to establish secure tunnel: {}", e))?; .map_err(|e| format!("Failed to establish secure tunnel: {}", e))?;
let factory = self.socket_factory.unwrap_or_else(|| { let factory = self.socket_factory.unwrap_or_else(|| {
// 1. Разыменовываем ссылку и клонируем данные в новый объект
let config_owned = (*NetworkConfig::global()).clone(); let config_owned = (*NetworkConfig::global()).clone();
// 2. Оборачиваем во владеющий Arc
let config = Arc::new(config_owned); let config = Arc::new(config_owned);
// Теперь типы совпадают: Arc<NetworkConfig> -> SmolSocketFactory::new
Arc::new(SmolSocketFactory::new(config)) Arc::new(SmolSocketFactory::new(config))
}); });
+6 -9
View File
@@ -19,7 +19,7 @@ pub struct SessionTracker {
active_udp: HashMap<SocketHandle, UdpConnection>, active_udp: HashMap<SocketHandle, UdpConnection>,
inbound_tx: HashMap<u64, mpsc::Sender<Bytes>>, inbound_tx: HashMap<u64, mpsc::Sender<Bytes>>,
handle_to_id: HashMap<SocketHandle, u64>, handle_to_id: HashMap<SocketHandle, u64>,
// 🔥 ДОБАВЛЕНО: Обратный маппинг, чтобы находить сокет по ID из туннеля
id_to_handle: HashMap<u64, SocketHandle>, id_to_handle: HashMap<u64, SocketHandle>,
pending_tcp: HashMap<SocketHandle, StdInstant>, pending_tcp: HashMap<SocketHandle, StdInstant>,
to_remove: Vec<SocketHandle>, to_remove: Vec<SocketHandle>,
@@ -34,7 +34,7 @@ impl SessionTracker {
active_udp: HashMap::new(), active_udp: HashMap::new(),
inbound_tx: HashMap::new(), inbound_tx: HashMap::new(),
handle_to_id: HashMap::new(), handle_to_id: HashMap::new(),
id_to_handle: HashMap::new(), // Инициализация id_to_handle: HashMap::new(),
pending_tcp: HashMap::new(), pending_tcp: HashMap::new(),
to_remove: Vec::new(), to_remove: Vec::new(),
next_socket_id: 1, next_socket_id: 1,
@@ -60,10 +60,10 @@ impl SessionTracker {
) { ) {
self.pending_tcp.remove(&handle); self.pending_tcp.remove(&handle);
self.handle_to_id.insert(handle, id); self.handle_to_id.insert(handle, id);
self.id_to_handle.insert(id, handle); // 🔥 Регистрация обратного индекса self.id_to_handle.insert(id, handle);
self.active_tcp.insert(handle, conn); self.active_tcp.insert(handle, conn);
self.inbound_tx.insert(id, tx); self.inbound_tx.insert(id, tx);
self.last_activity.insert(handle, StdInstant::now()); // Для TCP тоже полезно self.last_activity.insert(handle, StdInstant::now());
} }
pub fn register_udp( pub fn register_udp(
@@ -74,7 +74,7 @@ impl SessionTracker {
tx: mpsc::Sender<Bytes>, tx: mpsc::Sender<Bytes>,
) { ) {
self.handle_to_id.insert(handle, id); self.handle_to_id.insert(handle, id);
self.id_to_handle.insert(id, handle); // 🔥 Регистрация обратного индекса self.id_to_handle.insert(id, handle);
self.active_udp.insert(handle, conn); self.active_udp.insert(handle, conn);
self.inbound_tx.insert(id, tx); self.inbound_tx.insert(id, tx);
self.last_activity.insert(handle, StdInstant::now()); self.last_activity.insert(handle, StdInstant::now());
@@ -128,7 +128,7 @@ impl SessionTracker {
pub fn close_tunnel_session(&mut self, id: u64) { pub fn close_tunnel_session(&mut self, id: u64) {
self.inbound_tx.remove(&id); self.inbound_tx.remove(&id);
// 🔥 ИСПРАВЛЕНО: Теперь мы реально находим сокет и ставим его в очередь на удаление
if let Some(handle) = self.id_to_handle.remove(&id) { if let Some(handle) = self.id_to_handle.remove(&id) {
self.queue_removal(handle); self.queue_removal(handle);
} }
@@ -140,7 +140,6 @@ impl SessionTracker {
} }
} }
// 🔥 ДОБАВЛЕНО: Сборщик мусора для залипших сокетов
pub fn enforce_idle_timeouts(&mut self, timeout: Duration) { pub fn enforce_idle_timeouts(&mut self, timeout: Duration) {
let now = StdInstant::now(); let now = StdInstant::now();
let mut ghosts = Vec::new(); let mut ghosts = Vec::new();
@@ -159,8 +158,6 @@ impl SessionTracker {
pub fn cleanup(&mut self, socket_set: &mut SocketSet) { pub fn cleanup(&mut self, socket_set: &mut SocketSet) {
for handle in self.to_remove.drain(..) { for handle in self.to_remove.drain(..) {
// Удаление из socket_set автоматически делает abort() внутри smoltcp
// и освобождает память пула/буферов
socket_set.remove(handle); socket_set.remove(handle);
self.active_tcp.remove(&handle); self.active_tcp.remove(&handle);
+3 -5
View File
@@ -46,17 +46,15 @@ impl SmolSocketFactory {
pub fn new(config: Arc<NetworkConfig>) -> Self { pub fn new(config: Arc<NetworkConfig>) -> Self {
Self { Self {
config, config,
// Пул на 128 тяжелых буферов. Если нужно больше — queue вернет ошибку,
// и мы просто отдадим память ОС (что безопасно).
heavy_pool: Arc::new(ArrayQueue::new(128)), heavy_pool: Arc::new(ArrayQueue::new(128)),
} }
} }
/// Берем буфер из пула или создаем новый, если пул пуст
fn alloc_buf(&self, size: usize) -> Vec<u8> { fn alloc_buf(&self, size: usize) -> Vec<u8> {
if size == self.config.tcp_buf_heavy { if size == self.config.tcp_buf_heavy {
if let Some(mut buf) = self.heavy_pool.pop() { if let Some(mut buf) = self.heavy_pool.pop() {
buf.fill(0); // Очищаем данные от прошлой сессии buf.fill(0);
return buf; return buf;
} }
} }
@@ -144,7 +142,7 @@ impl SocketProvider for SmolSocketFactory {
fn create_base_set(&self, n_icmp: usize) -> SocketSet<'static> { fn create_base_set(&self, n_icmp: usize) -> SocketSet<'static> {
let mut sockets = SocketSet::new(Vec::with_capacity(128)); let mut sockets = SocketSet::new(Vec::with_capacity(128));
sockets.add(self.create_bound_udp(None, 53)); // DNS sockets.add(self.create_bound_udp(None, 53));
for _ in 0..n_icmp { for _ in 0..n_icmp {
sockets.add(self.create_icmp()); sockets.add(self.create_icmp());
} }
+3 -3
View File
@@ -44,9 +44,9 @@ impl NetworkConfig {
tcp_buffer_size: 8 * 1024, tcp_buffer_size: 8 * 1024,
udp_buffer_size: 64 * 1024, udp_buffer_size: 64 * 1024,
muxer_capacity: 256, muxer_capacity: 64,
tcp_stream_capacity: 32, tcp_stream_capacity: 8,
udp_stream_capacity: 128, udp_stream_capacity: 32,
smoltcp_socket_buf: heavy_buf, smoltcp_socket_buf: heavy_buf,
tcp_chunk_size: chunk_size, tcp_chunk_size: chunk_size,
+2 -2
View File
@@ -59,7 +59,7 @@ pub(crate) async fn run_tcp_bridge<R, W>(
data: buf.split().freeze(), data: buf.split().freeze(),
}; };
if muxer.send_to_network(msg).is_err() { if muxer.send_to_network(msg).await.is_err() {
return Ok(false); return Ok(false);
} }
Ok(true) Ok(true)
@@ -139,7 +139,7 @@ pub(crate) async fn run_udp_bridge(
match res { match res {
Ok(n) if n > 0 => { Ok(n) if n > 0 => {
let data = Bytes::copy_from_slice(&buf[..n]); let data = Bytes::copy_from_slice(&buf[..n]);
if let Err(e) = muxer.send_data_safe(stream_id, data, true) { if let Err(e) = muxer.send_data_safe(stream_id, data, true).await {
error!(stream_id, "UDP Failed to send to tunnel: {}", e); error!(stream_id, "UDP Failed to send to tunnel: {}", e);
return Err(e); return Err(e);
} }
+25 -44
View File
@@ -89,11 +89,9 @@ impl Connection {
} }
} }
// Измененный контекст: теперь храним оригинальный (локальный) socket_id
type StreamContext = (u32, Ipv4Addr, u16, LocalProtocol); type StreamContext = (u32, Ipv4Addr, u16, LocalProtocol);
pub struct ClientHandler; pub struct ClientHandler;
impl ClientHandler { impl ClientHandler {
async fn establish_leg( async fn establish_leg(
remote_proxy_addr: &str, remote_proxy_addr: &str,
@@ -197,9 +195,7 @@ impl ClientHandler {
let muxer = Arc::new(Muxer::new(true, session_id.clone())); let muxer = Arc::new(Muxer::new(true, session_id.clone()));
// Реестр для отправки данных обратно в виртуальный сетевой стек
let registry: Arc<DashMap<u32, StreamContext>> = Arc::new(DashMap::new()); let registry: Arc<DashMap<u32, StreamContext>> = Arc::new(DashMap::new());
// Мапа для трансляции: локальный socket_id -> глобальный stream_id
let local_to_global: Arc<DashMap<u32, u32>> = Arc::new(DashMap::new()); let local_to_global: Arc<DashMap<u32, u32>> = Arc::new(DashMap::new());
info!("🚀 Netrunner Multi-Path Tunnel Initializing (10 Legs max)."); info!("🚀 Netrunner Multi-Path Tunnel Initializing (10 Legs max).");
@@ -228,10 +224,7 @@ impl ClientHandler {
break; break;
} }
// 🔥 ЗАПУСК HEALTH CHECK: Пингуем все леги
m_stats.perform_health_check().await; m_stats.perform_health_check().await;
// После пинга выводим красивую топологию
m_stats.print_topology_tree(); m_stats.print_topology_tree();
} }
}); });
@@ -239,21 +232,18 @@ impl ClientHandler {
let muxer_inner = muxer.clone(); let muxer_inner = muxer.clone();
tokio::spawn(async move { tokio::spawn(async move {
while let Some(raw_frame) = rx_from_engine.recv().await { while let Some(raw_frame) = rx_from_engine.recv().await {
// Извлекаем нужные метаданные до передачи во фрейм
let dst_ip = raw_frame.dst_ip; let dst_ip = raw_frame.dst_ip;
let dst_port = raw_frame.dst_port; let dst_port = raw_frame.dst_port;
let protocol = raw_frame.protocol; let protocol = raw_frame.protocol;
let is_udp = protocol == LocalProtocol::Udp; let is_udp = protocol == LocalProtocol::Udp;
if let Ok(nrxp_frame) = RawCastAdapter::to_nrxp(raw_frame) { if let Ok(nrxp_frame) = RawCastAdapter::to_nrxp(raw_frame) {
// Адаптер кладет socket_id от smoltcp в заголовок stream_id
let local_socket_id = nrxp_frame.header.stream_id; let local_socket_id = nrxp_frame.header.stream_id;
let f_type = nrxp_frame.header.frame_type; let f_type = nrxp_frame.header.frame_type;
let payload = nrxp_frame.payload; let payload = nrxp_frame.payload;
match f_type { match f_type {
FrameType::Connect | FrameType::UdpConnect => { FrameType::Connect | FrameType::UdpConnect => {
// 🔥 ШАГ 2: Выделяем глобальный ID для нового потока
let global_stream_id = muxer_inner.next_stream_id(); let global_stream_id = muxer_inner.next_stream_id();
info!( info!(
@@ -264,7 +254,6 @@ impl ClientHandler {
global_stream_id global_stream_id
); );
// Запоминаем связь
local_to_global.insert(local_socket_id, global_stream_id); local_to_global.insert(local_socket_id, global_stream_id);
registry.insert( registry.insert(
global_stream_id, global_stream_id,
@@ -274,7 +263,6 @@ impl ClientHandler {
let (v_tx, mut v_rx) = let (v_tx, mut v_rx) =
mpsc::channel(NetworkConfig::global().tcp_stream_capacity); mpsc::channel(NetworkConfig::global().tcp_stream_capacity);
// Регистрируем локальный поток под глобальным ID
muxer_inner.register_stream(global_stream_id, v_tx); muxer_inner.register_stream(global_stream_id, v_tx);
let tx_to_tun = tx_to_engine.clone(); let tx_to_tun = tx_to_engine.clone();
@@ -282,16 +270,16 @@ impl ClientHandler {
tokio::spawn(async move { tokio::spawn(async move {
while let Some(back_payload) = v_rx.recv().await { while let Some(back_payload) = v_rx.recv().await {
if let Some(ctx) = reg.get(&global_stream_id) { // ✅ ИСПРАВЛЕНИЕ 1: Копируем данные и отпускаем DashMap до .await
let (orig_local_id, ip, port, proto) = *ctx; let ctx = reg.get(&global_stream_id).map(|r| *r);
if let Some((orig_local_id, ip, port, proto)) = ctx {
let out_f_type = if proto == LocalProtocol::Udp { let out_f_type = if proto == LocalProtocol::Udp {
FrameType::UdpData FrameType::UdpData
} else { } else {
FrameType::Data FrameType::Data
}; };
// Оборачиваем обратно в оригинальный локальный ID,
// чтобы smoltcp и tun-адаптер поняли, кому это принадлежит
let mock_nrxp = let mock_nrxp =
Frame::new(orig_local_id, out_f_type, back_payload); Frame::new(orig_local_id, out_f_type, back_payload);
@@ -307,14 +295,19 @@ impl ClientHandler {
} }
}); });
let _ = muxer_inner.send_control(global_stream_id, f_type, payload); // ✅ Добавлен .await
let _ = muxer_inner
.send_control(global_stream_id, f_type, payload)
.await;
} }
FrameType::Data | FrameType::UdpData => { FrameType::Data | FrameType::UdpData => {
// Ищем глобальный ID по локальному // ✅ ИСПРАВЛЕНИЕ 2: Сброс блокировки DashMap
if let Some(global_ref) = local_to_global.get(&local_socket_id) { let global_stream_id =
let global_stream_id = *global_ref; local_to_global.get(&local_socket_id).map(|r| *r);
let _ =
muxer_inner.send_data_safe(global_stream_id, payload, is_udp); if let Some(id) = global_stream_id {
// ✅ Добавлен .await
let _ = muxer_inner.send_data_safe(id, payload, is_udp).await;
} else { } else {
warn!( warn!(
"Data received for unknown local socket_id: {}", "Data received for unknown local socket_id: {}",
@@ -323,14 +316,16 @@ impl ClientHandler {
} }
} }
FrameType::Close => { FrameType::Close => {
// .remove() сам по себе возвращает владение данными и снимает лок мгновенно,
// поэтому здесь дедлока не было, но мы добавляем .await для вызова Муксера.
if let Some((_, global_stream_id)) = if let Some((_, global_stream_id)) =
local_to_global.remove(&local_socket_id) local_to_global.remove(&local_socket_id)
{ {
let _ = muxer_inner.send_control( // ✅ Добавлен .await
global_stream_id, let _ = muxer_inner
FrameType::Close, .send_control(global_stream_id, FrameType::Close, Bytes::new())
Bytes::new(), .await;
);
muxer_inner.remove_stream(global_stream_id); muxer_inner.remove_stream(global_stream_id);
registry.remove(&global_stream_id); registry.remove(&global_stream_id);
} }
@@ -346,7 +341,6 @@ impl ClientHandler {
Ok(()) Ok(())
} }
} }
pub struct ServerHandler { pub struct ServerHandler {
pub(crate) conn: Connection, pub(crate) conn: Connection,
pub(crate) session_manager: Arc<SessionManager>, pub(crate) session_manager: Arc<SessionManager>,
@@ -414,7 +408,6 @@ impl TunnelHandler for ServerHandler {
let handshake_timeout = std::time::Duration::from_secs(1); let handshake_timeout = std::time::Duration::from_secs(1);
// 1. ФАЗА TLS HANDSHAKE
let hello = loop { let hello = loop {
let buf_snapshot = self.conn.read_buf.clone().freeze(); let buf_snapshot = self.conn.read_buf.clone().freeze();
match self match self
@@ -423,9 +416,7 @@ impl TunnelHandler for ServerHandler {
.make_server_handshake(&mut self.conn.read_buf) .make_server_handshake(&mut self.conn.read_buf)
{ {
Ok(b) => break b, Ok(b) => break b,
Err(e) => { Err(e) => match e.execute_strategy() {
// 🔥 ВЫЗЫВАЕМ СТРАТЕГИЮ (Она заодно и залогирует ошибку)
match e.execute_strategy() {
ErrorAction::Wait => { ErrorAction::Wait => {
let res = tokio::time::timeout( let res = tokio::time::timeout(
handshake_timeout, handshake_timeout,
@@ -437,8 +428,6 @@ impl TunnelHandler for ServerHandler {
Ok(Ok(0)) => return Err("Client closed".into()), Ok(Ok(0)) => return Err("Client closed".into()),
Ok(Ok(_)) => continue, Ok(Ok(_)) => continue,
_ => { _ => {
// По таймауту тоже редиректим, так как сканеры часто
// присылают пол-пакета и "висят"
Self::handle_stealth_fallback( Self::handle_stealth_fallback(
self.conn.inbound, self.conn.inbound,
self.conn.outbound, self.conn.outbound,
@@ -450,7 +439,6 @@ impl TunnelHandler for ServerHandler {
} }
} }
ErrorAction::Redirect => { ErrorAction::Redirect => {
// Пришел валидный TLS пакет, но от чужака -> притворяемся сайтом
Self::handle_stealth_fallback( Self::handle_stealth_fallback(
self.conn.inbound, self.conn.inbound,
self.conn.outbound, self.conn.outbound,
@@ -460,22 +448,18 @@ impl TunnelHandler for ServerHandler {
return Ok(()); return Ok(());
} }
ErrorAction::Drop => { ErrorAction::Drop => {
// Полный мусор, отключаем немедленно (экономим сокеты ОС)
return Err("Dropped by security strategy (TLS Phase)".into()); return Err("Dropped by security strategy (TLS Phase)".into());
} }
} },
}
} }
}; };
// Отвечаем ServerHello
self.conn self.conn
.outbound .outbound
.write_all(&hello) .write_all(&hello)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
// 2. ФАЗА NETRUNNER HANDSHAKE (Внутри туннеля)
let (session_id, leg_id) = loop { let (session_id, leg_id) = loop {
let n = tokio::time::timeout( let n = tokio::time::timeout(
std::time::Duration::from_secs(3), std::time::Duration::from_secs(3),
@@ -513,17 +497,15 @@ impl TunnelHandler for ServerHandler {
let buf_snapshot = self.conn.read_buf.clone().freeze(); let buf_snapshot = self.conn.read_buf.clone().freeze();
match e.execute_strategy() { match e.execute_strategy() {
ErrorAction::Redirect => { ErrorAction::Redirect => {
// Если мы решаем прятать VPN даже на уровне неудачной криптографии
Self::handle_stealth_fallback( Self::handle_stealth_fallback(
self.conn.inbound, self.conn.inbound,
self.conn.outbound, self.conn.outbound,
buf_snapshot, // Отправляем зашифрованный мусор на целевой сервак (он сам его дропнет) buf_snapshot,
) )
.await; .await;
return Ok(()); return Ok(());
} }
_ => { _ => {
// Wait или Drop на этапе Handshake лучше просто дропать
return Err("Dropped by security strategy (Auth Phase)".into()); return Err("Dropped by security strategy (Auth Phase)".into());
} }
} }
@@ -531,7 +513,6 @@ impl TunnelHandler for ServerHandler {
} }
}; };
// ... Инициализация Muxer и TunnelEngine (остается без изменений)
let muxer = self.session_manager.get_or_create(&session_id); let muxer = self.session_manager.get_or_create(&session_id);
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity); let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
-1
View File
@@ -185,7 +185,6 @@ impl TunnelEngine {
} }
} }
} else { } else {
// TCP - потоковый, режем на чанки по 16KB для совместимости с TLS Record
while !data.is_empty() { while !data.is_empty() {
let chunk_size = let chunk_size =
std::cmp::min(data.len(), NetworkConfig::global().tcp_chunk_size); std::cmp::min(data.len(), NetworkConfig::global().tcp_chunk_size);
+3 -9
View File
@@ -14,8 +14,6 @@ use crate::net::{
}; };
use crate::nrxp::{Frame, FrameType}; use crate::nrxp::{Frame, FrameType};
// --- ОТКРЫВАШКА ДЛЯ СЕРВЕРА (Remote) ---
// Этот парень реально идет в интернет через сокеты ОС
pub struct RemoteOpener { pub struct RemoteOpener {
pub muxer: Arc<Muxer>, pub muxer: Arc<Muxer>,
} }
@@ -63,11 +61,9 @@ impl RemoteOpener {
} }
} }
// --- ЕДИНЫЙ ХЭНДЛЕР ---
pub(crate) struct StreamHandler { pub(crate) struct StreamHandler {
muxer: Arc<Muxer>, muxer: Arc<Muxer>,
opener: Option<Arc<RemoteOpener>>, // На сервере Some, на клиенте None opener: Option<Arc<RemoteOpener>>,
} }
impl StreamHandler { impl StreamHandler {
@@ -89,8 +85,7 @@ impl StreamHandler {
} }
FrameType::Data | FrameType::UdpData => { FrameType::Data | FrameType::UdpData => {
// Унифицировано: просто кидаем в локальный канал self.muxer.dispatch_to_local(stream_id, frame.payload).await;
self.muxer.dispatch_to_local(stream_id, frame.payload);
} }
FrameType::Close => { FrameType::Close => {
@@ -105,7 +100,6 @@ impl StreamHandler {
let target = String::from_utf8_lossy(&payload).to_string(); let target = String::from_utf8_lossy(&payload).to_string();
if let Some(opener) = &self.opener { if let Some(opener) = &self.opener {
// Создаем виртуальную трубу для этого стрима
let capacity = if is_udp { let capacity = if is_udp {
NetworkConfig::global().udp_stream_capacity NetworkConfig::global().udp_stream_capacity
} else { } else {
@@ -125,7 +119,7 @@ impl StreamHandler {
stream_id, stream_id,
"⚠️ [Tunnel] Rejected incoming connection to {} (Client mode)", target "⚠️ [Tunnel] Rejected incoming connection to {} (Client mode)", target
); );
// Если мы клиент, мы не принимаем входящие соединения из туннеля
let _ = self let _ = self
.muxer .muxer
.send_control(stream_id, FrameType::Close, Bytes::new()); .send_control(stream_id, FrameType::Close, Bytes::new());
+35 -66
View File
@@ -1,14 +1,13 @@
use bytes::Bytes; use bytes::Bytes;
use dashmap::DashMap; use dashmap::DashMap;
use netrunner_logger::{debug, info, warn}; use netrunner_logger::{debug, info, warn};
use tokio::sync::mpsc::error::TrySendError;
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc::error::TrySendError;
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
use crate::nrxp::FrameType; use crate::nrxp::FrameType;
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct LegStats { pub struct LegStats {
pub tx_bytes: AtomicU64, pub tx_bytes: AtomicU64,
@@ -59,9 +58,7 @@ pub struct Muxer {
id_gen: Arc<IdGenerator>, id_gen: Arc<IdGenerator>,
session_id: Arc<String>, session_id: Arc<String>,
} }
impl Muxer { impl Muxer {
pub fn new(is_client: bool, session_id: String) -> Self { pub fn new(is_client: bool, session_id: String) -> Self {
Self { Self {
legs: Arc::new(DashMap::new()), legs: Arc::new(DashMap::new()),
@@ -77,7 +74,6 @@ impl Muxer {
control_tx: Sender<MuxMessage>, control_tx: Sender<MuxMessage>,
data_tx: Sender<MuxMessage>, data_tx: Sender<MuxMessage>,
) { ) {
if self.legs.len() >= 10 { if self.legs.len() >= 10 {
warn!( warn!(
leg_id, leg_id,
@@ -113,43 +109,40 @@ impl Muxer {
let is_udp = matches!(frame_type, FrameType::UdpData | FrameType::UdpConnect); let is_udp = matches!(frame_type, FrameType::UdpData | FrameType::UdpConnect);
let mut candidates: Vec<(u32, MuxLeg)> = self
let mut candidates: Vec<(u32, MuxLeg)> = self.legs .legs
.iter() .iter()
.map(|kv| (*kv.key(), kv.value().clone())) .map(|kv| (*kv.key(), kv.value().clone()))
.filter(|(id, _)| { .filter(|(id, _)| if is_udp { id % 2 != 0 } else { id % 2 == 0 })
if is_udp { id % 2 != 0 } else { id % 2 == 0 }
})
.collect(); .collect();
if candidates.is_empty() { if candidates.is_empty() {
candidates = self.legs.iter().map(|kv| (*kv.key(), kv.value().clone())).collect(); candidates = self
.legs
.iter()
.map(|kv| (*kv.key(), kv.value().clone()))
.collect();
} }
candidates.sort_by_key(|(_, leg)| { candidates.sort_by_key(|(_, leg)| {
let rtt = leg.stats.rtt_ms.load(Ordering::Relaxed); let rtt = leg.stats.rtt_ms.load(Ordering::Relaxed);
if rtt == 0 { 9999 } else { rtt } if rtt == 0 {
9999
} else {
rtt
}
}); });
let pool_size = std::cmp::min(candidates.len(), 2); let pool_size = std::cmp::min(candidates.len(), 2);
let target = &candidates[stream_id as usize % pool_size]; let target = &candidates[stream_id as usize % pool_size];
Some(target.clone()) Some(target.clone())
} }
pub async fn send_to_network(&self, message: MuxMessage) -> Result<(), String> {
pub fn send_to_network(&self, message: MuxMessage) -> Result<(), String> {
let stream_id = message.stream_id; let stream_id = message.stream_id;
let size = message.data.len() as u64; let size = message.data.len() as u64;
let (leg_id, leg) = self let (leg_id, leg) = self
.select_leg(&message.frame_type, stream_id) .select_leg(&message.frame_type, stream_id)
.ok_or_else(|| "MUXER: No active legs available".to_string())?; .ok_or_else(|| "MUXER: No active legs available".to_string())?;
@@ -158,42 +151,30 @@ impl Muxer {
FrameType::Connect FrameType::Connect
| FrameType::Close | FrameType::Close
| FrameType::UdpConnect | FrameType::UdpConnect
| FrameType::Handshake => &leg.control_tx, | FrameType::Handshake => leg.control_tx.clone(),
_ => &leg.data_tx, _ => leg.data_tx.clone(),
}; };
match target_tx.try_send(message) { match target_tx.send(message).await {
Ok(_) => { Ok(_) => {
leg.stats.tx_bytes.fetch_add(size, Ordering::Relaxed); leg.stats.tx_bytes.fetch_add(size, Ordering::Relaxed);
if let Some(stream_ref) = self.streams.get(&stream_id) {
stream_ref
.value()
.1
.tx_bytes
.fetch_add(size, Ordering::Relaxed);
}
Ok(())
}
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
warn!( if let Some(stream_ref) = self.streams.get(&stream_id) {
leg_id, stream_ref.1.tx_bytes.fetch_add(size, Ordering::Relaxed);
"MUXER: Network queue full! Dropping outbound packet." }
);
Ok(()) Ok(())
} }
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => { Err(_) => {
self.remove_leg(leg_id); self.remove_leg(leg_id);
Err(format!("MUXER: Leg {} died during send", leg_id)) Err(format!("MUXER: Leg {} died during send", leg_id))
} }
} }
} }
pub fn send_data_safe( pub async fn send_data_safe(
&self, &self,
stream_id: u32, stream_id: u32,
data: Bytes, // Больше не mut data: Bytes,
is_udp: bool, is_udp: bool,
) -> Result<(), String> { ) -> Result<(), String> {
let frame_type = if is_udp { let frame_type = if is_udp {
@@ -202,15 +183,15 @@ impl Muxer {
FrameType::Data FrameType::Data
}; };
// Отправляем как есть, целиком!
self.send_to_network(MuxMessage { self.send_to_network(MuxMessage {
stream_id, stream_id,
frame_type, frame_type,
data, data,
}) })
.await
} }
pub fn send_control( pub async fn send_control(
&self, &self,
stream_id: u32, stream_id: u32,
f_type: FrameType, f_type: FrameType,
@@ -221,6 +202,7 @@ impl Muxer {
frame_type: f_type, frame_type: f_type,
data, data,
}) })
.await
} }
pub fn register_stream(&self, stream_id: u32, tx: Sender<Bytes>) { pub fn register_stream(&self, stream_id: u32, tx: Sender<Bytes>) {
@@ -232,8 +214,7 @@ impl Muxer {
self.streams.remove(&stream_id); self.streams.remove(&stream_id);
} }
pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) { pub async fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
let stream_opt = self let stream_opt = self
.streams .streams
.get(&stream_id) .get(&stream_id)
@@ -242,18 +223,11 @@ pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
if let Some((tx, stats)) = stream_opt { if let Some((tx, stats)) = stream_opt {
let size = data.len() as u64; let size = data.len() as u64;
match tx.send(data).await {
match tx.try_send(data) {
Ok(_) => { Ok(_) => {
stats.rx_bytes.fetch_add(size, Ordering::Relaxed); stats.rx_bytes.fetch_add(size, Ordering::Relaxed);
} }
Err(TrySendError::Full(_)) => { Err(_) => {
netrunner_logger::warn!(
stream_id,
"Muxer -> Local: Buffer full, dropping packet. Check your bridge/TUN speed."
);
}
Err(TrySendError::Closed(_)) => {
self.remove_stream(stream_id); self.remove_stream(stream_id);
} }
} }
@@ -271,8 +245,9 @@ pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
} }
pub async fn perform_health_check(&self) { pub async fn perform_health_check(&self) {
let legs: Vec<(u32, Sender<MuxMessage>)> = self
let legs: Vec<(u32, Sender<MuxMessage>)> = self.legs.iter() .legs
.iter()
.map(|k| (*k.key(), k.value().control_tx.clone())) .map(|k| (*k.key(), k.value().control_tx.clone()))
.collect(); .collect();
@@ -280,7 +255,6 @@ pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
let probe_stream_id = self.id_gen.next(); let probe_stream_id = self.id_gen.next();
let (probe_tx, mut probe_rx) = tokio::sync::mpsc::channel(2); let (probe_tx, mut probe_rx) = tokio::sync::mpsc::channel(2);
self.register_stream(probe_stream_id, probe_tx); self.register_stream(probe_stream_id, probe_tx);
let msg = MuxMessage { let msg = MuxMessage {
@@ -291,10 +265,9 @@ pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
if tx.try_send(msg).is_ok() { if tx.try_send(msg).is_ok() {
match tokio::time::timeout(std::time::Duration::from_secs(2), probe_rx.recv()).await
match tokio::time::timeout(std::time::Duration::from_secs(2), probe_rx.recv()).await { {
Ok(Some(_)) => { Ok(Some(_)) => {
let rtt = start.elapsed().as_millis() as u32; let rtt = start.elapsed().as_millis() as u32;
if let Some(leg) = self.legs.get(&leg_id) { if let Some(leg) = self.legs.get(&leg_id) {
@@ -303,7 +276,6 @@ pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
} }
} }
_ => { _ => {
if let Some(leg) = self.legs.get(&leg_id) { if let Some(leg) = self.legs.get(&leg_id) {
leg.stats.rtt_ms.store(5000, Ordering::Relaxed); leg.stats.rtt_ms.store(5000, Ordering::Relaxed);
warn!(leg_id, "❌ Leg Health Check Timeout (marked as slow)"); warn!(leg_id, "❌ Leg Health Check Timeout (marked as slow)");
@@ -312,12 +284,10 @@ pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
} }
} }
self.remove_stream(probe_stream_id); self.remove_stream(probe_stream_id);
} }
} }
fn format_size(bytes: u64) -> String { fn format_size(bytes: u64) -> String {
const KB: u64 = 1024; const KB: u64 = 1024;
const MB: u64 = KB * 1024; const MB: u64 = KB * 1024;
@@ -333,7 +303,6 @@ pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
} }
} }
pub fn print_topology_tree(&self) { pub fn print_topology_tree(&self) {
println!( println!(
"\n🌐 Netrunner Tunnel Topology [Session: {}]", "\n🌐 Netrunner Tunnel Topology [Session: {}]",
+1 -1
View File
@@ -139,7 +139,7 @@ impl Codec {
let decrypted = self.crypto.decrypt(&mut data_to_decrypt).map_err(|_| { let decrypted = self.crypto.decrypt(&mut data_to_decrypt).map_err(|_| {
self.staging.clear(); self.staging.clear();
// Сохраняем первые 32 байта зашифрованного пакета для логов, чтобы понять, что нам прислали
let bad_data = let bad_data =
Bytes::copy_from_slice(&data_to_decrypt[..data_to_decrypt.len().min(32)]); Bytes::copy_from_slice(&data_to_decrypt[..data_to_decrypt.len().min(32)]);
TlsError::new( TlsError::new(
+2 -3
View File
@@ -16,7 +16,6 @@ impl Network {
pub async fn run(&self, token: CancellationToken) { pub async fn run(&self, token: CancellationToken) {
let addr = format!("{}:{}", self.host, self.port); let addr = format!("{}:{}", self.host, self.port);
// Инициализируем конфиг для сервера
NetworkConfig::init_global(1500); NetworkConfig::init_global(1500);
info!("🌐 Netrunner Server: Listening on {}", addr); info!("🌐 Netrunner Server: Listening on {}", addr);
@@ -32,10 +31,10 @@ impl Network {
if let Ok((stream, client_addr)) = res { if let Ok((stream, client_addr)) = res {
info!("🔌 Connection from {}", client_addr); info!("🔌 Connection from {}", client_addr);
// Создаем объект соединения (включает кодек)
let conn = Connection::new(stream, true); let conn = Connection::new(stream, true);
// Создаем хэндлер. (Он сам разберется с SessionManager, если нужно)
let handler = ServerHandler::new(conn); let handler = ServerHandler::new(conn);
tokio::spawn(async move { tokio::spawn(async move {