remove comments & rewrite client connection

This commit is contained in:
2026-03-24 12:42:32 +07:00
parent cf9f225969
commit 9177b61a02
13 changed files with 166 additions and 309 deletions
+1 -11
View File
@@ -109,10 +109,8 @@ impl TcpConnection {
while socket.can_recv() { while socket.can_recv() {
let mut full = false; let mut full = false;
// Используем recv_slice для контроля размера чанка
let mut temp = [0u8; TCP_CHUNK_SIZE]; let mut temp = [0u8; TCP_CHUNK_SIZE];
// Peek, чтобы проверить, сможем ли мы отправить данные, прежде чем извлечь их
if let Ok(n) = socket.peek_slice(&mut temp) { if let Ok(n) = socket.peek_slice(&mut temp) {
if n == 0 { if n == 0 {
break; break;
@@ -121,11 +119,10 @@ impl TcpConnection {
let chunk = Bytes::copy_from_slice(&temp[..n]); let chunk = Bytes::copy_from_slice(&temp[..n]);
match self.tx.try_send(chunk) { match self.tx.try_send(chunk) {
Ok(_) => { Ok(_) => {
// Только если успешно отправили в канал, удаляем данные из сокета
socket.recv_slice(&mut temp[..n]).unwrap(); socket.recv_slice(&mut temp[..n]).unwrap();
} }
Err(mpsc::error::TrySendError::Full(_)) => { Err(mpsc::error::TrySendError::Full(_)) => {
full = true; // Канал забит, сработает Backpressure в smoltcp full = true;
} }
Err(_) => { Err(_) => {
self.state = ConnectionState::Closed; self.state = ConnectionState::Closed;
@@ -143,25 +140,21 @@ impl TcpConnection {
let current_pending = self.pending_data.len(); let current_pending = self.pending_data.len();
// Считаем % заполненности для логирования
let fill_ratio = (current_pending as f32 / MAX_PENDING as f32) * 100.0; let fill_ratio = (current_pending as f32 / MAX_PENDING as f32) * 100.0;
if current_pending >= MAX_PENDING { if current_pending >= MAX_PENDING {
// Состояние активного Backpressure
netrunner_logger::warn!( netrunner_logger::warn!(
%self.handle, %self.handle,
"Backpressure ACTIVE: Buffer is FULL ({} bytes). Stalling RX channel.", "Backpressure ACTIVE: Buffer is FULL ({} bytes). Stalling RX channel.",
current_pending current_pending
); );
} else if fill_ratio > 80.0 { } else if fill_ratio > 80.0 {
// Состояние Bufferbloat (буфер почти полон, пакеты задерживаются)
netrunner_logger::info!( netrunner_logger::info!(
%self.handle, %self.handle,
"Bufferbloat Warning: Buffer {:.1}% full ({} bytes). Latency increasing.", "Bufferbloat Warning: Buffer {:.1}% full ({} bytes). Latency increasing.",
fill_ratio, current_pending fill_ratio, current_pending
); );
// Продолжаем читать, пока есть хоть какое-то место
while let Ok(data) = self.rx.try_recv() { while let Ok(data) = self.rx.try_recv() {
self.pending_data.extend_from_slice(&data); self.pending_data.extend_from_slice(&data);
if self.pending_data.len() >= MAX_PENDING { if self.pending_data.len() >= MAX_PENDING {
@@ -169,7 +162,6 @@ impl TcpConnection {
} }
} }
} else { } else {
// Обычный режим
while let Ok(data) = self.rx.try_recv() { while let Ok(data) = self.rx.try_recv() {
self.pending_data.extend_from_slice(&data); self.pending_data.extend_from_slice(&data);
if self.pending_data.len() >= MAX_PENDING { if self.pending_data.len() >= MAX_PENDING {
@@ -178,13 +170,11 @@ impl TcpConnection {
} }
} }
// 3. DOWNLOAD: Отправка накопленного буфера в smoltcp
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) => {
self.pending_data.advance(n); self.pending_data.advance(n);
// Лог освобождения (опционально, чтобы видеть, что пробка рассасывается)
if n > 0 && self.pending_data.len() < (MAX_PENDING / 2) && fill_ratio > 90.0 { if n > 0 && self.pending_data.len() < (MAX_PENDING / 2) && fill_ratio > 90.0 {
netrunner_logger::info!(%self.handle, "Backpressure RELIEVED: Buffer drained to {} bytes", self.pending_data.len()); netrunner_logger::info!(%self.handle, "Backpressure RELIEVED: Buffer drained to {} bytes", self.pending_data.len());
} }
+2 -14
View File
@@ -9,9 +9,9 @@ use bytes::Bytes;
pub struct UdpConnection { pub struct UdpConnection {
pub handle: SocketHandle, pub handle: SocketHandle,
// UPLOAD: Ограниченный канал для передачи данных наружу
tx: mpsc::Sender<Bytes>, tx: mpsc::Sender<Bytes>,
// DOWNLOAD: Канал для приема данных из сети
rx: mpsc::Receiver<Bytes>, rx: mpsc::Receiver<Bytes>,
client_endpoint: Option<IpEndpoint>, client_endpoint: Option<IpEndpoint>,
last_activity: Instant, last_activity: Instant,
@@ -20,10 +20,6 @@ pub struct UdpConnection {
const UDP_TIMEOUT: Duration = Duration::from_secs(60); const UDP_TIMEOUT: Duration = Duration::from_secs(60);
impl UdpConnection { impl UdpConnection {
/// Возвращает:
/// 1. Экземпляр UdpConnection
/// 2. Receiver (для чтения данных, отправляемых ИЗ smoltcp В сеть)
/// 3. Sender (для записи данных ИЗ сети В smoltcp)
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) { pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) {
let (tx_to_net, rx_from_smol) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY); let (tx_to_net, rx_from_smol) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY); let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
@@ -40,31 +36,25 @@ impl UdpConnection {
} }
pub fn tick(&mut self, socket: &mut udp::Socket) -> bool { pub fn tick(&mut self, socket: &mut udp::Socket) -> bool {
// Проверка таймаута бездействия
if self.last_activity.elapsed() > UDP_TIMEOUT { if self.last_activity.elapsed() > UDP_TIMEOUT {
netrunner_logger::debug!(%self.handle, "UDP Session closed due to timeout"); netrunner_logger::debug!(%self.handle, "UDP Session closed due to timeout");
socket.close(); socket.close();
return false; return false;
} }
// 1. UPLOAD: Читаем из smoltcp и отправляем в виртуальный канал
if socket.can_recv() { if socket.can_recv() {
while let Ok((data, metadata)) = socket.recv() { while let Ok((data, metadata)) = socket.recv() {
self.client_endpoint = Some(metadata.endpoint); self.client_endpoint = Some(metadata.endpoint);
// Копируем данные в Bytes и пытаемся протолкнуть.
// Если канал забит (Full), пакет дропается — это штатное поведение UDP.
if self.tx.try_send(Bytes::copy_from_slice(data)).is_ok() { if self.tx.try_send(Bytes::copy_from_slice(data)).is_ok() {
self.last_activity = Instant::now(); self.last_activity = Instant::now();
} }
} }
} }
// 2. DOWNLOAD: Читаем из виртуального канала и пишем в smoltcp
if socket.can_send() { if socket.can_send() {
if let Some(endpoint) = self.client_endpoint { if let Some(endpoint) = self.client_endpoint {
while let Ok(data) = self.rx.try_recv() { while let Ok(data) = self.rx.try_recv() {
// Сигнал закрытия стрима
if data.is_empty() { if data.is_empty() {
socket.close(); socket.close();
return false; return false;
@@ -75,8 +65,6 @@ impl UdpConnection {
self.last_activity = Instant::now(); self.last_activity = Instant::now();
} }
Err(_) => { Err(_) => {
// Если сокет smoltcp переполнен, прерываем цикл.
// Пакет теряется, что опять же является нормой для UDP.
break; break;
} }
} }
+4 -29
View File
@@ -1,35 +1,10 @@
uniffi::setup_scaffolding!(); uniffi::setup_scaffolding!();
pub mod connections; pub mod connections;
pub mod session;
pub mod tun; pub mod tun;
use netrunner_logger::info;
use std::sync::OnceLock; use std::sync::OnceLock;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use tokio_util::sync::CancellationToken;
use crate::tun::routing::reset_platform_routing; pub static RUNTIME: OnceLock<Runtime> = OnceLock::new();
pub mod session;
static RUNTIME: OnceLock<Runtime> = OnceLock::new();
#[derive(uniffi::Object)]
pub struct Session {
pub(crate) cancel_token: CancellationToken,
pub(crate) proxy_ip: String,
}
#[uniffi::export]
impl Session {
pub fn stop(&self) {
info!("Stopping session...");
self.cancel_token.cancel();
let _ = reset_platform_routing(Some(&self.proxy_ip));
}
}
impl Drop for Session {
fn drop(&mut self) {
info!("Session dropped, stopping all tasks...");
self.cancel_token.cancel();
let _ = reset_platform_routing(Some(&self.proxy_ip));
}
}
+6 -17
View File
@@ -6,7 +6,7 @@ use netrunner_client::{
tun::Tun, tun::Tun,
}, },
}; };
use netrunner_core::proxy::{connection::connection::ConnectionRole, network::Network}; use netrunner_core::proxy::connection::connection::ClientHandler;
use netrunner_logger::{error, info}; use netrunner_logger::{error, info};
use smoltcp::{iface::Config, phy::DeviceCapabilities}; use smoltcp::{iface::Config, phy::DeviceCapabilities};
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
@@ -42,26 +42,15 @@ async fn main() -> anyhow::Result<()> {
caps.max_transmission_unit = 1350; caps.max_transmission_unit = 1350;
caps.medium = smoltcp::phy::Medium::Ip; caps.medium = smoltcp::phy::Medium::Ip;
let network = Network::new(
"127.0.0.1".into(),
8080,
ConnectionRole::Client,
Some(remote_address.clone()),
);
let network_token = CancellationToken::new(); let network_token = CancellationToken::new();
let net_token_for_spawn = network_token.clone();
info!("Establishing secure tunnel to proxy server..."); info!("Establishing secure tunnel to proxy server...");
let muxer = match network.initialize_client_tunnel(net_token_for_spawn).await {
let muxer = match ClientHandler::connect(&remote_address).await {
Ok(m) => m, Ok(m) => m,
Err(e) => { Err(e) => {
error!("Failed to establish secure tunnel: {}", e); error!("Failed to establish secure tunnel to server: {}", e);
return Err(anyhow::anyhow!("Failed to establish secure tunnel: {}", e));
let _ = reset_platform_routing(Some(
&remote_address.split(':').next().unwrap().to_string(),
));
return Err(anyhow::anyhow!("Tunnel initialization failed: {}", e));
} }
}; };
info!("Secure tunnel established, Muxer is ready."); info!("Secure tunnel established, Muxer is ready.");
@@ -96,7 +85,7 @@ async fn main() -> anyhow::Result<()> {
error!("Failed to restore routing: {}", e); error!("Failed to restore routing: {}", e);
} else { } else {
info!("System routing restored successfully."); info!("System routing restored successfully.");
} };
Ok(()) Ok(())
} }
+48 -58
View File
@@ -1,22 +1,18 @@
use crate::{
RUNTIME,
tun::{engine::EngineBuilder, routing::reset_platform_routing, tun::Tun},
};
use netrunner_logger::{error, info};
use std::sync::Arc;
use tokio::runtime::Runtime;
use tokio_util::sync::CancellationToken;
#[cfg(any(target_os = "linux", target_os = "windows"))] #[cfg(any(target_os = "linux", target_os = "windows"))]
pub mod desktop; pub mod desktop;
#[cfg(any(target_os = "android", target_os = "ios"))] #[cfg(any(target_os = "android", target_os = "ios"))]
pub mod mobile; pub mod mobile;
use crate::{
RUNTIME, Session,
connections::dns::DnsHandler,
tun::{engine::Engine, routing::setup_platform_routing, tun::Tun},
};
use netrunner_core::proxy::{connection::connection::ConnectionRole, network::Network};
use netrunner_logger::{error, info};
use smoltcp::{iface::Config, phy::DeviceCapabilities};
use std::net::Ipv4Addr;
use std::sync::Arc;
use tokio::runtime::Runtime;
use tokio_util::sync::CancellationToken;
fn get_runtime() -> &'static Runtime { fn get_runtime() -> &'static Runtime {
RUNTIME.get_or_init(|| { RUNTIME.get_or_init(|| {
tokio::runtime::Builder::new_multi_thread() tokio::runtime::Builder::new_multi_thread()
@@ -26,6 +22,29 @@ fn get_runtime() -> &'static Runtime {
}) })
} }
#[derive(uniffi::Object)]
pub struct Session {
pub(crate) cancel_token: CancellationToken,
pub(crate) proxy_ip: String,
}
#[uniffi::export]
impl Session {
pub fn stop(&self) {
info!("Stopping session...");
self.cancel_token.cancel();
let _ = reset_platform_routing(Some(&self.proxy_ip));
}
}
impl Drop for Session {
fn drop(&mut self) {
info!("Session dropped, stopping all tasks...");
self.cancel_token.cancel();
let _ = reset_platform_routing(Some(&self.proxy_ip));
}
}
#[derive(uniffi::Object)] #[derive(uniffi::Object)]
pub struct SessionManager; pub struct SessionManager;
@@ -48,8 +67,7 @@ impl SessionManager {
) -> Arc<Session> { ) -> Arc<Session> {
let runtime = get_runtime(); let runtime = get_runtime();
let cancel_token = CancellationToken::new(); let cancel_token = CancellationToken::new();
let sesison_token = cancel_token.clone(); let session_token = cancel_token.clone();
let net_token = cancel_token.clone();
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 remote_proxy_ip = addr.ip().to_string(); let remote_proxy_ip = addr.ip().to_string();
@@ -68,11 +86,6 @@ impl SessionManager {
} }
}; };
let mut dns_handler = DnsHandler::new(&cache_path);
if let Err(e) = dns_handler.init().await {
error!("Failed to initialize DNS blocklist: {}", e);
}
let tun_device = { let tun_device = {
#[cfg(any(target_os = "android", target_os = "ios"))] #[cfg(any(target_os = "android", target_os = "ios"))]
{ {
@@ -100,55 +113,32 @@ impl SessionManager {
} }
}; };
let _ = setup_platform_routing(&remote_address); let builder_result = EngineBuilder::new(&remote_address)
.with_cache_path(&cache_path)
.with_tun(tun_device)
.build()
.await;
let config = Config::new(smoltcp::wire::HardwareAddress::Ip); match builder_result {
let mut caps = DeviceCapabilities::default(); Ok((mut engine, tun)) => {
caps.max_transmission_unit = 1350;
caps.medium = smoltcp::phy::Medium::Ip;
let network = Network::new(
"0.0.0.0".into(),
8080,
ConnectionRole::Client,
Some(remote_address.clone()),
);
let muxer = match network.initialize_client_tunnel(net_token).await {
Ok(m) => m,
Err(e) => {
error!("Failed to establish secure tunnel to server: {}", e);
return;
}
};
info!("Secure tunnel established, Muxer is ready.");
let mut engine = Engine::new(config, caps, dns_handler, muxer);
engine.set_any_ip(true);
engine.set_transparent_mode();
engine.set_default_gateway(Ipv4Addr::new(10, 0, 0, 2));
engine.activate();
let cancel_token_for_engine = cancel_token.clone();
tokio::spawn(async move {
info!("Engine async task started"); info!("Engine async task started");
tokio::select! { tokio::select! {
res = engine.run(tun) => {
res = engine.run(tun_device) => {
info!("Engine loop finished: {:?}", res); info!("Engine loop finished: {:?}", res);
}, },
_ = cancel_token_for_engine.cancelled() => { _ = cancel_token.cancelled() => {
info!("Engine task shutting down via token"); info!("Engine task shutting down via token");
} }
} }
}); }
Err(e) => {
error!("Failed to build VPN Engine: {}", e);
}
}
}); });
Arc::new(Session { Arc::new(Session {
cancel_token: sesison_token, cancel_token: session_token,
proxy_ip: remote_proxy_ip, proxy_ip: remote_proxy_ip,
}) })
} }
+3 -9
View File
@@ -101,10 +101,7 @@ impl ConnectionManager {
} }
pub fn process_sockets(&mut self, socket_set: &mut SocketSet) { pub fn process_sockets(&mut self, socket_set: &mut SocketSet) {
// Добавим лог в начало цикла, если сессий много, чтобы видеть нагрузку if !self.active_tcp_sessions.is_empty() || !self.active_udp_sessions.is_empty() {}
if !self.active_tcp_sessions.is_empty() || !self.active_udp_sessions.is_empty() {
// trace!("Processing sockets: TCP={}, UDP={}", self.active_tcp_sessions.len(), self.active_udp_sessions.len());
}
for (handle, socket) in socket_set.iter_mut() { for (handle, socket) in socket_set.iter_mut() {
if let Some(tcp) = tcp::Socket::downcast_mut(socket) { if let Some(tcp) = tcp::Socket::downcast_mut(socket) {
@@ -170,7 +167,6 @@ impl ConnectionManager {
return; return;
} }
// Ждем подтверждения от прокси (handshake)
match tokio::time::timeout(Duration::from_secs(10), v_rx.recv()).await { match tokio::time::timeout(Duration::from_secs(10), v_rx.recv()).await {
Ok(Some(data)) => { Ok(Some(data)) => {
if data.len() >= 2 && data[1] == 0x00 { if data.len() >= 2 && data[1] == 0x00 {
@@ -281,11 +277,9 @@ impl ConnectionManager {
netrunner_logger::info!(%handle, target = %target, "New UDP proxied session established"); netrunner_logger::info!(%handle, target = %target, "New UDP proxied session established");
// 1. Создаем UDP соединение и получаем каналы
let (conn, mut rx_from_smol, tx_to_smol) = UdpConnection::new(handle); let (conn, mut rx_from_smol, tx_to_smol) = UdpConnection::new(handle);
self.active_udp_sessions.insert(handle, conn); self.active_udp_sessions.insert(handle, conn);
// 2. Фоновая задача для Muxer'а
let muxer = self.muxer.clone(); let muxer = self.muxer.clone();
let stream_id = muxer.next_id(); let stream_id = muxer.next_id();
let connect_payload = target.to_string(); let connect_payload = target.to_string();
@@ -441,8 +435,8 @@ impl ConnectionManager {
fn create_dynamic_udp_socket<'a>(port: u16) -> udp::Socket<'a> { fn create_dynamic_udp_socket<'a>(port: u16) -> udp::Socket<'a> {
let (buf_size, packet_count) = match port { let (buf_size, packet_count) = match port {
443 => (512 * 1024, 390), // Большой буфер для QUIC (YouTube) 443 => (512 * 1024, 390),
53 => (64 * 1024, 32), // DNS 53 => (64 * 1024, 32),
_ => (128 * 1024, 100), _ => (128 * 1024, 100),
}; };
+4 -14
View File
@@ -6,11 +6,11 @@ use std::{
Arc, LazyLock, Mutex, Arc, LazyLock, Mutex,
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
}, },
time::Instant as StdInstant, // Добавлено для расчёта скорости time::Instant as StdInstant,
}; };
use bytes::BytesMut; use bytes::BytesMut;
use netrunner_logger::info; // Предполагается, что у тебя есть этот логгер use netrunner_logger::info;
use smoltcp::{ use smoltcp::{
phy::{self, Device, DeviceCapabilities}, phy::{self, Device, DeviceCapabilities},
time::Instant, time::Instant,
@@ -20,7 +20,6 @@ use tokio::sync::mpsc;
const TOKEN_BUFFER_LIST_MAX_SIZE: usize = 64; const TOKEN_BUFFER_LIST_MAX_SIZE: usize = 64;
static TOKEN_BUFFER_LIST: LazyLock<Mutex<Vec<BytesMut>>> = LazyLock::new(|| Mutex::new(Vec::new())); static TOKEN_BUFFER_LIST: LazyLock<Mutex<Vec<BytesMut>>> = LazyLock::new(|| Mutex::new(Vec::new()));
// Структура, которую мы будем возвращать пользователю
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct TrafficStats { pub struct TrafficStats {
pub rx_bytes: u64, pub rx_bytes: u64,
@@ -79,20 +78,17 @@ pub struct VirtTunDevice {
tx_queue: mpsc::UnboundedSender<TokenBuffer>, tx_queue: mpsc::UnboundedSender<TokenBuffer>,
rx_avail: Arc<AtomicBool>, rx_avail: Arc<AtomicBool>,
// === Статистика трафика ===
rx_bytes: u64, rx_bytes: u64,
tx_bytes: u64, tx_bytes: u64,
rx_packets: u64, rx_packets: u64,
tx_packets: u64, tx_packets: u64,
// Внутренние переменные для расчёта скорости
last_speed_calc: StdInstant, last_speed_calc: StdInstant,
last_rx_bytes: u64, last_rx_bytes: u64,
last_tx_bytes: u64, last_tx_bytes: u64,
cached_rx_speed: f64, cached_rx_speed: f64,
cached_tx_speed: f64, cached_tx_speed: f64,
// Для периодического логирования
last_log_time: StdInstant, last_log_time: StdInstant,
} }
@@ -147,7 +143,6 @@ impl VirtTunDevice {
let rx_diff = self.rx_bytes.saturating_sub(self.last_rx_bytes); let rx_diff = self.rx_bytes.saturating_sub(self.last_rx_bytes);
let tx_diff = self.tx_bytes.saturating_sub(self.last_tx_bytes); let tx_diff = self.tx_bytes.saturating_sub(self.last_tx_bytes);
// Переводим в МегаБайты в секунду (MB/s)
self.cached_rx_speed = (rx_diff as f64 / 1_048_576.0) / elapsed_speed; self.cached_rx_speed = (rx_diff as f64 / 1_048_576.0) / elapsed_speed;
self.cached_tx_speed = (tx_diff as f64 / 1_048_576.0) / elapsed_speed; self.cached_tx_speed = (tx_diff as f64 / 1_048_576.0) / elapsed_speed;
@@ -166,12 +161,11 @@ impl VirtTunDevice {
} }
} }
/// Внутренний метод проверки таймера для вывода логов
fn check_and_log_stats(&mut self) { fn check_and_log_stats(&mut self) {
let now = StdInstant::now(); let now = StdInstant::now();
// Логируем каждые 5 секунд
if now.duration_since(self.last_log_time).as_secs() >= 5 { if now.duration_since(self.last_log_time).as_secs() >= 5 {
let stats = self.get_stats(); // Заодно обновляем скорости let stats = self.get_stats();
info!( info!(
"TunDevice Traffic: RX: {:.2} MB ({} pkts) | TX: {:.2} MB ({} pkts) | Speed: ↓{:.2} MB/s, ↑{:.2} MB/s", "TunDevice Traffic: RX: {:.2} MB ({} pkts) | TX: {:.2} MB ({} pkts) | Speed: ↓{:.2} MB/s, ↑{:.2} MB/s",
@@ -193,12 +187,9 @@ impl Device for VirtTunDevice {
type TxToken<'a> = VirtTxToken<'a>; type TxToken<'a> = VirtTxToken<'a>;
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
// Проверяем, не пора ли записать стату в лог. receive() вызывается очень часто,
// поэтому это отличное место для heartbeat таймера.
self.check_and_log_stats(); self.check_and_log_stats();
if let Ok(buffer) = self.rx_queue.try_recv() { if let Ok(buffer) = self.rx_queue.try_recv() {
// Учет входящего трафика (DOWNLOAD)
self.rx_bytes += buffer.len() as u64; self.rx_bytes += buffer.len() as u64;
self.rx_packets += 1; self.rx_packets += 1;
@@ -250,7 +241,6 @@ impl phy::TxToken for VirtTxToken<'_> {
let result = f(&mut buffer); let result = f(&mut buffer);
// Учет исходящего трафика (UPLOAD)
self.0.tx_bytes += len as u64; self.0.tx_bytes += len as u64;
self.0.tx_packets += 1; self.0.tx_packets += 1;
+66 -1
View File
@@ -1,3 +1,4 @@
use netrunner_core::proxy::connection::connection::ClientHandler;
use netrunner_core::proxy::connection::muxer::Muxer; use netrunner_core::proxy::connection::muxer::Muxer;
use smoltcp::iface::PollResult; use smoltcp::iface::PollResult;
use smoltcp::time::Instant; use smoltcp::time::Instant;
@@ -6,6 +7,7 @@ use smoltcp::{
iface::{Config, Interface, SocketSet}, iface::{Config, Interface, SocketSet},
phy::DeviceCapabilities, phy::DeviceCapabilities,
}; };
use std::net::Ipv4Addr;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::{ use std::{
sync::{Arc, LazyLock, atomic::AtomicBool}, sync::{Arc, LazyLock, atomic::AtomicBool},
@@ -16,11 +18,12 @@ use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use tokio::time::{Duration, sleep}; use tokio::time::{Duration, sleep};
use tun::{DeviceReader, DeviceWriter}; use tun::{DeviceReader, DeviceWriter};
use netrunner_logger::{debug, info, warn}; use netrunner_logger::{debug, error, info, warn};
use crate::connections::dns::DnsHandler; use crate::connections::dns::DnsHandler;
use crate::tun::connection_manager::ConnectionManager; use crate::tun::connection_manager::ConnectionManager;
use crate::tun::device::{TokenBuffer, VirtTunDevice}; use crate::tun::device::{TokenBuffer, VirtTunDevice};
use crate::tun::routing::setup_platform_routing;
use crate::tun::tun::Tun; use crate::tun::tun::Tun;
pub static START_TIME: LazyLock<StdInstant> = LazyLock::new(StdInstant::now); pub static START_TIME: LazyLock<StdInstant> = LazyLock::new(StdInstant::now);
@@ -212,3 +215,65 @@ impl Engine {
self.manager.start_listening(&mut self.socket_set); self.manager.start_listening(&mut self.socket_set);
} }
} }
pub struct EngineBuilder {
remote_address: String,
cache_path: String,
tun_device: Option<Tun>,
}
impl EngineBuilder {
pub fn new(remote_address: impl Into<String>) -> Self {
Self {
remote_address: remote_address.into(),
cache_path: ".".to_string(),
tun_device: None,
}
}
pub fn with_cache_path(mut self, path: impl Into<String>) -> Self {
self.cache_path = path.into();
self
}
pub fn with_tun(mut self, tun: Tun) -> Self {
self.tun_device = Some(tun);
self
}
pub async fn build(self) -> Result<(Engine, Tun), String> {
let tun = self.tun_device.ok_or("TUN device is required")?;
info!("Initializing Engine components...");
let mut dns_handler = DnsHandler::new(&self.cache_path);
if let Err(e) = dns_handler.init().await {
error!("Failed to initialize DNS blocklist: {}", e);
}
if let Err(e) = setup_platform_routing(&self.remote_address) {
return Err(format!("Routing setup failed: {}", e));
}
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1350;
caps.medium = smoltcp::phy::Medium::Ip;
info!("Establishing secure tunnel to proxy server...");
let muxer = ClientHandler::connect(&self.remote_address)
.await
.map_err(|e| format!("Failed to establish secure tunnel: {}", e))?;
info!("Secure tunnel established, Muxer is ready.");
let mut engine = Engine::new(config, caps, dns_handler, muxer);
engine.set_any_ip(true);
engine.set_transparent_mode();
engine.set_default_gateway(Ipv4Addr::new(10, 0, 0, 2));
engine.activate();
info!("Stack IP initialized: 10.0.0.2");
Ok((engine, tun))
}
}
-4
View File
@@ -59,7 +59,6 @@ pub async fn run_proxy_bridge<R, W>(
} }
} }
// Отправляем Close фрейм перед выходом
let _ = muxer let _ = muxer
.send_to_netwrok(MuxMessage { .send_to_netwrok(MuxMessage {
stream_id, stream_id,
@@ -68,10 +67,8 @@ pub async fn run_proxy_bridge<R, W>(
}) })
.await; .await;
// Небольшая пауза, чтобы Close фрейм успел уйти в сеть
tokio::time::sleep(std::time::Duration::from_millis(500)).await; tokio::time::sleep(std::time::Duration::from_millis(500)).await;
// ИЗМЕНЕНИЕ: remove_stream теперь синхронный
muxer.remove_stream(stream_id); muxer.remove_stream(stream_id);
} }
@@ -131,6 +128,5 @@ pub async fn run_udp_bridge(
tokio::time::sleep(std::time::Duration::from_millis(500)).await; tokio::time::sleep(std::time::Duration::from_millis(500)).await;
// ИЗМЕНЕНИЕ: remove_stream теперь синхронный
muxer.remove_stream(stream_id); muxer.remove_stream(stream_id);
} }
+10 -25
View File
@@ -9,15 +9,12 @@ use crate::{
parser::parser::Parser, parser::parser::Parser,
}, },
proxy::connection::{ proxy::connection::{
bridge::run_proxy_bridge, bridge::run_proxy_bridge, engine::TunnelEngine, handler::StreamHandler, muxer::Muxer,
engine::TunnelEngine,
handler::StreamHandler,
muxer::{MuxMessage, Muxer},
MESSAGE_CHANNEL_SIZE, TCP_BUF_SIZE, MESSAGE_CHANNEL_SIZE, TCP_BUF_SIZE,
}, },
tlseng::profile::BrowserProfile, tlseng::profile::BrowserProfile,
}; };
use bytes::{Bytes, BytesMut}; use bytes::BytesMut;
use netrunner_logger::{info, warn}; use netrunner_logger::{info, warn};
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
@@ -101,10 +98,7 @@ pub struct ClientHandler {
} }
impl ClientHandler { impl ClientHandler {
pub async fn connect( pub async fn connect(remote_proxy_addr: &str) -> Result<Muxer, String> {
remote_proxy_addr: &str,
token: CancellationToken,
) -> Result<Muxer, String> {
let stream = TcpStream::connect(remote_proxy_addr) let stream = TcpStream::connect(remote_proxy_addr)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
@@ -138,9 +132,8 @@ impl ClientHandler {
} }
} }
// --- ИЗМЕНЕНИЕ ДЛЯ НОВОГО MUXER --- let (control_tx, control_rx) = mpsc::channel(MESSAGE_CHANNEL_SIZE * 4);
let (control_tx, control_rx) = mpsc::channel(MESSAGE_CHANNEL_SIZE); let (data_tx, data_rx) = mpsc::channel(MESSAGE_CHANNEL_SIZE * 4);
let (data_tx, data_rx) = mpsc::channel(MESSAGE_CHANNEL_SIZE);
let muxer = Muxer::new(control_tx, data_tx, true); let muxer = Muxer::new(control_tx, data_tx, true);
@@ -152,10 +145,9 @@ impl ClientHandler {
outbound: conn.outbound, outbound: conn.outbound,
codec: conn.codec, codec: conn.codec,
read_buf: conn.read_buf, read_buf: conn.read_buf,
control_rx, // Передаем оба ресивера в Engine control_rx,
data_rx, // Передаем оба ресивера в Engine data_rx,
handler, handler,
token: token.clone(),
}; };
tokio::spawn(async move { engine.run().await }); tokio::spawn(async move { engine.run().await });
@@ -210,7 +202,6 @@ impl TunnelHandler for ClientHandler {
let (v_tx, mut v_rx) = mpsc::channel::<bytes::Bytes>(TCP_BUF_SIZE); let (v_tx, mut v_rx) = mpsc::channel::<bytes::Bytes>(TCP_BUF_SIZE);
self.muxer.register_stream(stream_id, v_tx); self.muxer.register_stream(stream_id, v_tx);
// Используем send_control для отправки FrameType::Connect
self.muxer self.muxer
.send_control( .send_control(
stream_id, stream_id,
@@ -269,7 +260,6 @@ impl ServerHandler {
let target_host = "ubuntu.com:443"; let target_host = "ubuntu.com:443";
info!(target = %target_host, "Stealth fallback: bridging to Target"); info!(target = %target_host, "Stealth fallback: bridging to Target");
// 1. Пытаемся подключиться с коротким таймаутом
let target_stream = tokio::time::timeout( let target_stream = tokio::time::timeout(
std::time::Duration::from_secs(3), std::time::Duration::from_secs(3),
TcpStream::connect(target_host), TcpStream::connect(target_host),
@@ -277,10 +267,9 @@ impl ServerHandler {
.await; .await;
match target_stream { match target_stream {
Ok(Ok(mut target_server)) => { Ok(Ok(target_server)) => {
let (mut server_read, mut server_write) = target_server.into_split(); let (mut server_read, mut server_write) = target_server.into_split();
// 2. Скармливаем те байты, которые уже вычитали (Client Hello)
if !initial_data.is_empty() { if !initial_data.is_empty() {
if let Err(e) = server_write.write_all(&initial_data).await { if let Err(e) = server_write.write_all(&initial_data).await {
warn!("Failed to push initial data to fallback: {}", e); warn!("Failed to push initial data to fallback: {}", e);
@@ -288,8 +277,6 @@ impl ServerHandler {
} }
} }
// 3. Запускаем bidirectional copy
// Это создаст две задачи, которые будут перекачивать байты, пока одна сторона не закроется
let res = tokio::io::copy_bidirectional( let res = tokio::io::copy_bidirectional(
&mut tokio::io::join(&mut client_inbound, &mut client_outbound), &mut tokio::io::join(&mut client_inbound, &mut client_outbound),
&mut tokio::io::join(&mut server_read, &mut server_write), &mut tokio::io::join(&mut server_read, &mut server_write),
@@ -345,7 +332,7 @@ impl TunnelHandler for ServerHandler {
Ok(Err(e)) => return Err(e.to_string()), Ok(Err(e)) => return Err(e.to_string()),
Err(_) => { Err(_) => {
warn!("Handshake timeout. Going stealth."); warn!("Handshake timeout. Going stealth.");
// Используем наш снимок, так как основной буфер мог быть частично съеден
ServerHandler::handle_stealth_fallback( ServerHandler::handle_stealth_fallback(
self.conn.inbound, self.conn.inbound,
self.conn.outbound, self.conn.outbound,
@@ -358,7 +345,7 @@ impl TunnelHandler for ServerHandler {
} }
Err(e) => { Err(e) => {
warn!("Auth/Format failed: {:?}. Going stealth.", e); warn!("Auth/Format failed: {:?}. Going stealth.", e);
// ВАЖНО: используем сохраненный buf_snapshot
info!( info!(
"DEBUG: Restoring {} bytes from snapshot for fallback", "DEBUG: Restoring {} bytes from snapshot for fallback",
buf_snapshot.len() buf_snapshot.len()
@@ -375,7 +362,6 @@ impl TunnelHandler for ServerHandler {
} }
}; };
// Если дошли сюда — значит это наш клиент, шлем Server Hello
self.conn self.conn
.outbound .outbound
.write_all(&hello) .write_all(&hello)
@@ -392,7 +378,6 @@ impl TunnelHandler for ServerHandler {
control_rx, control_rx,
data_rx, data_rx,
handler, handler,
token: self.token,
} }
.run() .run()
.await .await
+13 -30
View File
@@ -25,7 +25,6 @@ pub struct TunnelEngine {
pub control_rx: Receiver<MuxMessage>, pub control_rx: Receiver<MuxMessage>,
pub data_rx: Receiver<MuxMessage>, pub data_rx: Receiver<MuxMessage>,
pub handler: Arc<StreamHandler>, pub handler: Arc<StreamHandler>,
pub token: CancellationToken,
} }
impl TunnelEngine { impl TunnelEngine {
@@ -33,25 +32,20 @@ impl TunnelEngine {
let inbound = self.inbound; let inbound = self.inbound;
let outbound = self.outbound; let outbound = self.outbound;
// Оборачиваем кодек в потокобезопасный мьютекс
let codec = Arc::new(Mutex::new(self.codec)); let codec = Arc::new(Mutex::new(self.codec));
let read_buf = self.read_buf; let read_buf = self.read_buf;
let control_rx = self.control_rx; let control_rx = self.control_rx;
let data_rx = self.data_rx; let data_rx = self.data_rx;
let handler = self.handler; let handler = self.handler;
let token = self.token; let token = CancellationToken::new();
// Клонируем Arc для независимых тасок
let codec_reader = codec.clone(); let codec_reader = codec.clone();
let codec_writer = codec.clone(); let codec_writer = codec.clone();
let token_reader = token.clone(); let token_reader = token.clone();
let token_writer = token.clone(); let token_writer = token.clone();
// ==========================================
// 1. READER TASK (Только чтение и расшифровка)
// ==========================================
let reader_handle = tokio::spawn(async move { let reader_handle = tokio::spawn(async move {
let mut read_buf = read_buf; let mut read_buf = read_buf;
let mut inbound = inbound; let mut inbound = inbound;
@@ -65,23 +59,23 @@ impl TunnelEngine {
res = inbound.read_buf(&mut read_buf) => { res = inbound.read_buf(&mut read_buf) => {
let n = res.map_err(|e| e.to_string())?; let n = res.map_err(|e| e.to_string())?;
// --- ИСПРАВЛЕННАЯ ЛОГИКА EOF ---
if n == 0 { if n == 0 {
if read_buf.is_empty() { if read_buf.is_empty() {
info!("Connection closed by peer (Clean EOF)"); info!("Connection closed by peer (Clean EOF)");
} else { } else {
error!("Connection abruptly closed by peer (Incomplete frame: {} bytes left)", read_buf.len()); error!("Connection abruptly closed by peer (Incomplete frame: {} bytes left)", read_buf.len());
} }
// ВЫХОДИМ В ЛЮБОМ СЛУЧАЕ, СОКЕТ МЕРТВ!
return Err::<(), String>("EOF".into()); return Err::<(), String>("EOF".into());
} }
// Вектор для фреймов. Мы расшифруем всё быстро,
// сложим сюда и отпустим лок Кодека.
let mut frames = Vec::new(); let mut frames = Vec::new();
{ {
// Блокируем кодек только на время математики (расшифровки)
let mut c = codec_reader.lock().await; let mut c = codec_reader.lock().await;
loop { loop {
match c.inbound(&mut read_buf) { match c.inbound(&mut read_buf) {
@@ -100,9 +94,9 @@ impl TunnelEngine {
} }
} }
} }
} // Лок кодека отпущен! }
// Обрабатываем фреймы (I/O) без лока, не мешая Writer Task
for frame in frames { for frame in frames {
handler.handle(frame).await; handler.handle(frame).await;
} }
@@ -112,9 +106,6 @@ impl TunnelEngine {
Ok::<(), String>(()) Ok::<(), String>(())
}); });
// ==========================================
// 2. WRITER TASK (Только шифрование и отправка)
// ==========================================
let writer_handle = tokio::spawn(async move { let writer_handle = tokio::spawn(async move {
let mut outbound = outbound; let mut outbound = outbound;
let mut control_rx = control_rx; let mut control_rx = control_rx;
@@ -123,14 +114,14 @@ impl TunnelEngine {
loop { loop {
tokio::select! { tokio::select! {
biased; // Приоритет сверху вниз biased;
_ = token_writer.cancelled() => { _ = token_writer.cancelled() => {
info!("Writer Task: Shutdown signal received."); info!("Writer Task: Shutdown signal received.");
break; break;
} }
// FAST TRACK: Управляющие команды
msg_opt = control_rx.recv() => { msg_opt = control_rx.recv() => {
if let Some(msg) = msg_opt { if let Some(msg) = msg_opt {
Self::handle_outbound(&mut outbound, &codec_writer, msg).await?; Self::handle_outbound(&mut outbound, &codec_writer, msg).await?;
@@ -139,13 +130,13 @@ impl TunnelEngine {
} }
} }
// Пинг
_ = heartbeat.tick() => { _ = heartbeat.tick() => {
let msg = MuxMessage { stream_id: 0, frame_type: FrameType::Heartbeat, data: Bytes::new() }; let msg = MuxMessage { stream_id: 0, frame_type: FrameType::Heartbeat, data: Bytes::new() };
Self::handle_outbound(&mut outbound, &codec_writer, msg).await?; Self::handle_outbound(&mut outbound, &codec_writer, msg).await?;
} }
// SLOW TRACK: Данные
msg_opt = data_rx.recv() => { msg_opt = data_rx.recv() => {
if let Some(msg) = msg_opt { if let Some(msg) = msg_opt {
Self::handle_outbound(&mut outbound, &codec_writer, msg).await?; Self::handle_outbound(&mut outbound, &codec_writer, msg).await?;
@@ -158,10 +149,6 @@ impl TunnelEngine {
Ok::<(), String>(()) Ok::<(), String>(())
}); });
// ==========================================
// Ожидаем завершения обеих тасок.
// Если одна падает с ошибкой, убиваем туннель.
// ==========================================
let res = tokio::select! { let res = tokio::select! {
res = reader_handle => res.unwrap_or_else(|e| Err(format!("Reader panic: {}", e))), res = reader_handle => res.unwrap_or_else(|e| Err(format!("Reader panic: {}", e))),
res = writer_handle => res.unwrap_or_else(|e| Err(format!("Writer panic: {}", e))), res = writer_handle => res.unwrap_or_else(|e| Err(format!("Writer panic: {}", e))),
@@ -185,11 +172,9 @@ impl TunnelEngine {
let stream_id = msg.stream_id; let stream_id = msg.stream_id;
let frame_type = msg.frame_type; let frame_type = msg.frame_type;
// Вектор зашифрованных пакетов. Собираем их быстро под локом.
let mut packets = Vec::new(); let mut packets = Vec::new();
{ {
// Берем лок кодека только для шифрования
let mut c = codec.lock().await; let mut c = codec.lock().await;
if data.is_empty() { if data.is_empty() {
@@ -214,10 +199,8 @@ impl TunnelEngine {
} }
} }
} }
} // Лок кодека отпущен! }
// Выполняем I/O операцию (которая может зависнуть при плохой сети) БЕЗ лока кодека.
// Это позволяет Reader Task продолжать читать сеть!
for pkt in packets { for pkt in packets {
outbound.write_all(&pkt).await.map_err(|e| { outbound.write_all(&pkt).await.map_err(|e| {
error!(stream_id, error = %e, "Failed to write encrypted data to network"); error!(stream_id, error = %e, "Failed to write encrypted data to network");
-3
View File
@@ -54,12 +54,10 @@ impl Muxer {
self.id_gen.next() self.id_gen.next()
} }
// Обычные данные летят в канал с низким приоритетом
pub async fn send_to_netwrok(&self, message: MuxMessage) -> Result<(), SendError<MuxMessage>> { pub async fn send_to_netwrok(&self, message: MuxMessage) -> Result<(), SendError<MuxMessage>> {
self.data_tx.send(message).await self.data_tx.send(message).await
} }
// Управляющие фреймы летят в VIP-канал
pub async fn send_control( pub async fn send_control(
&self, &self,
stream_id: u32, stream_id: u32,
@@ -96,7 +94,6 @@ impl Muxer {
self.remove_stream(stream_id); self.remove_stream(stream_id);
} }
} else { } else {
// Уменьшил уровень лога до TRACE, потому что это штатная ситуация при больших буферах!
netrunner_logger::trace!( netrunner_logger::trace!(
stream_id, stream_id,
len = data.len(), len = data.len(),
+4 -89
View File
@@ -1,19 +1,8 @@
use crate::{ use crate::proxy::connection::connection::{
protocol::errors::ErrorAction, ClientHandler, Connection, ConnectionRole, ServerHandler, TunnelHandler,
proxy::connection::{
connection::{ClientHandler, Connection, ConnectionRole, ServerHandler, TunnelHandler},
engine::TunnelEngine,
muxer::Muxer,
MESSAGE_CHANNEL_SIZE,
},
tlseng::profile::BrowserProfile,
}; };
use bytes::BytesMut;
use netrunner_logger::{error, info}; use netrunner_logger::{error, info};
use tokio::{ use tokio::net::TcpListener;
io::{AsyncReadExt, AsyncWriteExt},
net::{TcpListener, TcpStream},
};
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
pub struct Network { pub struct Network {
@@ -49,7 +38,7 @@ impl Network {
.as_ref() .as_ref()
.ok_or("No proxy addr") .ok_or("No proxy addr")
.unwrap(); .unwrap();
let muxer = match ClientHandler::connect(server_addr, token.clone()).await { let muxer = match ClientHandler::connect(server_addr).await {
Ok(m) => m, Ok(m) => m,
Err(e) => { Err(e) => {
error!(error = %e, "Global tunnel failed."); error!(error = %e, "Global tunnel failed.");
@@ -96,78 +85,4 @@ impl Network {
} }
} }
} }
pub async fn initialize_client_tunnel(
&self,
token: CancellationToken,
) -> Result<Muxer, String> {
let server_addr = self.remote_proxy_addr.as_ref().ok_or("No proxy addr")?;
let stream = TcpStream::connect(server_addr)
.await
.map_err(|e| e.to_string())?;
let (mut inbound, mut outbound) = stream.into_split();
let mut codec = crate::protocol::codec::codec::Codec::new(false);
let ch = codec
.make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
.map_err(|e| format!("{:?}", e))?;
outbound.write_all(&ch).await.map_err(|e| e.to_string())?;
let mut sh_buf = BytesMut::with_capacity(2048);
loop {
match codec.process_handshake(&mut sh_buf) {
Ok(_) => break,
Err(e) if e.action == ErrorAction::Wait => {
let n = inbound
.read_buf(&mut sh_buf)
.await
.map_err(|e| e.to_string())?;
if n == 0 {
return Err("EOF during handshake".into());
}
}
Err(e) => return Err(format!("TLS error: {:?}", e)),
}
}
// --- ИЗМЕНЕНИЯ ЗДЕСЬ: Создаем два канала ---
let (control_tx, control_rx) = tokio::sync::mpsc::channel(MESSAGE_CHANNEL_SIZE * 4);
let (data_tx, data_rx) = tokio::sync::mpsc::channel(MESSAGE_CHANNEL_SIZE * 4);
// Передаем оба трансмиттера в Muxer
let muxer = Muxer::new(control_tx, data_tx, true);
let handler = std::sync::Arc::new(crate::proxy::connection::handler::StreamHandler::new(
muxer.clone(),
ConnectionRole::Client,
));
let engine = TunnelEngine {
inbound,
outbound,
codec,
read_buf: sh_buf,
control_rx,
data_rx,
handler,
token,
};
tokio::spawn(async move {
if let Err(e) = engine.run().await {
netrunner_logger::error!(
"CRITICAL: Tunnel Engine died: {}. Restarting process...",
e
);
std::process::exit(1);
}
});
Ok(muxer)
}
pub fn get_self_local_address(&self) -> String {
format!("127.0.0.1:{}", self.port)
}
} }