clean comments
This commit is contained in:
@@ -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.");
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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,4 +1,4 @@
|
|||||||
mod bridge; // Все модули делаем приватными
|
mod bridge;
|
||||||
mod connection;
|
mod connection;
|
||||||
mod engine;
|
mod engine;
|
||||||
mod handler;
|
mod handler;
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,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};
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user