clean comments

This commit is contained in:
2026-04-03 12:06:58 +07:00
parent b90083539c
commit e53a95692e
18 changed files with 100 additions and 258 deletions
-11
View File
@@ -37,10 +37,6 @@ pub struct VpnTrafficStats {
pub tx_packets: u64, pub tx_packets: u64,
} }
// ==========================================
// SESSION
// ==========================================
#[derive(uniffi::Object)] #[derive(uniffi::Object)]
pub struct Session { pub struct Session {
pub(crate) cancel_token: CancellationToken, pub(crate) cancel_token: CancellationToken,
@@ -64,10 +60,6 @@ impl Drop for Session {
} }
} }
// ==========================================
// SESSION MANAGER
// ==========================================
#[derive(uniffi::Object)] #[derive(uniffi::Object)]
pub struct SessionManager; pub struct SessionManager;
@@ -107,7 +99,6 @@ impl SessionManager {
NetworkConfig::init_global(config.mtu); NetworkConfig::init_global(config.mtu);
// --- ЗАПУСК ENGINE И TUN ---
let engine_token = cancel_token.clone(); let engine_token = cancel_token.clone();
runtime.spawn(async move { runtime.spawn(async move {
info!("Starting VPN Engine thread..."); info!("Starting VPN Engine thread...");
@@ -148,8 +139,6 @@ impl SessionManager {
Ok((mut engine, tun)) => { Ok((mut engine, tun)) => {
info!("Engine built successfully, starting loop..."); info!("Engine built successfully, starting loop...");
// tokio::select! позволяет моментально прервать бесконечный цикл
// engine.run() при вызове session.stop()
tokio::select! { tokio::select! {
_ = engine.run(tun) => { _ = engine.run(tun) => {
info!("Engine loop finished normally."); info!("Engine loop finished normally.");
-12
View File
@@ -2,11 +2,9 @@ use netrunner_logger::{error, info};
mod net; mod net;
mod tun; mod tun;
// Импортируем Билдер, Конфиг и модули маршрутизации
use crate::tun::{routing::reset_platform_routing, tun::Tun}; use crate::tun::{routing::reset_platform_routing, tun::Tun};
use net::engine::{EngineBuilder, EngineConfig}; use net::engine::{EngineBuilder, EngineConfig};
// Импортируем глобальный конфиг сети
use netrunner_core::net::network::NetworkConfig; use netrunner_core::net::network::NetworkConfig;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
@@ -19,14 +17,10 @@ async fn main() -> anyhow::Result<()> {
let remote_address = "147.45.43.70:443".to_string(); let remote_address = "147.45.43.70:443".to_string();
let cancel_token = CancellationToken::new(); let cancel_token = CancellationToken::new();
// ==================================================
// 1. ИНИЦИАЛИЗАЦИЯ ДВИЖКА И TUN
// ==================================================
let config = EngineConfig::new(&remote_address) let config = EngineConfig::new(&remote_address)
.with_cache_path(".") .with_cache_path(".")
.with_mtu(1500); .with_mtu(1500);
// ВАЖНО: Инициализируем глобальные настройки сети (MTU, размеры буферов Muxer'а)
NetworkConfig::init_global(config.mtu); NetworkConfig::init_global(config.mtu);
let tun_device = Tun::create(|tun_cfg| { let tun_device = Tun::create(|tun_cfg| {
@@ -47,9 +41,6 @@ async fn main() -> anyhow::Result<()> {
.build() .build()
.await; .await;
// ==================================================
// 2. ГЛАВНЫЙ ЦИКЛ ENGINE
// ==================================================
match builder_result { match builder_result {
Ok((mut engine, tun)) => { Ok((mut engine, tun)) => {
info!("Engine starting process loop..."); info!("Engine starting process loop...");
@@ -70,9 +61,6 @@ async fn main() -> anyhow::Result<()> {
} }
} }
// ==================================================
// 3. ОЧИСТКА РОУТИНГА
// ==================================================
info!("Restoring system routing..."); info!("Restoring system routing...");
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format"); let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
let p_ip = addr.ip().to_string(); let p_ip = addr.ip().to_string();
+5 -31
View File
@@ -7,19 +7,13 @@ use smoltcp::{
}; };
use std::time::Duration; use std::time::Duration;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
// Добавили trace для частых логов (попакетно) и debug для состояний
use netrunner_logger::debug; use netrunner_logger::debug;
// ============================================================================
// 1. БАЗОВАЯ СТРУКТУРА (ConnectionCore)
// ============================================================================
/// Фундамент для любого соединения.
/// Инициализирует и хранит каналы связи между smoltcp и Muxer'ом.
pub struct ConnectionCore<T> { pub struct ConnectionCore<T> {
pub handle: SocketHandle, pub handle: SocketHandle,
pub tx: mpsc::Sender<T>, pub tx: mpsc::Sender<T>,
pub rx: mpsc::Receiver<Bytes>, // Входящие из туннеля всегда байты pub rx: mpsc::Receiver<Bytes>,
} }
impl<T> ConnectionCore<T> { impl<T> ConnectionCore<T> {
@@ -39,9 +33,6 @@ impl<T> ConnectionCore<T> {
(core, rx_from_smol, tx_to_smol) (core, rx_from_smol, tx_to_smol)
} }
} }
// ============================================================================
// 2. TCP СОЕДИНЕНИЕ (TcpConnection)
// ============================================================================
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum ConnectionState { pub enum ConnectionState {
@@ -57,7 +48,7 @@ pub struct TcpConnection {
pending_data: BytesMut, pending_data: BytesMut,
handshake_rx: Option<oneshot::Receiver<()>>, handshake_rx: Option<oneshot::Receiver<()>>,
chunk_buf: Vec<u8>, chunk_buf: Vec<u8>,
server_eof: bool, // <--- ДОБАВЛЕН ФЛАГ ОКОНЧАНИЯ ПЕРЕДАЧИ server_eof: bool,
} }
impl TcpConnection { impl TcpConnection {
@@ -110,7 +101,6 @@ impl TcpConnection {
ConnectionState::Active => { ConnectionState::Active => {
self.poll_and_process(socket); self.poll_and_process(socket);
// Если сокет достиг финальных стадий, убиваем нашу сессию
if matches!(socket.state(), tcp::State::Closed | tcp::State::TimeWait) { if matches!(socket.state(), tcp::State::Closed | tcp::State::TimeWait) {
debug!(%self.core.handle, "TCP Socket is finished, state -> Closed"); debug!(%self.core.handle, "TCP Socket is finished, state -> Closed");
self.state = ConnectionState::Closed; self.state = ConnectionState::Closed;
@@ -129,7 +119,6 @@ impl TcpConnection {
} }
fn poll_and_process(&mut self, socket: &mut tcp::Socket) { fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
// 1. Вычитываем данные из smoltcp и шлем в Muxer
while socket.can_recv() { while socket.can_recv() {
let mut full = false; let mut full = false;
@@ -147,7 +136,6 @@ impl TcpConnection {
full = true; full = true;
} }
Err(_) => { Err(_) => {
// Канал Muxer'а закрыт
self.server_eof = true; self.server_eof = true;
break; break;
} }
@@ -161,7 +149,6 @@ impl TcpConnection {
} }
} }
// 2. Читаем данные из Muxer'а
if !self.server_eof { if !self.server_eof {
loop { loop {
if self.pending_data.len() >= socket.send_capacity() { if self.pending_data.len() >= socket.send_capacity() {
@@ -182,7 +169,6 @@ impl TcpConnection {
} }
} }
// 3. Пишем данные браузеру
if !self.pending_data.is_empty() && socket.can_send() { if !self.pending_data.is_empty() && socket.can_send() {
match socket.send_slice(&self.pending_data) { match socket.send_slice(&self.pending_data) {
Ok(n) => { Ok(n) => {
@@ -194,9 +180,6 @@ impl TcpConnection {
} }
} }
// 4. ГРАЦИОЗНОЕ ЗАКРЫТИЕ (Отправка FIN браузеру)
// Закрываем сокет ТОЛЬКО если удаленный сервер закончил работу
// и мы отдали все накопленные данные браузеру.
if self.server_eof && self.pending_data.is_empty() && socket.may_send() { if self.server_eof && self.pending_data.is_empty() && socket.may_send() {
debug!(%self.core.handle, "All data flushed after server EOF, sending FIN to browser"); debug!(%self.core.handle, "All data flushed after server EOF, sending FIN to browser");
socket.close(); socket.close();
@@ -204,15 +187,11 @@ impl TcpConnection {
} }
} }
// ============================================================================
// 3. UDP СОЕДИНЕНИЕ (UdpConnection)
// ============================================================================
const UDP_TIMEOUT: Duration = Duration::from_secs(60); const UDP_TIMEOUT: Duration = Duration::from_secs(60);
pub type UdpPacketTarget = (Bytes, std::net::Ipv4Addr, u16); pub type UdpPacketTarget = (Bytes, std::net::Ipv4Addr, u16);
pub struct UdpConnection { pub struct UdpConnection {
core: ConnectionCore<UdpPacketTarget>, // Используем кортеж core: ConnectionCore<UdpPacketTarget>,
last_client_endpoint: Option<IpEndpoint>, last_client_endpoint: Option<IpEndpoint>,
last_activity: std::time::Instant, last_activity: std::time::Instant,
} }
@@ -234,7 +213,7 @@ impl UdpConnection {
(conn, rx_from_smol, tx_to_smol) (conn, rx_from_smol, tx_to_smol)
} }
// Вспомогательный метод для Tracker'а
pub fn has_client(&self, port: u16) -> bool { pub fn has_client(&self, port: u16) -> bool {
self.last_client_endpoint self.last_client_endpoint
.map_or(false, |ep| ep.port == port) .map_or(false, |ep| ep.port == port)
@@ -261,22 +240,17 @@ impl UdpConnection {
} }
} }
// Исходящие из туннеля
if let Some(client_endpoint) = self.last_client_endpoint { if let Some(client_endpoint) = self.last_client_endpoint {
// Проверяем can_send() перед КАЖДЫМ извлечением пакета
while socket.can_send() { while socket.can_send() {
match self.core.rx.try_recv() { match self.core.rx.try_recv() {
Ok(data) => { Ok(data) => {
if let Err(e) = socket.send_slice(&data, client_endpoint) { if let Err(e) = socket.send_slice(&data, client_endpoint) {
debug!("Dropped UDP packet due to smoltcp error: {:?}", e); debug!("Dropped UDP packet due to smoltcp error: {:?}", e);
// В UDP потеря пакетов нормальна, но мы хотя бы избежали
// слепого выкачивания всего канала
} else { } else {
self.last_activity = std::time::Instant::now(); self.last_activity = std::time::Instant::now();
} }
} }
Err(_) => { Err(_) => {
// Канал пуст или отключен
break; break;
} }
} }
+15 -60
View File
@@ -20,9 +20,6 @@ use crate::net::{
ip_store::FakeIpStore, ip_store::FakeIpStore,
}; };
// ============================================================================
// 1. УПРАВЛЕНИЕ СОСТОЯНИЕМ СЕССИЙ (SessionTracker)
// ============================================================================
struct SessionTracker { struct SessionTracker {
last_activity: HashMap<SocketHandle, StdInstant>, last_activity: HashMap<SocketHandle, StdInstant>,
active_tcp: HashMap<SocketHandle, TcpConnection>, active_tcp: HashMap<SocketHandle, TcpConnection>,
@@ -69,7 +66,7 @@ impl SessionTracker {
self.failed_until.remove(&handle); self.failed_until.remove(&handle);
self.active_tcp.remove(&handle); self.active_tcp.remove(&handle);
self.active_udp.remove(&handle); self.active_udp.remove(&handle);
self.pending_tcp.remove(&handle); // Очищаем из pending! self.pending_tcp.remove(&handle);
if let Some(socket_id) = self.handle_to_id.remove(&handle) { if let Some(socket_id) = self.handle_to_id.remove(&handle) {
self.inbound_tx.remove(&socket_id); self.inbound_tx.remove(&socket_id);
@@ -85,7 +82,6 @@ impl SessionTracker {
) -> bool { ) -> bool {
socket_set.iter().any(|(_, s)| { socket_set.iter().any(|(_, s)| {
if let Some(tcp) = tcp::Socket::downcast(s) { if let Some(tcp) = tcp::Socket::downcast(s) {
// Если сокет уже привязался к клиенту, проверяем его исходные данные
if let Some(remote) = tcp.remote_endpoint() { if let Some(remote) = tcp.remote_endpoint() {
if remote.addr == src_addr && remote.port == src_port { if remote.addr == src_addr && remote.port == src_port {
return true; return true;
@@ -107,10 +103,6 @@ impl SessionTracker {
} }
} }
// ============================================================================
// 2. РАЗРЕШЕНИЕ АДРЕСОВ И DNS (TargetResolver)
// ============================================================================
struct TargetResolver { struct TargetResolver {
dns_handler: DnsHandler, dns_handler: DnsHandler,
fake_ip_store: FakeIpStore, fake_ip_store: FakeIpStore,
@@ -129,58 +121,47 @@ impl TargetResolver {
} }
} }
// ============================================================================
// 3. ФАБРИКА СОКЕТОВ (SocketFactory)
// ============================================================================
struct SocketFactory; struct SocketFactory;
impl SocketFactory { impl SocketFactory {
fn create_tcp<'a>(port: u16) -> tcp::Socket<'a> { fn create_tcp<'a>(port: u16) -> tcp::Socket<'a> {
let max_buf = NetworkConfig::global().smoltcp_socket_buf; let cfg = NetworkConfig::global();
// Для Web-трафика используем максимум из конфига (например, 2 МБ)
// Для остальных урезаем в 4 раза, чтобы сэкономить RAM на фоновых соединениях
let buf_size = match port { let buf_size = match port {
443 | 80 | 8080 => max_buf, 443 | 80 | 8080 | 1935 => cfg.tcp_buf_heavy,
22 => 32 * 1024, _ => cfg.tcp_buf_light,
53 => 16 * 1024,
_ => max_buf,
}; };
let mut socket = tcp::Socket::new( let mut socket = tcp::Socket::new(
tcp::SocketBuffer::new(vec![0; buf_size]), tcp::SocketBuffer::new(vec![0; buf_size]),
tcp::SocketBuffer::new(vec![0; buf_size]), tcp::SocketBuffer::new(vec![0; buf_size]),
); );
socket.set_nagle_enabled(false); socket.set_nagle_enabled(false);
socket.set_ack_delay(None); socket.set_ack_delay(None);
socket socket
} }
fn create_udp<'a>(port: u16) -> udp::Socket<'a> { fn create_udp<'a>(port: u16) -> udp::Socket<'a> {
let config = NetworkConfig::global(); let cfg = NetworkConfig::global();
let max_buf = config.smoltcp_socket_buf;
let payload_size = config.safe_payload_size.max(1); // Защита от деления на 0
// Вычисляем размер буфера и количество пакетов let (buf_size, meta_count) = match port {
let (buf_size, packet_count) = match port { 443 | 4433 | 10000..=60000 => (cfg.udp_buf_heavy, cfg.udp_meta_heavy),
443 => (max_buf, max_buf / payload_size), // QUIC/HTTP3 трафик 53 | 123 => (cfg.udp_buf_light, cfg.udp_meta_light),
53 => (64 * 1024, (64 * 1024) / payload_size), // DNS _ => (cfg.udp_buf_heavy / 2, cfg.udp_meta_heavy / 2),
_ => (max_buf, max_buf / payload_size),
}; };
let packet_count = packet_count.max(32);
udp::Socket::new( udp::Socket::new(
udp::PacketBuffer::new( udp::PacketBuffer::new(
vec![udp::PacketMetadata::EMPTY; packet_count], vec![udp::PacketMetadata::EMPTY; meta_count],
vec![0; buf_size], vec![0; buf_size],
), ),
udp::PacketBuffer::new( udp::PacketBuffer::new(
vec![udp::PacketMetadata::EMPTY; packet_count], vec![udp::PacketMetadata::EMPTY; meta_count],
vec![0; buf_size], vec![0; buf_size],
), ),
) )
} }
fn create_icmp<'a>() -> icmp::Socket<'a> { fn create_icmp<'a>() -> icmp::Socket<'a> {
icmp::Socket::new( icmp::Socket::new(
icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY; 8], vec![0; 2048]), icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY; 8], vec![0; 2048]),
@@ -189,9 +170,6 @@ impl SocketFactory {
} }
} }
// ============================================================================
// 4. ГЛАВНЫЙ КООРДИНАТОР (ConnectionManager)
// ============================================================================
pub struct ConnectionManager { pub struct ConnectionManager {
tracker: SessionTracker, tracker: SessionTracker,
resolver: TargetResolver, resolver: TargetResolver,
@@ -207,8 +185,6 @@ impl ConnectionManager {
} }
} }
// ВАЖНО: Метод для получения данных из сети (от VPN) и инъекции их в smoltcp
// Пытается внедрить пакет. Если канал переполнен — возвращает пакет обратно!
pub fn try_inject_inbound(&mut self, frame: RawCastFrame) -> Result<(), RawCastFrame> { pub fn try_inject_inbound(&mut self, frame: RawCastFrame) -> Result<(), RawCastFrame> {
if frame.event != RawCastEvent::Data { if frame.event != RawCastEvent::Data {
if frame.event == RawCastEvent::Close { if frame.event == RawCastEvent::Close {
@@ -221,7 +197,6 @@ impl ConnectionManager {
if let Some(tx) = self.tracker.inbound_tx.get(&frame.socket_id) { if let Some(tx) = self.tracker.inbound_tx.get(&frame.socket_id) {
match tx.try_send(frame.payload.clone()) { match tx.try_send(frame.payload.clone()) {
Ok(_) => { Ok(_) => {
// Логируем входящие данные (trace чтобы не спамить, но можно временно info)
trace!( trace!(
"📥 [Stream {}] Inbound data: {} bytes", "📥 [Stream {}] Inbound data: {} bytes",
frame.socket_id, frame.socket_id,
@@ -251,7 +226,6 @@ impl ConnectionManager {
pub fn setup_sockets(n_icmp: usize) -> SocketSet<'static> { pub fn setup_sockets(n_icmp: usize) -> SocketSet<'static> {
let mut sockets = SocketSet::new(Vec::with_capacity(64)); let mut sockets = SocketSet::new(Vec::with_capacity(64));
// 1. Создаем ОДИН постоянный UDP сокет для перехвата DNS (чтобы не плодить их)
let mut dns_socket = SocketFactory::create_udp(53); let mut dns_socket = SocketFactory::create_udp(53);
let _ = dns_socket.bind(IpListenEndpoint { let _ = dns_socket.bind(IpListenEndpoint {
addr: None, addr: None,
@@ -259,7 +233,6 @@ impl ConnectionManager {
}); });
sockets.add(dns_socket); sockets.add(dns_socket);
// 2. Создаем ICMP
for _ in 0..n_icmp { for _ in 0..n_icmp {
sockets.add(SocketFactory::create_icmp()); sockets.add(SocketFactory::create_icmp());
} }
@@ -339,8 +312,6 @@ impl ConnectionManager {
} }
} }
// В TCP мы просто открываем "слушающий" сокет на конкретный IP:Port,
// а саму сессию туннеля запустим позже в handle_tcp, когда handshake завершится.
fn intercept_tcp( fn intercept_tcp(
&mut self, &mut self,
src: smoltcp::wire::IpAddress, src: smoltcp::wire::IpAddress,
@@ -361,7 +332,6 @@ impl ConnectionManager {
}); });
let handle = socket_set.add(socket); let handle = socket_set.add(socket);
// Засекаем время создания! Если это левый пакет, мы убьем этот сокет позже.
self.tracker.pending_tcp.insert(handle, StdInstant::now()); self.tracker.pending_tcp.insert(handle, StdInstant::now());
} }
} }
@@ -388,7 +358,6 @@ impl ConnectionManager {
let socket_id = self.tracker.generate_socket_id(); let socket_id = self.tracker.generate_socket_id();
// ВОТ ТУТ МЫ ИЗВЛЕКАЕМ IP И ДЕЛАЕМ LOOKUP С ЛОГИРОВАНИЕМ
let (dst_ip, target) = match dst { let (dst_ip, target) = match dst {
smoltcp::wire::IpAddress::Ipv4(ip) => { smoltcp::wire::IpAddress::Ipv4(ip) => {
let std_ip = std::net::Ipv4Addr::from(ip); let std_ip = std::net::Ipv4Addr::from(ip);
@@ -436,7 +405,6 @@ impl ConnectionManager {
tokio::spawn(async move { tokio::spawn(async move {
info!("📡 [UDP {}] Task started for {}", socket_id, target); info!("📡 [UDP {}] Task started for {}", socket_id, target);
// ВОТ ТУТ ИСПРАВЛЕН ХАРДКОД! Передаем dst_ip вместо нулей
let mut frame = RawCastFrame::connect(LocalProtocol::Udp, socket_id, dst_ip, dst_p); let mut frame = RawCastFrame::connect(LocalProtocol::Udp, socket_id, dst_ip, dst_p);
frame.payload = bytes::Bytes::from(target.clone()); frame.payload = bytes::Bytes::from(target.clone());
@@ -471,8 +439,6 @@ impl ConnectionManager {
} }
} }
// --- ЦИКЛ ОБРАБОТКИ СОКЕТОВ (Pumping) ---
pub fn process_sockets(&mut self, socket_set: &mut SocketSet) { pub fn process_sockets(&mut self, socket_set: &mut SocketSet) {
for (handle, socket) in socket_set.iter_mut() { for (handle, socket) in socket_set.iter_mut() {
if let Some(s) = tcp::Socket::downcast_mut(socket) { if let Some(s) = tcp::Socket::downcast_mut(socket) {
@@ -480,8 +446,6 @@ impl ConnectionManager {
} else if let Some(s) = udp::Socket::downcast_mut(socket) { } else if let Some(s) = udp::Socket::downcast_mut(socket) {
self.handle_udp(handle, s); self.handle_udp(handle, s);
} else if let Some(s) = icmp::Socket::downcast_mut(socket) { } else if let Some(s) = icmp::Socket::downcast_mut(socket) {
// smoltcp использует один тип icmp::Socket для v4 и v6
// разделяем логику по содержимому или конфигурации
self.handle_icmp(handle, s); self.handle_icmp(handle, s);
self.handle_icmpv6(handle, s); self.handle_icmpv6(handle, s);
} }
@@ -493,12 +457,11 @@ impl ConnectionManager {
if let Some(id) = self.tracker.handle_to_id.get(&handle) { if let Some(id) = self.tracker.handle_to_id.get(&handle) {
info!("🏁 [TCP {}] Connection closed", id); info!("🏁 [TCP {}] Connection closed", id);
} }
self.tracker.pending_tcp.remove(&handle); // Чистим self.tracker.pending_tcp.remove(&handle);
self.tracker.queue_removal(handle); self.tracker.queue_removal(handle);
return; return;
} }
// УБИРАЕМ GHOST SOCKETS: Если сокет висит в Listen/SynReceived больше 20 секунд — убиваем!
if matches!(socket.state(), tcp::State::Listen | tcp::State::SynReceived) { if matches!(socket.state(), tcp::State::Listen | tcp::State::SynReceived) {
if let Some(created_at) = self.tracker.pending_tcp.get(&handle) { if let Some(created_at) = self.tracker.pending_tcp.get(&handle) {
if created_at.elapsed() > std::time::Duration::from_secs(20) { if created_at.elapsed() > std::time::Duration::from_secs(20) {
@@ -510,11 +473,10 @@ impl ConnectionManager {
self.tracker.queue_removal(handle); self.tracker.queue_removal(handle);
} }
} }
return; // Сокет еще не Established, делать тут пока нечего return;
} }
if socket.state() == tcp::State::Established { if socket.state() == tcp::State::Established {
// Только что подключился! Удаляем из pending и переводим в active_tcp
if self.tracker.pending_tcp.remove(&handle).is_some() if self.tracker.pending_tcp.remove(&handle).is_some()
|| !self.tracker.active_tcp.contains_key(&handle) || !self.tracker.active_tcp.contains_key(&handle)
{ {
@@ -569,7 +531,6 @@ impl ConnectionManager {
} }
} }
// Проталкиваем активные соединения
if let Some(conn) = self.tracker.active_tcp.get_mut(&handle) { if let Some(conn) = self.tracker.active_tcp.get_mut(&handle) {
if !conn.tick(socket) { if !conn.tick(socket) {
info!("⚠️ [TCP] Tick failed, aborting handle {:?}", handle); info!("⚠️ [TCP] Tick failed, aborting handle {:?}", handle);
@@ -581,11 +542,9 @@ impl ConnectionManager {
fn handle_udp(&mut self, handle: SocketHandle, socket: &mut udp::Socket) { fn handle_udp(&mut self, handle: SocketHandle, socket: &mut udp::Socket) {
self.tracker.last_activity.insert(handle, StdInstant::now()); self.tracker.last_activity.insert(handle, StdInstant::now());
// 1. DNS перехват с логированием
if socket.endpoint().port == 53 { if socket.endpoint().port == 53 {
while let Ok((data, meta)) = socket.recv() { while let Ok((data, meta)) = socket.recv() {
if let Some(res) = self.resolver.process_dns_query(data) { if let Some(res) = self.resolver.process_dns_query(data) {
// Используем debug, чтобы не заливать консоль, но видеть активность
debug!("🔍 [DNS] Resolved query for client {}", meta.endpoint); debug!("🔍 [DNS] Resolved query for client {}", meta.endpoint);
if let Err(e) = socket.send_slice(&res, meta) { if let Err(e) = socket.send_slice(&res, meta) {
warn!("❌ [DNS] Failed to send response: {:?}", e); warn!("❌ [DNS] Failed to send response: {:?}", e);
@@ -595,7 +554,6 @@ impl ConnectionManager {
return; return;
} }
// 2. Жизненный цикл обычных UDP сессий
if let Some(conn) = self.tracker.active_udp.get_mut(&handle) { if let Some(conn) = self.tracker.active_udp.get_mut(&handle) {
if !conn.tick(socket) { if !conn.tick(socket) {
if let Some(socket_id) = self.tracker.handle_to_id.get(&handle) { if let Some(socket_id) = self.tracker.handle_to_id.get(&handle) {
@@ -621,7 +579,6 @@ impl ConnectionManager {
smoltcp::wire::Icmpv4Message::EchoRequest => { smoltcp::wire::Icmpv4Message::EchoRequest => {
info!("🏓 [ICMPv4] Ping Request from {}", src_addr); info!("🏓 [ICMPv4] Ping Request from {}", src_addr);
// Формируем ответ (Echo Reply)
let mut reply_data = data.to_vec(); let mut reply_data = data.to_vec();
let mut reply_pkt = let mut reply_pkt =
smoltcp::wire::Icmpv4Packet::new_unchecked(&mut reply_data); smoltcp::wire::Icmpv4Packet::new_unchecked(&mut reply_data);
@@ -654,9 +611,8 @@ impl ConnectionManager {
match socket.recv() { match socket.recv() {
Ok((data, src_addr)) => { Ok((data, src_addr)) => {
// 1. Извлекаем конкретно Ipv6Address из перечисления IpAddress
let smoltcp::wire::IpAddress::Ipv6(ipv6_src) = src_addr else { let smoltcp::wire::IpAddress::Ipv6(ipv6_src) = src_addr else {
return; // Если пришел не IPv6, выходим return;
}; };
let Ok(pkt) = smoltcp::wire::Icmpv6Packet::new_checked(data) else { let Ok(pkt) = smoltcp::wire::Icmpv6Packet::new_checked(data) else {
@@ -673,7 +629,6 @@ impl ConnectionManager {
reply_pkt.set_msg_type(smoltcp::wire::Icmpv6Message::EchoReply); reply_pkt.set_msg_type(smoltcp::wire::Icmpv6Message::EchoReply);
// 2. РАСЧЕТ ЧЕКСУММЫ (Критический момент)
let my_v6_gateway = let my_v6_gateway =
smoltcp::wire::Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); smoltcp::wire::Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
+5 -34
View File
@@ -44,8 +44,8 @@ impl Engine {
config: Config, config: Config,
caps: DeviceCapabilities, caps: DeviceCapabilities,
dns_handler: DnsHandler, dns_handler: DnsHandler,
tx_to_tunnel: mpsc::Sender<RawCastFrame>, // Куда менеджер будет слать пакеты tx_to_tunnel: mpsc::Sender<RawCastFrame>,
rx_from_tunnel: mpsc::Receiver<RawCastFrame>, // Откуда движок будет читать пакеты rx_from_tunnel: mpsc::Receiver<RawCastFrame>,
) -> Self { ) -> Self {
let now = Engine::current_time(); let now = Engine::current_time();
@@ -82,7 +82,6 @@ impl Engine {
let mut stuck_frame: Option<RawCastFrame> = None; let mut stuck_frame: Option<RawCastFrame> = None;
loop { loop {
// 1. Быстро читаем из TUN
while let Ok(token) = tun_to_engine_rx.try_recv() { while let Ok(token) = tun_to_engine_rx.try_recv() {
self.manager self.manager
.try_create_socket_from_packet(&token, &mut self.socket_set); .try_create_socket_from_packet(&token, &mut self.socket_set);
@@ -91,19 +90,17 @@ impl Engine {
} }
} }
// 2. Пытаемся протолкнуть застрявший кадр
if let Some(frame) = stuck_frame.take() { if let Some(frame) = stuck_frame.take() {
if let Err(returned_frame) = self.manager.try_inject_inbound(frame) { if let Err(returned_frame) = self.manager.try_inject_inbound(frame) {
stuck_frame = Some(returned_frame); stuck_frame = Some(returned_frame);
} }
} }
// 3. Забираем новые кадры из туннеля
if stuck_frame.is_none() { if stuck_frame.is_none() {
while let Ok(frame) = self.rx_from_tunnel.try_recv() { while let Ok(frame) = self.rx_from_tunnel.try_recv() {
if let Err(returned_frame) = self.manager.try_inject_inbound(frame) { if let Err(returned_frame) = self.manager.try_inject_inbound(frame) {
stuck_frame = Some(returned_frame); // Возвращаем пакет в "заначку" stuck_frame = Some(returned_frame);
break; // ТОРМОЗИМ чтение туннеля, пока канал не освободится break;
} }
} }
} }
@@ -118,17 +115,15 @@ impl Engine {
if matches!(result, PollResult::SocketStateChanged) { if matches!(result, PollResult::SocketStateChanged) {
self.manager.cleanup(&mut self.socket_set); self.manager.cleanup(&mut self.socket_set);
continue; // Если стейт изменился, поллим еще раз без задержек continue;
} }
self.manager.cleanup(&mut self.socket_set); self.manager.cleanup(&mut self.socket_set);
// 4. Правильный расчет таймаута без Busy-Loop
let delay = self let delay = self
.interface .interface
.poll_delay(Self::current_time(), &self.socket_set); .poll_delay(Self::current_time(), &self.socket_set);
// Создаем Future ожидания. Если None - ждем бесконечно (std::future::pending)
let sleep_fut = async { let sleep_fut = async {
if let Some(d) = delay { if let Some(d) = delay {
sleep(Duration::from_micros(d.micros())).await; sleep(Duration::from_micros(d.micros())).await;
@@ -137,7 +132,6 @@ impl Engine {
} }
}; };
// 5. Умный select!
if stuck_frame.is_none() { if stuck_frame.is_none() {
tokio::select! { tokio::select! {
_ = sleep_fut => {} _ = sleep_fut => {}
@@ -154,8 +148,6 @@ impl Engine {
} }
} }
} else { } else {
// Если буфер переполнен (stuck_frame), ждем ТОЛЬКО таймер или данные от браузера (ACK),
// чтобы освободить буфер. Из rx_from_tunnel пока не читаем!
tokio::select! { tokio::select! {
_ = sleep_fut => {} _ = sleep_fut => {}
Some(token) = tun_to_engine_rx.recv() => { Some(token) = tun_to_engine_rx.recv() => {
@@ -267,10 +259,6 @@ impl Engine {
} }
} }
// ============================================================================
// КОНФИГУРАЦИЯ ДВИЖКА
// ============================================================================
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct EngineConfig { pub struct EngineConfig {
pub remote_address: String, pub remote_address: String,
@@ -311,17 +299,12 @@ impl EngineConfig {
} }
} }
// ============================================================================
// БИЛДЕР ДВИЖКА
// ============================================================================
pub struct EngineBuilder { pub struct EngineBuilder {
config: EngineConfig, config: EngineConfig,
tun_device: Option<Tun>, tun_device: Option<Tun>,
} }
impl EngineBuilder { impl EngineBuilder {
/// Инициализируем билдер на основе готового конфига
pub fn new(config: EngineConfig) -> Self { pub fn new(config: EngineConfig) -> Self {
Self { Self {
config, config,
@@ -329,7 +312,6 @@ impl EngineBuilder {
} }
} }
/// Передаем TUN интерфейс (зависит от платформы, поэтому не в конфиге)
pub fn with_tun(mut self, tun: Tun) -> Self { pub fn with_tun(mut self, tun: Tun) -> Self {
self.tun_device = Some(tun); self.tun_device = Some(tun);
self self
@@ -343,13 +325,11 @@ impl EngineBuilder {
self.config self.config
); );
// 1. Инициализация DNS
let mut dns_handler = DnsHandler::new(&self.config.cache_path); let mut dns_handler = DnsHandler::new(&self.config.cache_path);
if let Err(e) = dns_handler.init().await { if let Err(e) = dns_handler.init().await {
error!("Failed to initialize DNS blocklist: {}", e); error!("Failed to initialize DNS blocklist: {}", e);
} }
// 2. Настройка системного роутинга (Опционально)
if self.config.setup_routing { if self.config.setup_routing {
info!("Applying platform routing rules..."); info!("Applying platform routing rules...");
if let Err(e) = setup_platform_routing(&self.config.remote_address) { if let Err(e) = setup_platform_routing(&self.config.remote_address) {
@@ -359,27 +339,19 @@ impl EngineBuilder {
info!("Platform routing setup skipped via config."); info!("Platform routing setup skipped via config.");
} }
// 3. Конфигурация интерфейса smoltcp
let smol_config = Config::new(smoltcp::wire::HardwareAddress::Ip); let smol_config = Config::new(smoltcp::wire::HardwareAddress::Ip);
let mut caps = DeviceCapabilities::default(); let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = self.config.mtu; caps.max_transmission_unit = self.config.mtu;
caps.medium = smoltcp::phy::Medium::Ip; caps.medium = smoltcp::phy::Medium::Ip;
// --- СОЗДАЕМ КАНАЛЫ СВЯЗИ ДВИЖОК <-> ТУННЕЛЬ ---
// tx_to_tunnel: Движок пишет, ClientHandler читает (исходящий трафик)
let (tx_to_tunnel, rx_for_client_handler) = let (tx_to_tunnel, rx_for_client_handler) =
mpsc::channel(NetworkConfig::global().muxer_capacity); mpsc::channel(NetworkConfig::global().muxer_capacity);
// tx_to_engine: ClientHandler пишет, Движок читает (входящий трафик)
let (tx_for_client_handler, rx_from_tunnel) = let (tx_for_client_handler, rx_from_tunnel) =
mpsc::channel(NetworkConfig::global().muxer_capacity); mpsc::channel(NetworkConfig::global().muxer_capacity);
// 4. Подключение к серверу
info!("Establishing secure tunnel to proxy server..."); info!("Establishing secure tunnel to proxy server...");
// ВАЖНО: Тебе нужно добавить эти аргументы в `ClientHandler::connect`,
// чтобы он знал, откуда забирать `RawCastFrame` для отправки на сервер,
// и куда отдавать `RawCastFrame`, прилетевшие от сервера.
ClientHandler::connect( ClientHandler::connect(
&self.config.remote_address, &self.config.remote_address,
rx_for_client_handler, rx_for_client_handler,
@@ -390,7 +362,6 @@ impl EngineBuilder {
info!("Secure tunnel established, Muxer is ready."); info!("Secure tunnel established, Muxer is ready.");
// 5. Инициализация и настройка Engine (передаем каналы)
let mut engine = Engine::new(smol_config, caps, dns_handler, tx_to_tunnel, rx_from_tunnel); let mut engine = Engine::new(smol_config, caps, dns_handler, tx_to_tunnel, rx_from_tunnel);
engine.set_any_ip(self.config.any_ip); engine.set_any_ip(self.config.any_ip);
+8 -15
View File
@@ -10,11 +10,8 @@ use tokio::net::UdpSocket;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tokio::time::timeout; use tokio::time::timeout;
/// Тайм-аут бездействия. Если данных нет 5 минут — закрываем мост.
const IDLE_TIMEOUT: Duration = Duration::from_secs(300); const IDLE_TIMEOUT: Duration = Duration::from_secs(300);
/// RAII Guard: гарантирует удаление стрима из Muxer при любом выходе из функции,
/// даже если произошла паника (panic) или ранняя ошибка.
struct StreamGuard { struct StreamGuard {
stream_id: u32, stream_id: u32,
muxer: Arc<Muxer>, muxer: Arc<Muxer>,
@@ -37,7 +34,6 @@ pub(crate) async fn run_tcp_bridge<R, W>(
R: tokio::io::AsyncReadExt + Unpin, R: tokio::io::AsyncReadExt + Unpin,
W: tokio::io::AsyncWriteExt + Unpin, W: tokio::io::AsyncWriteExt + Unpin,
{ {
// Инициализируем защитника ресурсов
let _guard = StreamGuard { let _guard = StreamGuard {
stream_id, stream_id,
muxer: muxer.clone(), muxer: muxer.clone(),
@@ -46,15 +42,14 @@ pub(crate) async fn run_tcp_bridge<R, W>(
let mut buf = BytesMut::with_capacity(NetworkConfig::global().tcp_buffer_size); let mut buf = BytesMut::with_capacity(NetworkConfig::global().tcp_buffer_size);
loop { loop {
// Оборачиваем весь select в таймаут, чтобы убивать "мертвые" задачи
let select_res = timeout(IDLE_TIMEOUT, async { let select_res = timeout(IDLE_TIMEOUT, async {
tokio::select! { tokio::select! {
// Данные из реального TCP сокета -> в Muxer
res = reader.read_buf(&mut buf) => { res = reader.read_buf(&mut buf) => {
match res { match res {
Ok(0) => { Ok(0) => {
debug!(stream_id, "TCP Socket reached EOF"); debug!(stream_id, "TCP Socket reached EOF");
return Ok(false); // Выход из цикла return Ok(false);
} }
Ok(_) => { Ok(_) => {
let msg = MuxMessage { let msg = MuxMessage {
@@ -62,7 +57,7 @@ pub(crate) async fn run_tcp_bridge<R, W>(
frame_type: FrameType::Data, frame_type: FrameType::Data,
data: buf.split().freeze(), data: buf.split().freeze(),
}; };
// Если туннель закрыт — выходим
if muxer.send_to_network(msg).is_err() { if muxer.send_to_network(msg).is_err() {
return Ok(false); return Ok(false);
} }
@@ -75,7 +70,7 @@ pub(crate) async fn run_tcp_bridge<R, W>(
} }
} }
// Данные из туннеля -> в реальный TCP сокет
maybe_data = v_rx.recv() => { maybe_data = v_rx.recv() => {
match maybe_data { match maybe_data {
Some(data) => { Some(data) => {
@@ -98,7 +93,7 @@ pub(crate) async fn run_tcp_bridge<R, W>(
match select_res { match select_res {
Ok(Ok(true)) => continue, Ok(Ok(true)) => continue,
Ok(Ok(false)) => break, // Штатное закрытие Ok(Ok(false)) => break,
Ok(Err(e)) => { Ok(Err(e)) => {
debug!(stream_id, "Bridge closing due to error: {}", e); debug!(stream_id, "Bridge closing due to error: {}", e);
break; break;
@@ -110,14 +105,12 @@ pub(crate) async fn run_tcp_bridge<R, W>(
} }
} }
// Попытка вежливо сказать удаленной стороне, что мы закрылись
let _ = muxer.send_to_network(MuxMessage { let _ = muxer.send_to_network(MuxMessage {
stream_id, stream_id,
frame_type: FrameType::Close, frame_type: FrameType::Close,
data: Bytes::new(), data: Bytes::new(),
}); });
// Небольшая пауза, чтобы пакет Close успел уйти в сеть перед тем, как Guard удалит стрим
tokio::time::sleep(Duration::from_millis(50)).await; tokio::time::sleep(Duration::from_millis(50)).await;
} }
@@ -140,7 +133,7 @@ pub(crate) async fn run_udp_bridge(
loop { loop {
let select_res = timeout(IDLE_TIMEOUT, async { let select_res = timeout(IDLE_TIMEOUT, async {
tokio::select! { tokio::select! {
// Из интернета в туннель
res = socket.recv(&mut buf) => { res = socket.recv(&mut buf) => {
match res { match res {
Ok(n) if n > 0 => { Ok(n) if n > 0 => {
@@ -159,7 +152,7 @@ pub(crate) async fn run_udp_bridge(
} }
} }
// Из туннеля в интернет
maybe_data = v_rx.recv() => { maybe_data = v_rx.recv() => {
match maybe_data { match maybe_data {
Some(data) => { Some(data) => {
@@ -178,7 +171,7 @@ pub(crate) async fn run_udp_bridge(
match select_res { match select_res {
Ok(Ok(true)) => continue, Ok(Ok(true)) => continue,
_ => break, // Любая ошибка или таймаут закрывают UDP мост _ => break,
} }
} }
+4 -15
View File
@@ -41,7 +41,6 @@ impl SessionManager {
pub fn get_or_create(&self, session_id: &str) -> Arc<Muxer> { pub fn get_or_create(&self, session_id: &str) -> Arc<Muxer> {
self.sessions self.sessions
.entry(session_id.to_string()) .entry(session_id.to_string())
// ОБНОВЛЕНО: Передаем session_id в конструктор Муксера
.or_insert_with(|| Arc::new(Muxer::new(false, session_id.to_string()))) .or_insert_with(|| Arc::new(Muxer::new(false, session_id.to_string())))
.clone() .clone()
} }
@@ -121,7 +120,6 @@ impl ClientHandler {
let (inbound, outbound) = stream.into_split(); let (inbound, outbound) = stream.into_split();
let mut conn = Connection::new_raw(inbound, outbound); let mut conn = Connection::new_raw(inbound, outbound);
// TLS Handshake
let ch = conn let ch = conn
.codec .codec
.make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com") .make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
@@ -152,7 +150,6 @@ impl ClientHandler {
info!("{} TLS Handshake complete.", leg_name); info!("{} TLS Handshake complete.", leg_name);
let handshake_payload = Bytes::from(format!("{}:{}", session_id, leg_id)); let handshake_payload = Bytes::from(format!("{}:{}", session_id, leg_id));
// Шифруем кастомный Handshake
let encrypted_handshake = conn let encrypted_handshake = conn
.codec .codec
.encrypt_data(0, FrameType::Handshake, handshake_payload) .encrypt_data(0, FrameType::Handshake, handshake_payload)
@@ -177,7 +174,7 @@ impl ClientHandler {
std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Client)); std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Client));
let engine = TunnelEngine { let engine = TunnelEngine {
leg_id, // ОБНОВЛЕНО: Передаем leg_id, чтобы движок писал стату leg_id,
inbound: conn.inbound, inbound: conn.inbound,
outbound: conn.outbound, outbound: conn.outbound,
codec: conn.codec, codec: conn.codec,
@@ -185,7 +182,7 @@ impl ClientHandler {
control_rx, control_rx,
data_rx, data_rx,
handler, handler,
muxer: muxer.clone(), // Не забудь передать muxer (он нужен движку для record_leg_rx) muxer: muxer.clone(),
}; };
engine.run().await.map_err(|e| e.to_string())?; engine.run().await.map_err(|e| e.to_string())?;
@@ -200,20 +197,17 @@ impl ClientHandler {
let session_id = SessionManager::generate_id(); let session_id = SessionManager::generate_id();
info!("🔑 Generated Master Session ID: {}", session_id); info!("🔑 Generated Master Session ID: {}", session_id);
// ОБНОВЛЕНО: Передаем session_id в Муксер
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());
info!("🚀 Netrunner Multi-Path Tunnel Initializing (10 Legs max)."); info!("🚀 Netrunner Multi-Path Tunnel Initializing (10 Legs max).");
// === ДИНАМИЧЕСКИЙ ПУЛ НА 10 НОГ ===
for id in 0..10 { for id in 0..10 {
let addr = remote_proxy_addr.to_string(); let addr = remote_proxy_addr.to_string();
let m = muxer.clone(); let m = muxer.clone();
let sid = session_id.clone(); let sid = session_id.clone();
tokio::spawn(async move { tokio::spawn(async move {
// Плавный запуск (staggered start), чтобы не задудосить свой же сервер
tokio::time::sleep(std::time::Duration::from_millis(id as u64 * 250)).await; tokio::time::sleep(std::time::Duration::from_millis(id as u64 * 250)).await;
loop { loop {
if let Err(e) = Self::establish_leg(&addr, id, m.clone(), &sid).await { if let Err(e) = Self::establish_leg(&addr, id, m.clone(), &sid).await {
@@ -224,7 +218,6 @@ impl ClientHandler {
}); });
} }
// === ДЕМОН СТАТИСТИКИ (КЛИЕНТ) ===
let m_weak = Arc::downgrade(&muxer); let m_weak = Arc::downgrade(&muxer);
tokio::spawn(async move { tokio::spawn(async move {
while let Some(m_stats) = m_weak.upgrade() { while let Some(m_stats) = m_weak.upgrade() {
@@ -236,7 +229,6 @@ impl ClientHandler {
} }
}); });
// === ЛОКАЛЬНАЯ МАРШРУТИЗАЦИЯ (RawCast -> Nrxp) ===
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 {
@@ -472,7 +464,6 @@ impl TunnelHandler for ServerHandler {
} }
}; };
// РЕГИСТРИРУЕМ НОГУ
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);
@@ -494,9 +485,8 @@ impl TunnelHandler for ServerHandler {
} }
}); });
// ЗАПУСКАЕМ ДВИЖОК
let engine = TunnelEngine { let engine = TunnelEngine {
leg_id, // ОБНОВЛЕНО leg_id,
inbound: self.conn.inbound, inbound: self.conn.inbound,
outbound: self.conn.outbound, outbound: self.conn.outbound,
codec: self.conn.codec, codec: self.conn.codec,
@@ -504,12 +494,11 @@ impl TunnelHandler for ServerHandler {
control_rx, control_rx,
data_rx, data_rx,
handler, handler,
muxer: muxer.clone(), // ОБНОВЛЕНО muxer: muxer.clone(),
}; };
let _ = engine.run().await; let _ = engine.run().await;
// ОЧИСТКА ПОСЛЕ ОТКЛЮЧЕНИЯ НОГИ
muxer.remove_leg(leg_id); muxer.remove_leg(leg_id);
if muxer.active_legs_count() == 0 { if muxer.active_legs_count() == 0 {
self.session_manager.remove(&session_id); self.session_manager.remove(&session_id);
+1 -2
View File
@@ -41,7 +41,6 @@ impl TunnelEngine {
let data_rx = self.data_rx; let data_rx = self.data_rx;
let handler = self.handler; let handler = self.handler;
// === ИЗВЛЕКАЕМ НОВЫЕ ПОЛЯ ===
let leg_id = self.leg_id; let leg_id = self.leg_id;
let muxer = self.muxer.clone(); let muxer = self.muxer.clone();
@@ -76,7 +75,7 @@ impl TunnelEngine {
return Err::<(), String>("EOF".into()); return Err::<(), String>("EOF".into());
} }
// === РЕГИСТРИРУЕМ ВХОДЯЩИЙ ТРАФИК ДЛЯ СТАТИСТИКИ ===
muxer.record_leg_rx(leg_id, n as u64); muxer.record_leg_rx(leg_id, n as u64);
let mut frames = Vec::new(); let mut frames = Vec::new();
-1
View File
@@ -149,7 +149,6 @@ impl StreamHandler {
} }
async fn on_data(&self, stream_id: u32, payload: Bytes) { async fn on_data(&self, stream_id: u32, payload: Bytes) {
// Здесь info может быть избыточным при большой нагрузке, но для отладки полезно
debug!(stream_id, "📦 [TCP Data] Size: {} bytes", payload.len()); debug!(stream_id, "📦 [TCP Data] Size: {} bytes", payload.len());
self.muxer.dispatch_to_local(stream_id, payload); self.muxer.dispatch_to_local(stream_id, payload);
} }
+1 -1
View File
@@ -1,4 +1,4 @@
mod bridge; // Все модули делаем приватными mod bridge;
mod connection; mod connection;
mod engine; mod engine;
mod handler; mod handler;
+26 -26
View File
@@ -9,7 +9,7 @@ use tokio::sync::mpsc::Sender;
use crate::net::network::NetworkConfig; use crate::net::network::NetworkConfig;
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,
@@ -56,14 +56,14 @@ pub struct MuxMessage {
#[derive(Clone)] #[derive(Clone)]
pub struct Muxer { pub struct Muxer {
legs: Arc<DashMap<u32, MuxLeg>>, legs: Arc<DashMap<u32, MuxLeg>>,
// Храним канал + статистику стрима
streams: Arc<DashMap<u32, (Sender<Bytes>, Arc<StreamStats>)>>, streams: Arc<DashMap<u32, (Sender<Bytes>, Arc<StreamStats>)>>,
id_gen: Arc<IdGenerator>, id_gen: Arc<IdGenerator>,
session_id: Arc<String>, session_id: Arc<String>,
} }
impl Muxer { impl Muxer {
// Конструктор теперь принимает session_id
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()),
@@ -79,7 +79,7 @@ impl Muxer {
control_tx: Sender<MuxMessage>, control_tx: Sender<MuxMessage>,
data_tx: Sender<MuxMessage>, data_tx: Sender<MuxMessage>,
) { ) {
// Ограничение: максимум 10 физических ног
if self.legs.len() >= 10 { if self.legs.len() >= 10 {
warn!( warn!(
leg_id, leg_id,
@@ -115,43 +115,43 @@ 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.legs let mut candidates: Vec<(u32, MuxLeg)> = self.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();
} }
// --- МАГИЯ ЗДЕСЬ ---
// Сортируем кандидатов по RTT (от меньшего к большему)
// Ноги с RTT = 0 (еще не проверенные) или таймаутом считаем медленными
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 }
}); });
// Выбираем из ТОП-2 лучших ног по RTT, используя stream_id для аффинити
// Это защитит от ситуации, когда все стримы прыгнут на одну ногу с RTT 10мс
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 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;
// Передаем stream_id в select_leg
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())?;
@@ -166,7 +166,7 @@ impl Muxer {
match target_tx.try_send(message) { match target_tx.try_send(message) {
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) { if let Some(stream_ref) = self.streams.get(&stream_id) {
stream_ref stream_ref
@@ -178,7 +178,7 @@ impl Muxer {
Ok(()) Ok(())
} }
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => { Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
// Если очередь переполнена - просто дропаем пакет (Backpressure)
warn!( warn!(
leg_id, leg_id,
"MUXER: Network queue full! Dropping outbound packet." "MUXER: Network queue full! Dropping outbound packet."
@@ -241,7 +241,7 @@ impl Muxer {
} }
pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) { pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
// Получаем клон Sender и Stats из DashMap
let stream_opt = self let stream_opt = self
.streams .streams
.get(&stream_id) .get(&stream_id)
@@ -250,7 +250,7 @@ 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;
// Используем try_send вместо send().await
match tx.try_send(data) { match tx.try_send(data) {
Ok(_) => { Ok(_) => {
stats.rx_bytes.fetch_add(size, Ordering::Relaxed); stats.rx_bytes.fetch_add(size, Ordering::Relaxed);
@@ -275,7 +275,7 @@ 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) {
// Создаем снапшот ног, чтобы не держать lock DashMap слишком долго
let legs: Vec<(u32, Sender<MuxMessage>)> = self.legs.iter() let legs: Vec<(u32, Sender<MuxMessage>)> = self.legs.iter()
.map(|k| (*k.key(), k.value().control_tx.clone())) .map(|k| (*k.key(), k.value().control_tx.clone()))
.collect(); .collect();
@@ -284,7 +284,7 @@ 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);
// Регистрируем временный стрим для ответа на PING
self.register_stream(probe_stream_id, probe_tx); self.register_stream(probe_stream_id, probe_tx);
let msg = MuxMessage { let msg = MuxMessage {
@@ -295,9 +295,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;
@@ -307,7 +307,7 @@ pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
} }
} }
_ => { _ => {
// Если таймаут — задираем RTT до небес, чтобы select_leg ее не выбирал
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)");
@@ -316,12 +316,12 @@ pub fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
} }
} }
// Обязательная очистка, чтобы не было утечки памяти в DashMap streams
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;
@@ -337,7 +337,7 @@ 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: {}]",
+29 -17
View File
@@ -22,13 +22,10 @@ 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);
// Инициализируем глобальный конфиг сети (MTU, размеры буферов)
NetworkConfig::init_global(1500); NetworkConfig::init_global(1500);
match self.role { match self.role {
ConnectionRole::Client => { ConnectionRole::Client => {
// В новой архитектуре клиент запускается через EngineBuilder + TUN.
// Структура Network теперь используется только для запуска Сервера.
error!("Client mode cannot be run via Network::run anymore."); error!("Client mode cannot be run via Network::run anymore.");
error!("Please use EngineBuilder to initialize the TUN client."); error!("Please use EngineBuilder to initialize the TUN client.");
panic!("Legacy SOCKS5 client mode has been removed."); panic!("Legacy SOCKS5 client mode has been removed.");
@@ -47,7 +44,7 @@ impl Network {
if let Ok((stream, client_addr)) = res { if let Ok((stream, client_addr)) = res {
info!("New connection from {}", client_addr); info!("New connection from {}", client_addr);
// Создаем соединение (init = true для сервера)
let conn = Connection::new(stream, true); let conn = Connection::new(stream, true);
let handler = ServerHandler::new(conn); let handler = ServerHandler::new(conn);
@@ -72,40 +69,55 @@ pub struct NetworkConfig {
pub max_wire_frame_size: usize, pub max_wire_frame_size: usize,
pub safe_payload_size: usize, pub safe_payload_size: usize,
// --- ИЗМЕНЕНИЯ ЗДЕСЬ --- pub tcp_buffer_size: usize,
pub tcp_buffer_size: usize, // Размер буфера для системного tokio::TcpStream
pub udp_buffer_size: usize, pub udp_buffer_size: usize,
pub muxer_capacity: usize, // Глобальные каналы (Muxer <-> Engine) pub muxer_capacity: usize,
pub tcp_stream_capacity: usize, // Локальные каналы 1 сокета (Engine <-> Muxer) pub tcp_stream_capacity: usize,
pub udp_stream_capacity: usize, // Локальные каналы 1 сокета (Engine <-> Muxer) pub udp_stream_capacity: usize,
pub smoltcp_socket_buf: usize, pub smoltcp_socket_buf: usize,
pub tcp_chunk_size: usize, pub tcp_chunk_size: usize,
}
pub tcp_buf_heavy: usize,
pub tcp_buf_light: usize,
pub udp_buf_heavy: usize,
pub udp_meta_heavy: usize,
pub udp_buf_light: usize,
pub udp_meta_light: usize,
}
impl NetworkConfig { impl NetworkConfig {
pub fn new(system_mtu: usize) -> Self { pub fn new(system_mtu: usize) -> Self {
let transport_overhead = 48; let transport_overhead = 48;
let max_wire_frame = system_mtu.saturating_sub(transport_overhead); let max_wire_frame = system_mtu.saturating_sub(transport_overhead);
let safe_payload = max_wire_frame.saturating_sub(64); let safe_payload = max_wire_frame.saturating_sub(64);
let chunk_size = 16 * 1024;
let heavy_buf = 1024 * 1024;
Self { Self {
mtu: system_mtu, mtu: system_mtu,
max_wire_frame_size: max_wire_frame, max_wire_frame_size: max_wire_frame,
safe_payload_size: safe_payload, safe_payload_size: safe_payload,
// Буферы ОС/Tokio (держим с запасом под быстрые всплески) tcp_buffer_size: heavy_buf,
tcp_buffer_size: 1024 * 1024, udp_buffer_size: 256 * 1024,
udp_buffer_size: 64 * 1024,
muxer_capacity: 512, muxer_capacity: 512,
tcp_stream_capacity: 16, tcp_stream_capacity: 16,
udp_stream_capacity: 128, udp_stream_capacity: 128,
// Настройки smoltcp smoltcp_socket_buf: heavy_buf,
smoltcp_socket_buf: 256 * 1024, tcp_chunk_size: chunk_size,
tcp_chunk_size: 16 * 1024,
tcp_buf_heavy: heavy_buf,
tcp_buf_light: 64 * 1024,
udp_buf_heavy: heavy_buf,
udp_meta_heavy: 512,
udp_buf_light: 32 * 1024,
udp_meta_light: 16,
} }
} }
-3
View File
@@ -74,14 +74,11 @@ impl Frame {
} }
} }
// 3. БЕЗОПАСНАЯ СЕРИАЛИЗАЦИЯ С ИСПРАВЛЕННЫМ БАГОМ РАЗМЕРА
pub(crate) fn into_bytes(mut self, auth_key: &[u8; 16]) -> BytesMut { pub(crate) fn into_bytes(mut self, auth_key: &[u8; 16]) -> BytesMut {
let generated_padding = Padding::generate_padding(); let generated_padding = Padding::generate_padding();
// Обновляем заголовок реальной длиной сгенерированного паддинга
self.header.padding_len = generated_padding.len; self.header.padding_len = generated_padding.len;
// Теперь размер считается правильно!
let total_size = let total_size =
FRAME_HEADER_SIZE as usize + self.payload.len() + generated_padding.len as usize; FRAME_HEADER_SIZE as usize + self.payload.len() + generated_padding.len as usize;
let mut buf = BytesMut::with_capacity(total_size); let mut buf = BytesMut::with_capacity(total_size);
+1 -2
View File
@@ -1,9 +1,8 @@
// Скрываем модули
mod bridge; mod bridge;
mod codec; mod codec;
mod errors; mod errors;
mod frame; mod frame;
// Экспортируем для остального ядра только необходимые типы
pub(crate) use codec::Codec; pub(crate) use codec::Codec;
pub(crate) use errors::{ErrorAction, ErrorStage, TlsError}; pub(crate) use errors::{ErrorAction, ErrorStage, TlsError};
pub(crate) use frame::{Frame, FrameType, FRAME_HEADER_SIZE, MAX_PADDING_SIZE}; pub(crate) use frame::{Frame, FrameType, FRAME_HEADER_SIZE, MAX_PADDING_SIZE};
+3 -20
View File
@@ -1,6 +1,6 @@
use bytes::Bytes; use bytes::Bytes;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
// Добавили импорт логгеров для детального трейсинга
use netrunner_logger::{debug, trace}; use netrunner_logger::{debug, trace};
use crate::{ use crate::{
@@ -11,16 +11,11 @@ use crate::{
pub struct RawCastAdapter; pub struct RawCastAdapter;
impl RawCastAdapter { impl RawCastAdapter {
/// Перевод из RawCast (от локального TUN/smoltcp) в NRXP (в ядро/сеть)
pub(crate) fn to_nrxp(raw: RawCastFrame) -> Result<Frame, String> { pub(crate) fn to_nrxp(raw: RawCastFrame) -> Result<Frame, String> {
// Кастим ID сокета
let stream_id = raw.socket_id as u32; let stream_id = raw.socket_id as u32;
let (frame_type, payload) = match (raw.protocol, raw.event) { let (frame_type, payload) = match (raw.protocol, raw.event) {
// Открытие TCP соединения
(LocalProtocol::Tcp, RawCastEvent::Connect) => { (LocalProtocol::Tcp, RawCastEvent::Connect) => {
// Если payload не пустой (ConnectionManager положил туда домен) - используем его.
// Иначе фоллбэк: собираем из сырого IP и порта.
let final_payload = if !raw.payload.is_empty() { let final_payload = if !raw.payload.is_empty() {
raw.payload raw.payload
} else { } else {
@@ -35,9 +30,8 @@ impl RawCastAdapter {
(FrameType::Connect, final_payload) (FrameType::Connect, final_payload)
} }
// Открытие UDP сессии
(LocalProtocol::Udp, RawCastEvent::Connect) => { (LocalProtocol::Udp, RawCastEvent::Connect) => {
// Аналогичная логика защиты переданного таргета для UDP
let final_payload = if !raw.payload.is_empty() { let final_payload = if !raw.payload.is_empty() {
raw.payload raw.payload
} else { } else {
@@ -53,31 +47,23 @@ impl RawCastAdapter {
(FrameType::UdpConnect, final_payload) (FrameType::UdpConnect, final_payload)
} }
// Передача данных (TCP)
(LocalProtocol::Tcp, RawCastEvent::Data) => (FrameType::Data, raw.payload), (LocalProtocol::Tcp, RawCastEvent::Data) => (FrameType::Data, raw.payload),
// Передача данных (UDP)
(LocalProtocol::Udp, RawCastEvent::Data) => (FrameType::UdpData, raw.payload), (LocalProtocol::Udp, RawCastEvent::Data) => (FrameType::UdpData, raw.payload),
// Закрытие соединения (одинаково для TCP и UDP)
(_, RawCastEvent::Close) => { (_, RawCastEvent::Close) => {
trace!("🧩 [Adapter] Stream {}: Packing Close frame", stream_id); trace!("🧩 [Adapter] Stream {}: Packing Close frame", stream_id);
(FrameType::Close, Bytes::new()) (FrameType::Close, Bytes::new())
} }
// ICMP пока не поддерживается в NRXP, отбрасываем
(LocalProtocol::Icmp, _) => { (LocalProtocol::Icmp, _) => {
return Err("ICMP protocol is not supported by NRXP core".into()); return Err("ICMP protocol is not supported by NRXP core".into());
} }
}; };
// Используем твой новый удобный конструктор Frame!
Ok(Frame::new(stream_id, frame_type, payload)) Ok(Frame::new(stream_id, frame_type, payload))
} }
/// Перевод из NRXP (от сервера) обратно в RawCast (в smoltcp)
///
/// ВАЖНО: Так как NRXP Frame не содержит IP и Port (только stream_id),
/// локальный клиент должен помнить, какому stream_id какой IP/Port принадлежит.
pub(crate) fn from_nrxp( pub(crate) fn from_nrxp(
nrxp_frame: Frame, nrxp_frame: Frame,
dst_ip: Ipv4Addr, dst_ip: Ipv4Addr,
@@ -92,7 +78,6 @@ impl RawCastAdapter {
}; };
let event = match nrxp_frame.header.frame_type { let event = match nrxp_frame.header.frame_type {
// Сервер прислал успешный коннект
FrameType::Connect | FrameType::UdpConnect => { FrameType::Connect | FrameType::UdpConnect => {
trace!( trace!(
"🧩 [Adapter] Stream {}: Unpacked Connect Ack from server", "🧩 [Adapter] Stream {}: Unpacked Connect Ack from server",
@@ -101,10 +86,8 @@ impl RawCastAdapter {
RawCastEvent::Connect RawCastEvent::Connect
} }
// Сервер прислал данные
FrameType::Data | FrameType::UdpData => RawCastEvent::Data, FrameType::Data | FrameType::UdpData => RawCastEvent::Data,
// Сервер закрыл соединение
FrameType::Close => { FrameType::Close => {
trace!( trace!(
"🧩 [Adapter] Stream {}: Unpacked Close signal from server", "🧩 [Adapter] Stream {}: Unpacked Close signal from server",
-1
View File
@@ -93,7 +93,6 @@ impl Parser for RawCastFrame {
return false; return false;
} }
// читаем длину payload без сдвига буфера
let payload_len_pos = LOCAL_HEADER_SIZE; let payload_len_pos = LOCAL_HEADER_SIZE;
let payload_len = let payload_len =
u16::from_be_bytes([bytes[payload_len_pos], bytes[payload_len_pos + 1]]) as usize; u16::from_be_bytes([bytes[payload_len_pos], bytes[payload_len_pos + 1]]) as usize;
+1 -1
View File
@@ -14,7 +14,7 @@ struct Args {
fn main() { fn main() {
Logger::init("./logs".into()); Logger::init("./logs".into());
Logger::global().set_level("error"); //set error in prod Logger::global().set_level("error");
let args = Args::parse(); let args = Args::parse();
let net = Network::new(args.host, args.port, ConnectionRole::Server); let net = Network::new(args.host, args.port, ConnectionRole::Server);
+1 -6
View File
@@ -24,16 +24,14 @@ impl Logger {
let mut file_guard = None; let mut file_guard = None;
let mut file_layer = None; let mut file_layer = None;
// Настраиваем запись в файл, если передан путь
if let Some(path) = log_dir { if let Some(path) = log_dir {
// Ротация: новый файл каждый день, префикс "netrunner.log"
let file_appender = tracing_appender::rolling::daily(path, "netrunner.log"); let file_appender = tracing_appender::rolling::daily(path, "netrunner.log");
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender); let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
file_layer = Some( file_layer = Some(
fmt::layer() fmt::layer()
.with_writer(non_blocking) .with_writer(non_blocking)
.with_ansi(false) // В файле цвета не нужны .with_ansi(false)
.with_target(true) .with_target(true)
.with_line_number(true), .with_line_number(true),
); );
@@ -42,7 +40,6 @@ impl Logger {
let registry = tracing_subscriber::registry().with(filter_layer); let registry = tracing_subscriber::registry().with(filter_layer);
// Слой для Android
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
let registry = { let registry = {
let android_layer = tracing_android::layer("NETRUNNER_RUST") let android_layer = tracing_android::layer("NETRUNNER_RUST")
@@ -50,7 +47,6 @@ impl Logger {
registry.with(android_layer) registry.with(android_layer)
}; };
// Слой для консоли (Desktop/Server)
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
let registry = { let registry = {
let fmt_layer = fmt::layer() let fmt_layer = fmt::layer()
@@ -61,7 +57,6 @@ impl Logger {
registry.with(fmt_layer) registry.with(fmt_layer)
}; };
// Добавляем файловый слой, если он был создан
if let Some(f_layer) = file_layer { if let Some(f_layer) = file_layer {
registry.with(f_layer).init(); registry.with(f_layer).init();
} else { } else {