buf sizes changes and socks5 remove to rawcast
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
things.txt
|
things.txt
|
||||||
host_cache.txt
|
hosts_cache.txt
|
||||||
/gen
|
/gen
|
||||||
+9
-38
@@ -1,3 +1,4 @@
|
|||||||
|
use netrunner_core::net::network::NetworkConfig;
|
||||||
use uniffi;
|
use uniffi;
|
||||||
uniffi::setup_scaffolding!();
|
uniffi::setup_scaffolding!();
|
||||||
|
|
||||||
@@ -9,8 +10,6 @@ use crate::{
|
|||||||
net::engine::{EngineBuilder, EngineConfig},
|
net::engine::{EngineBuilder, EngineConfig},
|
||||||
tun::routing::reset_platform_routing,
|
tun::routing::reset_platform_routing,
|
||||||
};
|
};
|
||||||
use netrunner_core::net::ConnectionRole;
|
|
||||||
use netrunner_core::net::network::Network;
|
|
||||||
use netrunner_logger::{error, info};
|
use netrunner_logger::{error, info};
|
||||||
use std::sync::{Arc, OnceLock};
|
use std::sync::{Arc, OnceLock};
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
@@ -88,40 +87,9 @@ impl SessionManager {
|
|||||||
config = config.with_mtu(1350);
|
config = config.with_mtu(1350);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 1. ЗАПУСК ЛОКАЛЬНОГО ПРОКСИ (NETWORK) ---
|
NetworkConfig::init_global(config.mtu);
|
||||||
let local_proxy_port = 8080;
|
|
||||||
let local_proxy_host = "127.0.0.1".to_string();
|
|
||||||
let proxy_token = cancel_token.clone();
|
|
||||||
let remote_addr_clone = remote_address.clone();
|
|
||||||
|
|
||||||
runtime.spawn(async move {
|
// --- ЗАПУСК ENGINE И TUN ---
|
||||||
info!(
|
|
||||||
"Starting Local Proxy (Network) on {}:{}",
|
|
||||||
local_proxy_host, local_proxy_port
|
|
||||||
);
|
|
||||||
|
|
||||||
let network = Network::new(
|
|
||||||
local_proxy_host,
|
|
||||||
local_proxy_port,
|
|
||||||
ConnectionRole::Client,
|
|
||||||
Some(remote_addr_clone),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Оборачиваем в tokio::select! для жесткой отмены
|
|
||||||
tokio::select! {
|
|
||||||
_ = network.run(proxy_token.clone()) => {
|
|
||||||
info!("Local Proxy (Network) task finished normally.");
|
|
||||||
}
|
|
||||||
_ = proxy_token.cancelled() => {
|
|
||||||
info!("Local Proxy (Network) task forcefully stopped via CancellationToken.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Даем прокси немного времени на бинд порта
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
||||||
|
|
||||||
// --- 2. ЗАПУСК 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...");
|
||||||
@@ -160,10 +128,13 @@ impl SessionManager {
|
|||||||
|
|
||||||
match builder_result {
|
match builder_result {
|
||||||
Ok((mut engine, tun)) => {
|
Ok((mut engine, tun)) => {
|
||||||
info!("Engine async task started");
|
info!("Engine built successfully, starting loop...");
|
||||||
|
|
||||||
|
// tokio::select! позволяет моментально прервать бесконечный цикл
|
||||||
|
// engine.run() при вызове session.stop()
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
res = engine.run(tun) => {
|
_ = engine.run(tun) => {
|
||||||
info!("Engine loop finished: {:?}", res);
|
info!("Engine loop finished normally.");
|
||||||
},
|
},
|
||||||
_ = engine_token.cancelled() => {
|
_ = engine_token.cancelled() => {
|
||||||
info!("Engine task shutting down via token");
|
info!("Engine task shutting down via token");
|
||||||
|
|||||||
+8
-40
@@ -6,9 +6,8 @@ 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::ConnectionRole;
|
use netrunner_core::net::network::NetworkConfig;
|
||||||
use netrunner_core::net::network::Network;
|
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -17,45 +16,18 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
info!("Initializing NetRunner Stack...");
|
info!("Initializing NetRunner Stack...");
|
||||||
|
|
||||||
let remote_address = "147.45.43.70:443".to_string();
|
let remote_address = "147.45.43.70:443".to_string();
|
||||||
let local_proxy_host = "127.0.0.1".to_string();
|
|
||||||
let local_proxy_port = 8080; // Локальный порт прокси
|
|
||||||
|
|
||||||
// Токен для управления жизненным циклом фоновых задач
|
|
||||||
let cancel_token = CancellationToken::new();
|
let cancel_token = CancellationToken::new();
|
||||||
|
|
||||||
// ==================================================
|
// ==================================================
|
||||||
// 1. ЗАПУСК ЛОКАЛЬНОГО ПРОКСИ (NETWORK)
|
// 1. ИНИЦИАЛИЗАЦИЯ ДВИЖКА И TUN
|
||||||
// ==================================================
|
|
||||||
let proxy_token = cancel_token.clone();
|
|
||||||
let remote_addr_clone = remote_address.clone();
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
|
||||||
info!(
|
|
||||||
"Starting Local Proxy (Network) on {}:{}",
|
|
||||||
local_proxy_host, local_proxy_port
|
|
||||||
);
|
|
||||||
let network = Network::new(
|
|
||||||
local_proxy_host,
|
|
||||||
local_proxy_port,
|
|
||||||
ConnectionRole::Client,
|
|
||||||
Some(remote_addr_clone),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Эта функция заблокирует поток, пока не сработает proxy_token
|
|
||||||
network.run(proxy_token).await;
|
|
||||||
info!("Local Proxy (Network) task stopped.");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Даем локальному прокси немного времени на бинд порта и установку соединения
|
|
||||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
|
||||||
|
|
||||||
// ==================================================
|
|
||||||
// 2. ИНИЦИАЛИЗАЦИЯ ДВИЖКА И TUN
|
|
||||||
// ==================================================
|
// ==================================================
|
||||||
let config = EngineConfig::new(&remote_address)
|
let config = EngineConfig::new(&remote_address)
|
||||||
.with_cache_path(".")
|
.with_cache_path(".")
|
||||||
.with_mtu(1350);
|
.with_mtu(1350);
|
||||||
|
|
||||||
|
// ВАЖНО: Инициализируем глобальные настройки сети (MTU, размеры буферов Muxer'а)
|
||||||
|
NetworkConfig::init_global(config.mtu);
|
||||||
|
|
||||||
let tun_device = Tun::create(|tun_cfg| {
|
let tun_device = Tun::create(|tun_cfg| {
|
||||||
tun_cfg
|
tun_cfg
|
||||||
.tun_name("netr0")
|
.tun_name("netr0")
|
||||||
@@ -75,7 +47,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// ==================================================
|
// ==================================================
|
||||||
// 3. ГЛАВНЫЙ ЦИКЛ ENGINE
|
// 2. ГЛАВНЫЙ ЦИКЛ ENGINE
|
||||||
// ==================================================
|
// ==================================================
|
||||||
match builder_result {
|
match builder_result {
|
||||||
Ok((mut engine, tun)) => {
|
Ok((mut engine, tun)) => {
|
||||||
@@ -87,7 +59,6 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
},
|
},
|
||||||
_ = tokio::signal::ctrl_c() => {
|
_ = tokio::signal::ctrl_c() => {
|
||||||
info!("Ctrl+C received, shutting down...");
|
info!("Ctrl+C received, shutting down...");
|
||||||
// Отменяем токен, чтобы Network.run завершился
|
|
||||||
cancel_token.cancel();
|
cancel_token.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +70,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==================================================
|
// ==================================================
|
||||||
// 4. ОЧИСТКА РОУТИНГА
|
// 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");
|
||||||
@@ -111,8 +82,5 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
info!("System routing restored successfully.");
|
info!("System routing restored successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Даем таске Network время на graceful shutdown (чтобы сокеты успели закрыться)
|
|
||||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ impl ConnectionCore {
|
|||||||
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) {
|
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) {
|
||||||
trace!(%handle, "Creating ConnectionCore channels");
|
trace!(%handle, "Creating ConnectionCore channels");
|
||||||
let (tx_to_net, rx_from_smol) =
|
let (tx_to_net, rx_from_smol) =
|
||||||
mpsc::channel::<Bytes>(NetworkConfig::global().channel_capacity);
|
mpsc::channel::<Bytes>(NetworkConfig::global().stream_capacity);
|
||||||
let (tx_to_smol, rx_from_net) =
|
let (tx_to_smol, rx_from_net) =
|
||||||
mpsc::channel::<Bytes>(NetworkConfig::global().channel_capacity);
|
mpsc::channel::<Bytes>(NetworkConfig::global().stream_capacity);
|
||||||
|
|
||||||
let core = Self {
|
let core = Self {
|
||||||
handle,
|
handle,
|
||||||
@@ -42,7 +42,6 @@ impl ConnectionCore {
|
|||||||
(core, rx_from_smol, tx_to_smol)
|
(core, rx_from_smol, tx_to_smol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 2. TCP СОЕДИНЕНИЕ (TcpConnection)
|
// 2. TCP СОЕДИНЕНИЕ (TcpConnection)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -55,14 +54,13 @@ pub enum ConnectionState {
|
|||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_PENDING: usize = 64 * 1024;
|
|
||||||
const TCP_CHUNK_SIZE: usize = 1024 * 16;
|
|
||||||
|
|
||||||
pub struct TcpConnection {
|
pub struct TcpConnection {
|
||||||
core: ConnectionCore,
|
core: ConnectionCore,
|
||||||
state: ConnectionState,
|
state: ConnectionState,
|
||||||
pending_data: BytesMut,
|
pending_data: BytesMut,
|
||||||
handshake_rx: Option<oneshot::Receiver<()>>,
|
handshake_rx: Option<oneshot::Receiver<()>>,
|
||||||
|
chunk_buf: Vec<u8>,
|
||||||
|
server_eof: bool, // <--- ДОБАВЛЕН ФЛАГ ОКОНЧАНИЯ ПЕРЕДАЧИ
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TcpConnection {
|
impl TcpConnection {
|
||||||
@@ -83,18 +81,14 @@ impl TcpConnection {
|
|||||||
state: ConnectionState::Handshaking,
|
state: ConnectionState::Handshaking,
|
||||||
pending_data: BytesMut::new(),
|
pending_data: BytesMut::new(),
|
||||||
handshake_rx: Some(handshake_rx),
|
handshake_rx: Some(handshake_rx),
|
||||||
|
chunk_buf: vec![0u8; NetworkConfig::global().tcp_chunk_size],
|
||||||
|
server_eof: false, // Инициализируем
|
||||||
};
|
};
|
||||||
|
|
||||||
(conn, rx_from_smol, tx_to_smol, handshake_tx)
|
(conn, rx_from_smol, tx_to_smol, handshake_tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_finished(&self, socket: &tcp::Socket) -> bool {
|
|
||||||
matches!(socket.state(), tcp::State::Closed | tcp::State::TimeWait)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tick(&mut self, socket: &mut tcp::Socket) -> bool {
|
pub fn tick(&mut self, socket: &mut tcp::Socket) -> bool {
|
||||||
let state = socket.state();
|
|
||||||
|
|
||||||
match self.state {
|
match self.state {
|
||||||
ConnectionState::Handshaking => {
|
ConnectionState::Handshaking => {
|
||||||
if let Some(rx) = &mut self.handshake_rx {
|
if let Some(rx) = &mut self.handshake_rx {
|
||||||
@@ -105,9 +99,8 @@ impl TcpConnection {
|
|||||||
self.handshake_rx = None;
|
self.handshake_rx = None;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Err(oneshot::error::TryRecvError::Empty) => return true, // Ждем
|
Err(oneshot::error::TryRecvError::Empty) => return true,
|
||||||
Err(oneshot::error::TryRecvError::Closed) => {
|
Err(oneshot::error::TryRecvError::Closed) => {
|
||||||
debug!(%self.core.handle, "TCP Handshake channel dropped/aborted, State -> Closed");
|
|
||||||
self.state = ConnectionState::Closed;
|
self.state = ConnectionState::Closed;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -120,19 +113,12 @@ impl TcpConnection {
|
|||||||
ConnectionState::Active => {
|
ConnectionState::Active => {
|
||||||
self.poll_and_process(socket);
|
self.poll_and_process(socket);
|
||||||
|
|
||||||
if state == tcp::State::CloseWait {
|
// Если сокет достиг финальных стадий, убиваем нашу сессию
|
||||||
debug!(%self.core.handle, "TCP Socket reached CloseWait state, closing");
|
if matches!(socket.state(), tcp::State::Closed | tcp::State::TimeWait) {
|
||||||
socket.close();
|
debug!(%self.core.handle, "TCP Socket is finished, state -> Closed");
|
||||||
self.state = ConnectionState::Closed;
|
self.state = ConnectionState::Closed;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_finished(socket) {
|
|
||||||
debug!(%self.core.handle, "TCP Socket is finished (Closed/TimeWait), state -> Closed");
|
|
||||||
self.state = ConnectionState::Closed;
|
|
||||||
socket.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionState::Closed => {
|
ConnectionState::Closed => {
|
||||||
@@ -146,30 +132,29 @@ impl TcpConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
|
fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
|
||||||
|
let max_pending = NetworkConfig::global().tcp_max_pending;
|
||||||
|
|
||||||
// 1. Вычитываем данные из smoltcp и шлем в Muxer
|
// 1. Вычитываем данные из smoltcp и шлем в Muxer
|
||||||
while socket.can_recv() {
|
while socket.can_recv() {
|
||||||
let mut full = false;
|
let mut full = false;
|
||||||
let mut temp = [0u8; TCP_CHUNK_SIZE];
|
|
||||||
|
|
||||||
if let Ok(n) = socket.peek_slice(&mut temp) {
|
if let Ok(n) = socket.peek_slice(&mut self.chunk_buf) {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let chunk = Bytes::copy_from_slice(&temp[..n]);
|
let chunk = Bytes::copy_from_slice(&self.chunk_buf[..n]);
|
||||||
match self.core.tx.try_send(chunk) {
|
match self.core.tx.try_send(chunk) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
trace!(%self.core.handle, "Forwarded {} bytes from smoltcp to Muxer", n);
|
socket.recv_slice(&mut self.chunk_buf[..n]).unwrap();
|
||||||
socket.recv_slice(&mut temp[..n]).unwrap();
|
|
||||||
}
|
}
|
||||||
Err(mpsc::error::TrySendError::Full(_)) => {
|
Err(mpsc::error::TrySendError::Full(_)) => {
|
||||||
debug!(%self.core.handle, "Muxer TX channel full, backpressure applied to smoltcp read");
|
|
||||||
full = true;
|
full = true;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug!(%self.core.handle, "Muxer TX channel closed unexpectedly, state -> Closed");
|
// Канал Muxer'а закрыт
|
||||||
self.state = ConnectionState::Closed;
|
self.server_eof = true;
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -181,60 +166,57 @@ impl TcpConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Читаем данные из Muxer'а с учетом Backpressure
|
// 2. Читаем данные из Muxer'а
|
||||||
let current_pending = self.pending_data.len();
|
if !self.server_eof {
|
||||||
let fill_ratio = (current_pending as f32 / MAX_PENDING as f32) * 100.0;
|
loop {
|
||||||
|
match self.core.rx.try_recv() {
|
||||||
if current_pending >= MAX_PENDING {
|
Ok(data) => {
|
||||||
warn!(
|
self.pending_data.extend_from_slice(&data);
|
||||||
%self.core.handle,
|
if self.pending_data.len() >= max_pending {
|
||||||
"Backpressure ACTIVE: Buffer is FULL ({} bytes). Stalling RX channel.",
|
break;
|
||||||
current_pending
|
}
|
||||||
);
|
}
|
||||||
} else if fill_ratio > 80.0 {
|
Err(mpsc::error::TryRecvError::Empty) => {
|
||||||
info!(
|
break;
|
||||||
%self.core.handle,
|
}
|
||||||
"Bufferbloat Warning: Buffer {:.1}% full ({} bytes). Latency increasing.",
|
Err(mpsc::error::TryRecvError::Disconnected) => {
|
||||||
fill_ratio, current_pending
|
// ВАЖНО: Удаленный сервер прислал EOF!
|
||||||
);
|
debug!(%self.core.handle, "Server sent EOF (channel disconnected).");
|
||||||
|
self.server_eof = true;
|
||||||
while let Ok(data) = self.core.rx.try_recv() {
|
break;
|
||||||
trace!(%self.core.handle, "Received {} bytes from Muxer (high buffer)", data.len());
|
}
|
||||||
self.pending_data.extend_from_slice(&data);
|
|
||||||
if self.pending_data.len() >= MAX_PENDING {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while let Ok(data) = self.core.rx.try_recv() {
|
|
||||||
trace!(%self.core.handle, "Received {} bytes from Muxer", data.len());
|
|
||||||
self.pending_data.extend_from_slice(&data);
|
|
||||||
if self.pending_data.len() >= MAX_PENDING {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Отправляем буферизированные данные в smoltcp
|
// 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) => {
|
||||||
trace!(%self.core.handle, "Wrote {} bytes from buffer to smoltcp", 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 {
|
|
||||||
info!(
|
|
||||||
%self.core.handle,
|
|
||||||
"Backpressure RELIEVED: Buffer drained to {} bytes",
|
|
||||||
self.pending_data.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!(%self.core.handle, "Smoltcp socket send error: {:?}", e);
|
debug!(%self.core.handle, "Smoltcp send error: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. ГРАЦИОЗНОЕ ЗАКРЫТИЕ (Отправка FIN браузеру)
|
||||||
|
|
||||||
|
// Сценарий А: Сервер закрыл соединение, и мы отдали все остатки данных браузеру
|
||||||
|
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");
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сценарий Б: Браузер сам инициировал закрытие (CloseWait), но мы дожидаемся опустошения буфера
|
||||||
|
if socket.state() == tcp::State::CloseWait
|
||||||
|
&& self.pending_data.is_empty()
|
||||||
|
&& socket.may_send()
|
||||||
|
{
|
||||||
|
debug!(%self.core.handle, "Browser in CloseWait and buffer flushed, sending FIN");
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use netrunner_core::nrxp::TargetAddress;
|
use netrunner_core::{
|
||||||
use netrunner_logger::{debug, error, info, trace, warn};
|
net::network::NetworkConfig,
|
||||||
|
rawcast::{LocalProtocol, RawCastEvent, RawCastFrame},
|
||||||
|
};
|
||||||
|
use netrunner_logger::{debug, error, info, trace};
|
||||||
use smoltcp::{
|
use smoltcp::{
|
||||||
iface::{SocketHandle, SocketSet},
|
iface::{SocketHandle, SocketSet},
|
||||||
socket::{AnySocket, icmp, tcp, udp},
|
socket::{AnySocket, icmp, tcp, udp},
|
||||||
wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket},
|
wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket},
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, time::Instant as StdInstant};
|
use std::{collections::HashMap, time::Instant as StdInstant};
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::sync::mpsc;
|
||||||
use tokio::net::{TcpStream, UdpSocket};
|
|
||||||
|
|
||||||
use crate::net::{
|
use crate::net::{
|
||||||
connection::{TcpConnection, UdpConnection},
|
connection::{TcpConnection, UdpConnection},
|
||||||
@@ -19,13 +21,15 @@ use crate::net::{
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 1. УПРАВЛЕНИЕ СОСТОЯНИЕМ СЕССИЙ (SessionTracker)
|
// 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>,
|
||||||
active_udp: HashMap<SocketHandle, UdpConnection>,
|
active_udp: HashMap<SocketHandle, UdpConnection>,
|
||||||
|
inbound_tx: HashMap<u64, mpsc::Sender<Bytes>>,
|
||||||
failed_until: HashMap<SocketHandle, StdInstant>,
|
failed_until: HashMap<SocketHandle, StdInstant>,
|
||||||
to_remove: Vec<SocketHandle>,
|
to_remove: Vec<SocketHandle>,
|
||||||
|
next_socket_id: u64,
|
||||||
|
handle_to_id: HashMap<SocketHandle, u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionTracker {
|
impl SessionTracker {
|
||||||
@@ -34,11 +38,20 @@ impl SessionTracker {
|
|||||||
last_activity: HashMap::new(),
|
last_activity: HashMap::new(),
|
||||||
active_tcp: HashMap::new(),
|
active_tcp: HashMap::new(),
|
||||||
active_udp: HashMap::new(),
|
active_udp: HashMap::new(),
|
||||||
|
inbound_tx: HashMap::new(),
|
||||||
failed_until: HashMap::new(),
|
failed_until: HashMap::new(),
|
||||||
to_remove: Vec::new(),
|
to_remove: Vec::new(),
|
||||||
|
next_socket_id: 1,
|
||||||
|
handle_to_id: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_socket_id(&mut self) -> u64 {
|
||||||
|
let id = self.next_socket_id;
|
||||||
|
self.next_socket_id = self.next_socket_id.wrapping_add(1);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
fn queue_removal(&mut self, handle: SocketHandle) {
|
fn queue_removal(&mut self, handle: SocketHandle) {
|
||||||
if !self.to_remove.contains(&handle) {
|
if !self.to_remove.contains(&handle) {
|
||||||
self.to_remove.push(handle);
|
self.to_remove.push(handle);
|
||||||
@@ -53,19 +66,28 @@ 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);
|
||||||
|
|
||||||
|
// ВОТ ТУТ ГЛАВНОЕ ИСПРАВЛЕНИЕ:
|
||||||
|
// Достаем НАШ socket_id, который мы выдали этому handle при старте сессии
|
||||||
|
if let Some(socket_id) = self.handle_to_id.remove(&handle) {
|
||||||
|
self.inbound_tx.remove(&socket_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_tcp(
|
fn has_connection_from(
|
||||||
&self,
|
&self,
|
||||||
dst_addr: smoltcp::wire::IpAddress,
|
src_addr: smoltcp::wire::IpAddress,
|
||||||
dst_port: u16,
|
src_port: u16,
|
||||||
socket_set: &SocketSet,
|
socket_set: &SocketSet,
|
||||||
) -> 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(ep) = tcp.local_endpoint() {
|
// Если сокет уже привязался к клиенту, проверяем его исходные данные
|
||||||
return ep.addr == dst_addr && ep.port == dst_port;
|
if let Some(remote) = tcp.remote_endpoint() {
|
||||||
|
if remote.addr == src_addr && remote.port == src_port {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@@ -105,30 +127,6 @@ impl TargetResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_tcp(&self, socket: &tcp::Socket) -> TargetAddress {
|
|
||||||
let ep = match socket.local_endpoint() {
|
|
||||||
Some(ep) => ep,
|
|
||||||
None => {
|
|
||||||
warn!(handle=?socket, "Target resolution failed: no local endpoint");
|
|
||||||
return TargetAddress::Domain("disconnected".to_string(), 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match ep.addr {
|
|
||||||
smoltcp::wire::IpAddress::Ipv4(ip) => {
|
|
||||||
let std_ip = std::net::Ipv4Addr::from(ip);
|
|
||||||
if let Some(domain) = self.fake_ip_store.lookup_by_ip(&std_ip) {
|
|
||||||
TargetAddress::Domain(domain, ep.port)
|
|
||||||
} else {
|
|
||||||
TargetAddress::Ipv4(std_ip, ep.port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
smoltcp::wire::IpAddress::Ipv6(ip) => {
|
|
||||||
TargetAddress::Ipv6(std::net::Ipv6Addr::from(ip), ep.port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_dns_query(&mut self, data: &[u8]) -> Option<Vec<u8>> {
|
fn process_dns_query(&mut self, data: &[u8]) -> Option<Vec<u8>> {
|
||||||
self.dns_handler.handle_query(data, &mut self.fake_ip_store)
|
self.dns_handler.handle_query(data, &mut self.fake_ip_store)
|
||||||
}
|
}
|
||||||
@@ -137,17 +135,21 @@ impl TargetResolver {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 3. ФАБРИКА СОКЕТОВ (SocketFactory)
|
// 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;
|
||||||
|
|
||||||
|
// Для Web-трафика используем максимум из конфига (например, 2 МБ)
|
||||||
|
// Для остальных урезаем в 4 раза, чтобы сэкономить RAM на фоновых соединениях
|
||||||
let buf_size = match port {
|
let buf_size = match port {
|
||||||
443 | 80 => 1024 * 1024 * 2,
|
443 | 80 | 8080 => max_buf,
|
||||||
22 => 32 * 1024,
|
22 => 32 * 1024,
|
||||||
53 => 16 * 1024,
|
53 => 16 * 1024,
|
||||||
_ => 128 * 1024,
|
_ => max_buf / 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
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]),
|
||||||
@@ -158,11 +160,20 @@ impl SocketFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_udp<'a>(port: u16) -> udp::Socket<'a> {
|
fn create_udp<'a>(port: u16) -> udp::Socket<'a> {
|
||||||
|
let config = NetworkConfig::global();
|
||||||
|
let max_buf = config.udp_buffer_size;
|
||||||
|
let payload_size = config.safe_payload_size.max(1); // Защита от деления на 0
|
||||||
|
|
||||||
|
// Вычисляем размер буфера и количество пакетов
|
||||||
let (buf_size, packet_count) = match port {
|
let (buf_size, packet_count) = match port {
|
||||||
443 => (512 * 1024, 390),
|
443 => (max_buf, max_buf / payload_size), // QUIC/HTTP3 трафик
|
||||||
53 => (64 * 1024, 32),
|
53 => (64 * 1024, (64 * 1024) / payload_size), // DNS
|
||||||
_ => (128 * 1024, 100),
|
_ => (max_buf / 2, (max_buf / 2) / payload_size),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Гарантируем, что метаданных хватит хотя бы на 10 пакетов
|
||||||
|
let packet_count = packet_count.max(10);
|
||||||
|
|
||||||
udp::Socket::new(
|
udp::Socket::new(
|
||||||
udp::PacketBuffer::new(
|
udp::PacketBuffer::new(
|
||||||
vec![udp::PacketMetadata::EMPTY; packet_count],
|
vec![udp::PacketMetadata::EMPTY; packet_count],
|
||||||
@@ -186,20 +197,45 @@ impl SocketFactory {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 4. ГЛАВНЫЙ КООРДИНАТОР (ConnectionManager)
|
// 4. ГЛАВНЫЙ КООРДИНАТОР (ConnectionManager)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
pub struct ConnectionManager {
|
pub struct ConnectionManager {
|
||||||
tracker: SessionTracker,
|
tracker: SessionTracker,
|
||||||
resolver: TargetResolver,
|
resolver: TargetResolver,
|
||||||
|
tx_to_tunnel: mpsc::Sender<RawCastFrame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionManager {
|
impl ConnectionManager {
|
||||||
pub fn new(dns_handler: DnsHandler) -> Self {
|
pub fn new(dns_handler: DnsHandler, tx_to_tunnel: mpsc::Sender<RawCastFrame>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tracker: SessionTracker::new(),
|
tracker: SessionTracker::new(),
|
||||||
resolver: TargetResolver::new(dns_handler),
|
resolver: TargetResolver::new(dns_handler),
|
||||||
|
tx_to_tunnel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ВАЖНО: Метод для получения данных из сети (от VPN) и инъекции их в smoltcp
|
||||||
|
// Пытается внедрить пакет. Если канал переполнен — возвращает пакет обратно!
|
||||||
|
pub fn try_inject_inbound(&mut self, frame: RawCastFrame) -> Result<(), RawCastFrame> {
|
||||||
|
if frame.event != RawCastEvent::Data {
|
||||||
|
if frame.event == RawCastEvent::Close {
|
||||||
|
self.tracker.inbound_tx.remove(&frame.socket_id);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(tx) = self.tracker.inbound_tx.get(&frame.socket_id) {
|
||||||
|
match tx.try_send(frame.payload.clone()) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
|
||||||
|
Err(frame) // Возвращаем кадр, чтобы Engine затормозил чтение туннеля
|
||||||
|
}
|
||||||
|
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
|
||||||
|
Ok(()) // Сокет уже закрыт браузером, дропаем пакет
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
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(48));
|
let mut sockets = SocketSet::new(Vec::with_capacity(48));
|
||||||
for _ in 0..n_icmp {
|
for _ in 0..n_icmp {
|
||||||
@@ -238,13 +274,23 @@ impl ConnectionManager {
|
|||||||
|
|
||||||
match ip_packet.next_header() {
|
match ip_packet.next_header() {
|
||||||
IpProtocol::Tcp => {
|
IpProtocol::Tcp => {
|
||||||
if let Ok(tcp_packet) = TcpPacket::new_checked(ip_packet.payload()) {
|
let src_addr = ip_packet.src_addr();
|
||||||
if tcp_packet.syn() && !tcp_packet.ack() {
|
let dst_addr = ip_packet.dst_addr();
|
||||||
let dst_port = tcp_packet.dst_port();
|
|
||||||
trace!(%dst_addr, dst_port, "Received TCP SYN");
|
|
||||||
|
|
||||||
if !self.tracker.has_tcp(dst_addr.into(), dst_port, socket_set) {
|
if let Ok(tcp_packet) = TcpPacket::new_checked(ip_packet.payload()) {
|
||||||
debug!(%dst_addr, dst_port, "No active TCP socket found, allocating new one");
|
// 2. Проверяем, что это пакет инициализации соединения (SYN)
|
||||||
|
if tcp_packet.syn() && !tcp_packet.ack() {
|
||||||
|
let src_port = tcp_packet.src_port();
|
||||||
|
let dst_port = tcp_packet.dst_port();
|
||||||
|
|
||||||
|
trace!(%dst_addr, dst_port, src_port, "Received TCP SYN");
|
||||||
|
|
||||||
|
// 3. Теперь src_addr доступен в этой области видимости
|
||||||
|
if !self
|
||||||
|
.tracker
|
||||||
|
.has_connection_from(src_addr.into(), src_port, socket_set)
|
||||||
|
{
|
||||||
|
debug!(%dst_addr, dst_port, src_port, "Allocating new TCP socket");
|
||||||
|
|
||||||
let mut socket = SocketFactory::create_tcp(dst_port);
|
let mut socket = SocketFactory::create_tcp(dst_port);
|
||||||
let endpoint = IpListenEndpoint {
|
let endpoint = IpListenEndpoint {
|
||||||
@@ -254,19 +300,16 @@ impl ConnectionManager {
|
|||||||
|
|
||||||
match socket.listen(endpoint) {
|
match socket.listen(endpoint) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
debug!(%dst_addr, dst_port, "TCP socket successfully listening");
|
|
||||||
socket_set.add(socket);
|
socket_set.add(socket);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(%dst_addr, dst_port, "Failed to listen on TCP socket: {:?}", e);
|
error!(%dst_addr, dst_port, "Failed to listen: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
trace!(%dst_addr, dst_port, "TCP socket already exists, ignoring SYN");
|
trace!(%dst_addr, dst_port, src_port, "Socket already exists, ignoring SYN");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
trace!("try_create_socket: Failed to parse TCP payload");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IpProtocol::Udp => {
|
IpProtocol::Udp => {
|
||||||
@@ -324,7 +367,6 @@ impl ConnectionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) {
|
fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) {
|
||||||
if socket.state() == tcp::State::Closed {
|
if socket.state() == tcp::State::Closed {
|
||||||
self.tracker.active_tcp.remove(&handle);
|
self.tracker.active_tcp.remove(&handle);
|
||||||
@@ -335,67 +377,63 @@ impl ConnectionManager {
|
|||||||
if socket.state() == tcp::State::Established
|
if socket.state() == tcp::State::Established
|
||||||
&& !self.tracker.active_tcp.contains_key(&handle)
|
&& !self.tracker.active_tcp.contains_key(&handle)
|
||||||
{
|
{
|
||||||
let target = self.resolver.resolve_tcp(socket);
|
let (dst_ip, dst_port) = match socket.local_endpoint() {
|
||||||
if let TargetAddress::Domain(ref d, _) = target {
|
Some(ep) => match ep.addr {
|
||||||
if d == "disconnected" {
|
smoltcp::wire::IpAddress::Ipv4(ip) => (std::net::Ipv4Addr::from(ip), ep.port),
|
||||||
socket.abort();
|
_ => return,
|
||||||
return;
|
},
|
||||||
}
|
None => return,
|
||||||
}
|
|
||||||
|
|
||||||
info!(%handle, target = %target, "New TCP session established");
|
|
||||||
|
|
||||||
let (conn, mut rx_from_smol, tx_to_smol, handshake_tx) = TcpConnection::new(handle);
|
|
||||||
self.tracker.active_tcp.insert(handle, conn);
|
|
||||||
|
|
||||||
// Конвертируем TargetAddress в строку, понятную для tokio::net
|
|
||||||
let target_str = match target {
|
|
||||||
TargetAddress::Domain(d, p) => format!("{}:{}", d, p),
|
|
||||||
TargetAddress::Ipv4(ip, p) => format!("{}:{}", ip, p),
|
|
||||||
TargetAddress::Ipv6(ip, p) => format!("{}:{}", ip, p),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
tokio::spawn(async move {
|
info!(%handle, ip = %dst_ip, port = dst_port, "New TCP session intercepted");
|
||||||
let mut upstream = match TcpStream::connect(&target_str).await {
|
|
||||||
Ok(s) => s,
|
let socket_id = self.tracker.generate_socket_id();
|
||||||
Err(e) => {
|
self.tracker.handle_to_id.insert(handle, socket_id);
|
||||||
error!("Failed to connect to upstream TCP {}: {}", target_str, e);
|
|
||||||
return;
|
let (conn, mut rx_from_smol, tx_to_smol, handshake_tx) = TcpConnection::new(handle);
|
||||||
}
|
|
||||||
};
|
self.tracker.active_tcp.insert(handle, conn);
|
||||||
|
self.tracker.inbound_tx.insert(socket_id, tx_to_smol);
|
||||||
|
|
||||||
|
// ИСПРАВЛЕНИЕ ЗДЕСЬ: Переводим IP обратно в домен
|
||||||
|
let target_str = if let Some(domain) = self.resolver.fake_ip_store.lookup_by_ip(&dst_ip)
|
||||||
|
{
|
||||||
|
format!("{}:{}", domain, dst_port)
|
||||||
|
} else {
|
||||||
|
format!("{}:{}", dst_ip, dst_port)
|
||||||
|
};
|
||||||
|
|
||||||
|
let tx_tunnel = self.tx_to_tunnel.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
// Создаем кадр коннекта
|
||||||
|
let mut connect_frame =
|
||||||
|
RawCastFrame::connect(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
|
||||||
|
// Кладем доменное имя в payload, чтобы ClientHandler его прочитал
|
||||||
|
connect_frame.payload = bytes::Bytes::from(target_str);
|
||||||
|
|
||||||
|
if tx_tunnel.send(connect_frame).await.is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Сообщаем соединению smoltcp, что мы готовы (рукопожатие выполнено)
|
|
||||||
let _ = handshake_tx.send(());
|
let _ = handshake_tx.send(());
|
||||||
|
|
||||||
let (mut r, mut w) = upstream.into_split();
|
while let Some(data) = rx_from_smol.recv().await {
|
||||||
|
let data_frame = RawCastFrame::data(
|
||||||
// Читаем из tun (smoltcp) и пишем во внешнюю сеть
|
LocalProtocol::Tcp,
|
||||||
let to_upstream = async {
|
socket_id,
|
||||||
while let Some(data) = rx_from_smol.recv().await {
|
dst_ip,
|
||||||
if w.write_all(&data).await.is_err() {
|
dst_port,
|
||||||
break;
|
data.to_vec(),
|
||||||
}
|
);
|
||||||
|
if tx_tunnel.send(data_frame).await.is_err() {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Читаем из внешней сети и пишем в tun (smoltcp)
|
let close_frame =
|
||||||
let from_upstream = async {
|
RawCastFrame::close(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
|
||||||
let mut buf = vec![0u8; 8192]; // Читаем чанками
|
let _ = tx_tunnel.send(close_frame).await;
|
||||||
while let Ok(n) = r.read(&mut buf).await {
|
|
||||||
if n == 0 {
|
|
||||||
break;
|
|
||||||
} // EOF
|
|
||||||
if tx_to_smol
|
|
||||||
.send(Bytes::copy_from_slice(&buf[..n]))
|
|
||||||
.await
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tokio::select! { _ = to_upstream => {}, _ = from_upstream => {} }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,12 +442,7 @@ impl ConnectionManager {
|
|||||||
socket.abort();
|
socket.abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if socket.state() == tcp::State::CloseWait {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
||||||
|
|
||||||
@@ -427,71 +460,54 @@ impl ConnectionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if socket.is_open() && !self.tracker.active_udp.contains_key(&handle) {
|
if socket.is_open() && !self.tracker.active_udp.contains_key(&handle) {
|
||||||
let ep = socket.endpoint();
|
let (dst_ip, dst_port) = match socket.endpoint().addr {
|
||||||
let target = match ep.addr {
|
|
||||||
Some(smoltcp::wire::IpAddress::Ipv4(ip)) => {
|
Some(smoltcp::wire::IpAddress::Ipv4(ip)) => {
|
||||||
TargetAddress::Ipv4(std::net::Ipv4Addr::from(ip), ep.port)
|
(std::net::Ipv4Addr::from(ip), socket.endpoint().port)
|
||||||
}
|
}
|
||||||
Some(smoltcp::wire::IpAddress::Ipv6(ip)) => {
|
_ => return,
|
||||||
TargetAddress::Ipv6(std::net::Ipv6Addr::from(ip), ep.port)
|
|
||||||
}
|
|
||||||
None => return,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 1. Генерируем уникальный ID
|
||||||
|
let socket_id = self.tracker.generate_socket_id();
|
||||||
|
|
||||||
|
// 2. Сохраняем привязку для будущего cleanup
|
||||||
|
self.tracker.handle_to_id.insert(handle, socket_id);
|
||||||
|
|
||||||
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.tracker.active_udp.insert(handle, conn);
|
|
||||||
|
|
||||||
// Конвертируем для tokio::net::UdpSocket
|
self.tracker.active_udp.insert(handle, conn);
|
||||||
let target_str = match target {
|
self.tracker.inbound_tx.insert(socket_id, tx_to_smol);
|
||||||
TargetAddress::Domain(d, p) => format!("{}:{}", d, p),
|
let target_str = if let Some(domain) = self.resolver.fake_ip_store.lookup_by_ip(&dst_ip)
|
||||||
TargetAddress::Ipv4(ip, p) => format!("{}:{}", ip, p),
|
{
|
||||||
TargetAddress::Ipv6(ip, p) => format!("{}:{}", ip, p),
|
format!("{}:{}", domain, dst_port)
|
||||||
|
} else {
|
||||||
|
format!("{}:{}", dst_ip, dst_port)
|
||||||
};
|
};
|
||||||
|
|
||||||
tokio::spawn(async move {
|
let tx_tunnel = self.tx_to_tunnel.clone();
|
||||||
// Создаем локальный UDP сокет со случайным портом
|
|
||||||
let upstream = match UdpSocket::bind("0.0.0.0:0").await {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to bind local UDP socket: {}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// "Подключаем" UDP сокет к цели (включает фильтр пакетов и позволяет использовать обычные send/recv)
|
tokio::spawn(async move {
|
||||||
if let Err(e) = upstream.connect(&target_str).await {
|
let mut connect_frame =
|
||||||
error!("Failed to connect UDP to {}: {}", target_str, e);
|
RawCastFrame::connect(LocalProtocol::Udp, socket_id, dst_ip, dst_port);
|
||||||
return;
|
connect_frame.payload = bytes::Bytes::from(target_str); // Кладем домен
|
||||||
|
let _ = tx_tunnel.send(connect_frame).await;
|
||||||
|
|
||||||
|
while let Some(data) = rx_from_smol.recv().await {
|
||||||
|
let data_frame = RawCastFrame::data(
|
||||||
|
LocalProtocol::Udp,
|
||||||
|
socket_id,
|
||||||
|
dst_ip,
|
||||||
|
dst_port,
|
||||||
|
data.to_vec(),
|
||||||
|
);
|
||||||
|
if tx_tunnel.send(data_frame).await.is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let upstream = std::sync::Arc::new(upstream);
|
let close_frame =
|
||||||
let upstream_rx = upstream.clone();
|
RawCastFrame::close(LocalProtocol::Udp, socket_id, dst_ip, dst_port);
|
||||||
let upstream_tx = upstream;
|
let _ = tx_tunnel.send(close_frame).await;
|
||||||
|
|
||||||
// Из smoltcp наружу
|
|
||||||
let to_upstream = async {
|
|
||||||
while let Some(data) = rx_from_smol.recv().await {
|
|
||||||
if upstream_tx.send(&data).await.is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Извне в smoltcp
|
|
||||||
let from_upstream = async {
|
|
||||||
let mut buf = vec![0u8; 65536]; // Максимальный размер UDP датаграммы
|
|
||||||
while let Ok(n) = upstream_rx.recv(&mut buf).await {
|
|
||||||
if tx_to_smol
|
|
||||||
.send(Bytes::copy_from_slice(&buf[..n]))
|
|
||||||
.await
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tokio::select! { _ = to_upstream => {}, _ = from_upstream => {} }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+96
-26
@@ -1,4 +1,6 @@
|
|||||||
use netrunner_core::net::ClientHandler;
|
use netrunner_core::net::ClientHandler;
|
||||||
|
use netrunner_core::net::network::NetworkConfig;
|
||||||
|
use netrunner_core::rawcast::RawCastFrame;
|
||||||
use smoltcp::iface::PollResult;
|
use smoltcp::iface::PollResult;
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
use smoltcp::wire::{IpAddress, IpCidr};
|
use smoltcp::wire::{IpAddress, IpCidr};
|
||||||
@@ -35,17 +37,25 @@ pub struct Engine {
|
|||||||
to_smoltcp_tx: UnboundedSender<TokenBuffer>,
|
to_smoltcp_tx: UnboundedSender<TokenBuffer>,
|
||||||
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
|
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
|
||||||
avail: Arc<AtomicBool>,
|
avail: Arc<AtomicBool>,
|
||||||
|
rx_from_tunnel: mpsc::Receiver<RawCastFrame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
pub fn new(config: Config, caps: DeviceCapabilities, dns_handler: DnsHandler) -> Self {
|
pub fn new(
|
||||||
|
config: Config,
|
||||||
|
caps: DeviceCapabilities,
|
||||||
|
dns_handler: DnsHandler,
|
||||||
|
tx_to_tunnel: mpsc::Sender<RawCastFrame>, // Куда менеджер будет слать пакеты
|
||||||
|
rx_from_tunnel: mpsc::Receiver<RawCastFrame>, // Откуда движок будет читать пакеты
|
||||||
|
) -> Self {
|
||||||
let now = Engine::current_time();
|
let now = Engine::current_time();
|
||||||
|
|
||||||
let (mut device, to_smoltcp_tx, from_smoltcp_rx, avail) = VirtTunDevice::new(caps);
|
let (mut device, to_smoltcp_tx, from_smoltcp_rx, avail) = VirtTunDevice::new(caps);
|
||||||
let interface = Interface::new(config, &mut device, now);
|
let interface = Interface::new(config, &mut device, now);
|
||||||
|
|
||||||
let socket_set = ConnectionManager::setup_sockets(2);
|
let socket_set = ConnectionManager::setup_sockets(2);
|
||||||
let manager = ConnectionManager::new(dns_handler);
|
|
||||||
|
// Передаем TX-канал в ConnectionManager
|
||||||
|
let manager = ConnectionManager::new(dns_handler, tx_to_tunnel);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
interface,
|
interface,
|
||||||
@@ -55,6 +65,7 @@ impl Engine {
|
|||||||
from_smoltcp_rx: Some(from_smoltcp_rx),
|
from_smoltcp_rx: Some(from_smoltcp_rx),
|
||||||
avail,
|
avail,
|
||||||
manager,
|
manager,
|
||||||
|
rx_from_tunnel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,17 +80,36 @@ impl Engine {
|
|||||||
Self::spawn_tun_writer(writer, from_smoltcp_rx);
|
Self::spawn_tun_writer(writer, from_smoltcp_rx);
|
||||||
|
|
||||||
let mut last_log = StdInstant::now();
|
let mut last_log = StdInstant::now();
|
||||||
|
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);
|
||||||
|
|
||||||
if self.to_smoltcp_tx.send(token).is_ok() {
|
if self.to_smoltcp_tx.send(token).is_ok() {
|
||||||
self.device.mark_rx_available();
|
self.device.mark_rx_available();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Пытаемся протолкнуть застрявший кадр
|
||||||
|
if let Some(frame) = stuck_frame.take() {
|
||||||
|
if let Err(returned_frame) = self.manager.try_inject_inbound(frame) {
|
||||||
|
stuck_frame = Some(returned_frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Если затора нет, читаем новые кадры из VPN-туннеля
|
||||||
|
if stuck_frame.is_none() {
|
||||||
|
while let Ok(frame) = self.rx_from_tunnel.try_recv() {
|
||||||
|
if let Err(returned_frame) = self.manager.try_inject_inbound(frame) {
|
||||||
|
stuck_frame = Some(returned_frame);
|
||||||
|
break; // СТОП! Локальный сокет переполнен, давим на тормоза!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Двигаем стейт-машину smoltcp
|
||||||
let result = self.poll();
|
let result = self.poll();
|
||||||
self.manager.process_sockets(&mut self.socket_set);
|
self.manager.process_sockets(&mut self.socket_set);
|
||||||
|
|
||||||
@@ -89,16 +119,48 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if matches!(result, PollResult::SocketStateChanged) {
|
if matches!(result, PollResult::SocketStateChanged) {
|
||||||
continue;
|
self.manager.cleanup(&mut self.socket_set);
|
||||||
}
|
|
||||||
|
|
||||||
if self.avail.swap(false, Ordering::Acquire) {
|
|
||||||
tokio::task::yield_now().await;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.manager.cleanup(&mut self.socket_set);
|
self.manager.cleanup(&mut self.socket_set);
|
||||||
self.poll_delay().await;
|
|
||||||
|
let delay = self
|
||||||
|
.interface
|
||||||
|
.poll_delay(Self::current_time(), &self.socket_set);
|
||||||
|
let sleep_duration = match delay {
|
||||||
|
Some(d) => Duration::from_micros(d.micros()),
|
||||||
|
None => Duration::from_millis(10),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 5. Умный select!
|
||||||
|
if stuck_frame.is_none() {
|
||||||
|
tokio::select! {
|
||||||
|
_ = sleep(sleep_duration) => {}
|
||||||
|
Some(token) = tun_to_engine_rx.recv() => {
|
||||||
|
self.manager.try_create_socket_from_packet(&token, &mut self.socket_set);
|
||||||
|
if self.to_smoltcp_tx.send(token).is_ok() {
|
||||||
|
self.device.mark_rx_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(frame) = self.rx_from_tunnel.recv() => {
|
||||||
|
if let Err(returned_frame) = self.manager.try_inject_inbound(frame) {
|
||||||
|
stuck_frame = Some(returned_frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Если буфер переполнен, ждем только таймер или данные от браузера
|
||||||
|
tokio::select! {
|
||||||
|
_ = sleep(sleep_duration) => {}
|
||||||
|
Some(token) = tun_to_engine_rx.recv() => {
|
||||||
|
self.manager.try_create_socket_from_packet(&token, &mut self.socket_set);
|
||||||
|
if self.to_smoltcp_tx.send(token).is_ok() {
|
||||||
|
self.device.mark_rx_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,16 +170,6 @@ impl Engine {
|
|||||||
.poll(now, &mut self.device, &mut self.socket_set)
|
.poll(now, &mut self.device, &mut self.socket_set)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn poll_delay(&mut self) {
|
|
||||||
let timestamp = Self::current_time();
|
|
||||||
let delay = self.interface.poll_delay(timestamp, &self.socket_set);
|
|
||||||
let sleep_duration = match delay {
|
|
||||||
Some(d) => Duration::from_micros(d.micros()),
|
|
||||||
None => Duration::from_millis(10),
|
|
||||||
};
|
|
||||||
sleep(sleep_duration).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_tun_reader(
|
fn spawn_tun_reader(
|
||||||
mut reader: DeviceReader,
|
mut reader: DeviceReader,
|
||||||
to_engine: UnboundedSender<TokenBuffer>,
|
to_engine: UnboundedSender<TokenBuffer>,
|
||||||
@@ -305,18 +357,36 @@ impl EngineBuilder {
|
|||||||
// 3. Конфигурация интерфейса smoltcp
|
// 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) =
|
||||||
|
mpsc::channel(NetworkConfig::global().muxer_capacity);
|
||||||
|
|
||||||
|
// tx_to_engine: ClientHandler пишет, Движок читает (входящий трафик)
|
||||||
|
let (tx_for_client_handler, rx_from_tunnel) =
|
||||||
|
mpsc::channel(NetworkConfig::global().muxer_capacity);
|
||||||
|
|
||||||
// 4. Подключение к серверу
|
// 4. Подключение к серверу
|
||||||
info!("Establishing secure tunnel to proxy server...");
|
info!("Establishing secure tunnel to proxy server...");
|
||||||
ClientHandler::connect(&self.config.remote_address)
|
|
||||||
.await
|
// ВАЖНО: Тебе нужно добавить эти аргументы в `ClientHandler::connect`,
|
||||||
.map_err(|e| format!("Failed to establish secure tunnel: {}", e))?;
|
// чтобы он знал, откуда забирать `RawCastFrame` для отправки на сервер,
|
||||||
|
// и куда отдавать `RawCastFrame`, прилетевшие от сервера.
|
||||||
|
ClientHandler::connect(
|
||||||
|
&self.config.remote_address,
|
||||||
|
rx_for_client_handler,
|
||||||
|
tx_for_client_handler,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to establish secure tunnel: {}", e))?;
|
||||||
|
|
||||||
info!("Secure tunnel established, Muxer is ready.");
|
info!("Secure tunnel established, Muxer is ready.");
|
||||||
|
|
||||||
// 5. Инициализация и настройка Engine
|
// 5. Инициализация и настройка Engine (передаем каналы)
|
||||||
let mut engine = Engine::new(smol_config, caps, dns_handler);
|
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);
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
net::{
|
net::{
|
||||||
connection::{
|
connection::{engine::TunnelEngine, handler::StreamHandler, muxer::Muxer},
|
||||||
bridge::run_tcp_bridge, engine::TunnelEngine, handler::StreamHandler, muxer::Muxer,
|
|
||||||
},
|
|
||||||
network::NetworkConfig,
|
network::NetworkConfig,
|
||||||
},
|
},
|
||||||
nrxp::{
|
nrxp::{Codec, ErrorAction, FrameType},
|
||||||
Codec, ErrorAction, FrameType, {SocksReply, SocksRequest},
|
rawcast::{LocalProtocol, RawCastEvent, RawCastFrame},
|
||||||
},
|
|
||||||
parser::Parser,
|
|
||||||
tlseng::BrowserProfile,
|
tlseng::BrowserProfile,
|
||||||
};
|
};
|
||||||
use bytes::BytesMut;
|
use bytes::{Bytes, BytesMut};
|
||||||
use netrunner_logger::{info, warn};
|
use netrunner_logger::{error, info, warn};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncReadExt, AsyncWriteExt},
|
io::{AsyncReadExt, AsyncWriteExt},
|
||||||
net::{
|
net::{
|
||||||
@@ -21,7 +17,6 @@ use tokio::{
|
|||||||
},
|
},
|
||||||
sync::mpsc,
|
sync::mpsc,
|
||||||
};
|
};
|
||||||
use tokio_util::sync::CancellationToken;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum ConnectionRole {
|
pub enum ConnectionRole {
|
||||||
@@ -60,53 +55,34 @@ impl Connection {
|
|||||||
codec: Codec::new(false),
|
codec: Codec::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_socks_request(&mut self) -> Result<SocksRequest, String> {
|
|
||||||
loop {
|
|
||||||
match SocksRequest::parse(&mut self.read_buf) {
|
|
||||||
Ok(Some(req)) => return Ok(req),
|
|
||||||
Ok(None) => {}
|
|
||||||
Err(e) => return Err(format!("Socks parse error: {}", e)),
|
|
||||||
}
|
|
||||||
let n = self
|
|
||||||
.inbound
|
|
||||||
.read_buf(&mut self.read_buf)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
if n == 0 {
|
|
||||||
return Err("Client closed connection".into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_socks_reply(&mut self, reply: SocksReply) -> Result<(), String> {
|
|
||||||
let mut buf = BytesMut::with_capacity(24);
|
|
||||||
reply.write_to(&mut buf);
|
|
||||||
self.outbound
|
|
||||||
.write_all(&buf)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientHandler {
|
pub struct ClientHandler;
|
||||||
pub(crate) conn: Connection,
|
|
||||||
pub(crate) muxer: Muxer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientHandler {
|
impl ClientHandler {
|
||||||
pub async fn connect(remote_proxy_addr: &str) -> Result<Muxer, String> {
|
/// Вспомогательная функция: устанавливает одно физическое TLS-соединение
|
||||||
|
/// и возвращает готовый Muxer для работы с ним.
|
||||||
|
async fn establish_leg(remote_proxy_addr: &str, leg_name: &str) -> Result<Muxer, String> {
|
||||||
|
info!(
|
||||||
|
"Establishing dedicated {} tunnel to {}...",
|
||||||
|
leg_name, remote_proxy_addr
|
||||||
|
);
|
||||||
|
|
||||||
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| format!("Failed to connect: {}", e))?;
|
||||||
let (inbound, outbound) = stream.into_split();
|
|
||||||
|
|
||||||
|
if let Err(e) = stream.set_nodelay(true) {
|
||||||
|
warn!("Failed to set TCP_NODELAY on {} leg: {}", leg_name, e);
|
||||||
|
}
|
||||||
|
let (inbound, outbound) = stream.into_split();
|
||||||
let mut conn = Connection::new_raw(inbound, outbound);
|
let mut conn = Connection::new_raw(inbound, outbound);
|
||||||
|
|
||||||
let ch = conn
|
let ch = conn
|
||||||
.codec
|
.codec
|
||||||
.make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
|
.make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
.map_err(|e| format!("Handshake generation failed: {:?}", e))?;
|
||||||
|
|
||||||
conn.outbound
|
conn.outbound
|
||||||
.write_all(&ch)
|
.write_all(&ch)
|
||||||
.await
|
.await
|
||||||
@@ -122,18 +98,19 @@ impl ClientHandler {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return Err("EOF during handshake".into());
|
return Err(format!("EOF during handshake on {} leg", leg_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => return Err(format!("TLS error: {:?}", e)),
|
Err(e) => return Err(format!("TLS error on {} leg: {:?}", leg_name, e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().channel_capacity);
|
info!("{} TLS Handshake complete. Starting Engine.", leg_name);
|
||||||
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().channel_capacity);
|
|
||||||
|
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
|
||||||
|
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
|
||||||
|
|
||||||
let muxer = Muxer::new(control_tx, data_tx, true);
|
let muxer = Muxer::new(control_tx, data_tx, true);
|
||||||
|
|
||||||
let handler =
|
let handler =
|
||||||
std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Client));
|
std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Client));
|
||||||
|
|
||||||
@@ -152,102 +129,125 @@ impl ClientHandler {
|
|||||||
Ok(muxer)
|
Ok(muxer)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_udp_associate(&mut self) -> Result<(), String> {
|
/// Главная функция запуска клиента
|
||||||
let reply = SocksReply::ConnectResult {
|
pub async fn connect(
|
||||||
reply_code: 0x00,
|
remote_proxy_addr: &str,
|
||||||
atyp: 0x01,
|
mut rx_from_engine: mpsc::Receiver<RawCastFrame>,
|
||||||
addr: [0, 0, 0, 0],
|
tx_to_engine: mpsc::Sender<RawCastFrame>,
|
||||||
port: 0,
|
) -> Result<(), String> {
|
||||||
};
|
// 1. Устанавливаем ДВА независимых соединения
|
||||||
self.conn.send_socks_reply(reply).await?;
|
// Мы можем сделать это параллельно через tokio::try_join! для скорости
|
||||||
|
let (tcp_muxer, udp_muxer) = tokio::try_join!(
|
||||||
|
Self::establish_leg(remote_proxy_addr, "TCP"),
|
||||||
|
Self::establish_leg(remote_proxy_addr, "UDP"),
|
||||||
|
)?;
|
||||||
|
|
||||||
let mut buf = [0u8; 1024];
|
info!("Dual-tunnel architecture established successfully!");
|
||||||
loop {
|
|
||||||
if self
|
// 2. ЗАПУСКАЕМ МОСТ-РОУТЕР
|
||||||
.conn
|
tokio::spawn(async move {
|
||||||
.inbound
|
while let Some(raw_frame) = rx_from_engine.recv().await {
|
||||||
.read(&mut buf)
|
let stream_id = raw_frame.socket_id as u32;
|
||||||
.await
|
let is_udp = raw_frame.protocol == LocalProtocol::Udp;
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
== 0
|
// РОУТИНГ: Выбираем нужный физический канал
|
||||||
{
|
let muxer = if is_udp {
|
||||||
break;
|
udp_muxer.clone()
|
||||||
|
} else {
|
||||||
|
tcp_muxer.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
match raw_frame.event {
|
||||||
|
RawCastEvent::Connect => {
|
||||||
|
let (v_tx, mut v_rx) =
|
||||||
|
mpsc::channel(NetworkConfig::global().tcp_buffer_size);
|
||||||
|
muxer.register_stream(stream_id, v_tx);
|
||||||
|
|
||||||
|
// Читаем домен, который мы прокинули в предыдущем шаге
|
||||||
|
let target = if !raw_frame.payload.is_empty() {
|
||||||
|
String::from_utf8_lossy(&raw_frame.payload).to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}:{}", raw_frame.dst_ip, raw_frame.dst_port)
|
||||||
|
};
|
||||||
|
|
||||||
|
let frame_type = if is_udp {
|
||||||
|
FrameType::UdpConnect
|
||||||
|
} else {
|
||||||
|
FrameType::Connect
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = muxer
|
||||||
|
.send_control(stream_id, frame_type, Bytes::from(target))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("Failed to send connect control frame: {}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tx_engine_clone = tx_to_engine.clone();
|
||||||
|
let mut muxer_clone = muxer.clone();
|
||||||
|
let dst_ip = raw_frame.dst_ip;
|
||||||
|
let dst_port = raw_frame.dst_port;
|
||||||
|
let protocol = raw_frame.protocol;
|
||||||
|
let socket_id = raw_frame.socket_id;
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Some(payload) = v_rx.recv().await {
|
||||||
|
if payload.is_empty() {
|
||||||
|
let close_frame =
|
||||||
|
RawCastFrame::close(protocol, socket_id, dst_ip, dst_port);
|
||||||
|
let _ = tx_engine_clone.send(close_frame).await;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
let data_frame = RawCastFrame::data(
|
||||||
|
protocol,
|
||||||
|
socket_id,
|
||||||
|
dst_ip,
|
||||||
|
dst_port,
|
||||||
|
payload.to_vec(),
|
||||||
|
);
|
||||||
|
if tx_engine_clone.send(data_frame).await.is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
muxer_clone.remove_stream(stream_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
RawCastEvent::Data => {
|
||||||
|
let frame_type = if is_udp {
|
||||||
|
FrameType::UdpData
|
||||||
|
} else {
|
||||||
|
FrameType::Data
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = muxer
|
||||||
|
.send_control(stream_id, frame_type, raw_frame.payload)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("Failed to send data frame: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RawCastEvent::Close => {
|
||||||
|
let _ = muxer
|
||||||
|
.send_control(stream_id, FrameType::Close, Bytes::new())
|
||||||
|
.await;
|
||||||
|
muxer.remove_stream(stream_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
info!("ClientHandler bridge task terminated.");
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl TunnelHandler for ClientHandler {
|
|
||||||
async fn run(mut self) -> Result<(), String> {
|
|
||||||
info!("Starting SOCKS multiplexed handling");
|
|
||||||
|
|
||||||
self.conn.read_socks_request().await?;
|
|
||||||
self.conn
|
|
||||||
.send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 })
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let req = self.conn.read_socks_request().await?;
|
|
||||||
|
|
||||||
match req {
|
|
||||||
SocksRequest::Connect {
|
|
||||||
command: 0x01,
|
|
||||||
target,
|
|
||||||
} => {
|
|
||||||
let stream_id = self.muxer.next_id();
|
|
||||||
let (v_tx, mut v_rx) =
|
|
||||||
mpsc::channel::<bytes::Bytes>(NetworkConfig::global().tcp_buffer_size);
|
|
||||||
self.muxer.register_stream(stream_id, v_tx);
|
|
||||||
|
|
||||||
self.muxer
|
|
||||||
.send_control(
|
|
||||||
stream_id,
|
|
||||||
FrameType::Connect,
|
|
||||||
bytes::Bytes::from(target.to_string()),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let first_payload =
|
|
||||||
tokio::time::timeout(std::time::Duration::from_secs(10), v_rx.recv())
|
|
||||||
.await
|
|
||||||
.map_err(|_| "Timeout waiting for proxy response")?
|
|
||||||
.ok_or("No data from proxy")?;
|
|
||||||
|
|
||||||
if first_payload.len() >= 2 && first_payload[1] == 0x00 {
|
|
||||||
self.conn
|
|
||||||
.outbound
|
|
||||||
.write_all(&first_payload)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
} else {
|
|
||||||
self.conn.outbound.write_all(&first_payload).await.ok();
|
|
||||||
return Err("Proxy rejected connection".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let browser_in = self.conn.inbound;
|
|
||||||
let browser_out = self.conn.outbound;
|
|
||||||
let muxer = self.muxer;
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
|
||||||
run_tcp_bridge(stream_id, browser_in, browser_out, muxer, v_rx).await;
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
SocksRequest::Connect { command: 0x03, .. } => {
|
|
||||||
info!("Handling UDP Associate request");
|
|
||||||
self.handle_udp_associate().await
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => Err("Unsupported SOCKS command".into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ServerHandler {
|
pub struct ServerHandler {
|
||||||
pub(crate) conn: Connection,
|
pub(crate) conn: Connection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerHandler {
|
impl ServerHandler {
|
||||||
async fn handle_stealth_fallback(
|
async fn handle_stealth_fallback(
|
||||||
mut client_inbound: OwnedReadHalf,
|
mut client_inbound: OwnedReadHalf,
|
||||||
@@ -295,14 +295,13 @@ impl ServerHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl TunnelHandler for ServerHandler {
|
impl TunnelHandler for ServerHandler {
|
||||||
async fn run(mut self) -> Result<(), String> {
|
async fn run(mut self) -> Result<(), String> {
|
||||||
info!("Acting as TLS Server with Stealth Fallback");
|
info!("Acting as TLS Server with Stealth Fallback");
|
||||||
|
|
||||||
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().channel_capacity);
|
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
|
||||||
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().channel_capacity);
|
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
|
||||||
let muxer = Muxer::new(control_tx, data_tx, false);
|
let muxer = Muxer::new(control_tx, data_tx, false);
|
||||||
|
|
||||||
let handshake_timeout = std::time::Duration::from_secs(1);
|
let handshake_timeout = std::time::Duration::from_secs(1);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use bytes::{Bytes, BytesMut};
|
use bytes::Bytes;
|
||||||
use netrunner_logger::{debug, error, info};
|
use netrunner_logger::{debug, error, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -10,9 +10,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
network::NetworkConfig,
|
network::NetworkConfig,
|
||||||
},
|
},
|
||||||
nrxp::{
|
nrxp::{Frame, FrameType},
|
||||||
SocksReply, {Frame, FrameType},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) struct StreamHandler {
|
pub(crate) struct StreamHandler {
|
||||||
@@ -43,7 +41,7 @@ impl StreamHandler {
|
|||||||
let target_str = String::from_utf8_lossy(&payload).to_string();
|
let target_str = String::from_utf8_lossy(&payload).to_string();
|
||||||
let muxer = self.muxer.clone();
|
let muxer = self.muxer.clone();
|
||||||
|
|
||||||
let (v_tx, v_rx) = tokio::sync::mpsc::channel(NetworkConfig::global().channel_capacity);
|
let (v_tx, v_rx) = tokio::sync::mpsc::channel(NetworkConfig::global().stream_capacity);
|
||||||
muxer.register_stream(stream_id, v_tx);
|
muxer.register_stream(stream_id, v_tx);
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -61,29 +59,17 @@ impl StreamHandler {
|
|||||||
let elapsed = start.elapsed();
|
let elapsed = start.elapsed();
|
||||||
info!(stream_id, target = %target_str, latency_ms = elapsed.as_millis(), "Remote TCP connection established");
|
info!(stream_id, target = %target_str, latency_ms = elapsed.as_millis(), "Remote TCP connection established");
|
||||||
|
|
||||||
let mut reply_buf = BytesMut::with_capacity(10);
|
// Больше никаких ответов (Reply), просто прокидываем байты!
|
||||||
let reply = SocksReply::ConnectResult {
|
|
||||||
reply_code: 0x00,
|
|
||||||
atyp: 0x01,
|
|
||||||
addr: [0, 0, 0, 0],
|
|
||||||
port: 0,
|
|
||||||
};
|
|
||||||
reply.write_to(&mut reply_buf);
|
|
||||||
|
|
||||||
let _ = muxer
|
|
||||||
.send_control(stream_id, FrameType::Connect, reply_buf.freeze())
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let (r, w) = stream.into_split();
|
let (r, w) = stream.into_split();
|
||||||
run_tcp_bridge(stream_id, r, w, muxer, v_rx).await;
|
run_tcp_bridge(stream_id, r, w, muxer, v_rx).await;
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
error!(stream_id, target = %target_str, error = %e, "TCP connection failed");
|
error!(stream_id, target = %target_str, error = %e, "TCP connection failed");
|
||||||
Self::send_error_reply(&muxer, stream_id, 0x01, FrameType::Connect).await;
|
Self::close_stream(&muxer, stream_id).await;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!(stream_id, target = %target_str, "Connection timed out (DNS/TCP)");
|
error!(stream_id, target = %target_str, "Connection timed out (DNS/TCP)");
|
||||||
Self::send_error_reply(&muxer, stream_id, 0x04, FrameType::Connect).await;
|
Self::close_stream(&muxer, stream_id).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -97,7 +83,7 @@ impl StreamHandler {
|
|||||||
let target_str = String::from_utf8_lossy(&payload).to_string();
|
let target_str = String::from_utf8_lossy(&payload).to_string();
|
||||||
let muxer = self.muxer.clone();
|
let muxer = self.muxer.clone();
|
||||||
|
|
||||||
let (v_tx, v_rx) = tokio::sync::mpsc::channel(NetworkConfig::global().channel_capacity);
|
let (v_tx, v_rx) = tokio::sync::mpsc::channel(NetworkConfig::global().stream_capacity);
|
||||||
muxer.register_stream(stream_id, v_tx);
|
muxer.register_stream(stream_id, v_tx);
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -107,30 +93,16 @@ impl StreamHandler {
|
|||||||
Ok(socket) => {
|
Ok(socket) => {
|
||||||
if let Err(e) = socket.connect(&target_str).await {
|
if let Err(e) = socket.connect(&target_str).await {
|
||||||
error!(stream_id, target = %target_str, error = %e, "UDP connect failed");
|
error!(stream_id, target = %target_str, error = %e, "UDP connect failed");
|
||||||
Self::send_error_reply(&muxer, stream_id, 0x01, FrameType::UdpConnect)
|
Self::close_stream(&muxer, stream_id).await;
|
||||||
.await;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut reply_buf = BytesMut::with_capacity(10);
|
// Успех - просто начинаем слушать UDP и слать в туннель
|
||||||
let reply = SocksReply::ConnectResult {
|
|
||||||
reply_code: 0x00,
|
|
||||||
atyp: 0x01,
|
|
||||||
addr: [0, 0, 0, 0],
|
|
||||||
port: 0,
|
|
||||||
};
|
|
||||||
reply.write_to(&mut reply_buf);
|
|
||||||
|
|
||||||
let _ = muxer
|
|
||||||
.send_control(stream_id, FrameType::UdpConnect, reply_buf.freeze())
|
|
||||||
.await;
|
|
||||||
|
|
||||||
run_udp_bridge(stream_id, socket, muxer, v_rx).await;
|
run_udp_bridge(stream_id, socket, muxer, v_rx).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(stream_id, target = %target_str, error = %e, "UDP bind failed");
|
error!(stream_id, target = %target_str, error = %e, "UDP bind failed");
|
||||||
Self::send_error_reply(&muxer, stream_id, 0x01, FrameType::UdpConnect)
|
Self::close_stream(&muxer, stream_id).await;
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -143,19 +115,12 @@ impl StreamHandler {
|
|||||||
self.muxer.dispatch_to_local(stream_id, payload).await;
|
self.muxer.dispatch_to_local(stream_id, payload).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_error_reply(muxer: &Muxer, stream_id: u32, code: u8, frame_type: FrameType) {
|
// Вспомогательная функция вместо send_error_reply
|
||||||
muxer.remove_stream(stream_id);
|
async fn close_stream(muxer: &Muxer, stream_id: u32) {
|
||||||
let mut reply_buf = BytesMut::with_capacity(10);
|
|
||||||
let reply = SocksReply::ConnectResult {
|
|
||||||
reply_code: code,
|
|
||||||
atyp: 0x01,
|
|
||||||
addr: [0, 0, 0, 0],
|
|
||||||
port: 0,
|
|
||||||
};
|
|
||||||
reply.write_to(&mut reply_buf);
|
|
||||||
let _ = muxer
|
let _ = muxer
|
||||||
.send_control(stream_id, frame_type, reply_buf.freeze())
|
.send_control(stream_id, FrameType::Close, Bytes::new())
|
||||||
.await;
|
.await;
|
||||||
|
muxer.remove_stream(stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_data(&self, stream_id: u32, payload: Bytes) {
|
async fn on_data(&self, stream_id: u32, payload: Bytes) {
|
||||||
|
|||||||
+45
-60
@@ -1,10 +1,10 @@
|
|||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
net::connection::{ClientHandler, Connection, ConnectionRole, ServerHandler, TunnelHandler},
|
net::connection::{Connection, ConnectionRole, ServerHandler, TunnelHandler},
|
||||||
nrxp::{FRAME_HEADER_SIZE, MAX_PADDING_SIZE},
|
nrxp::{FRAME_HEADER_SIZE, MAX_PADDING_SIZE},
|
||||||
};
|
};
|
||||||
use netrunner_logger::{error, info};
|
use netrunner_logger::{error, info, warn};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
@@ -32,50 +32,36 @@ 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(1350);
|
NetworkConfig::init_global(1350);
|
||||||
|
|
||||||
match self.role {
|
match self.role {
|
||||||
ConnectionRole::Client => {
|
ConnectionRole::Client => {
|
||||||
info!("Starting Client mode");
|
// В новой архитектуре клиент запускается через EngineBuilder + TUN.
|
||||||
let server_addr = self
|
// Структура Network теперь используется только для запуска Сервера.
|
||||||
.remote_proxy_addr
|
error!("Client mode cannot be run via Network::run anymore.");
|
||||||
.as_ref()
|
error!("Please use EngineBuilder to initialize the TUN client.");
|
||||||
.ok_or("No proxy addr")
|
panic!("Legacy SOCKS5 client mode has been removed.");
|
||||||
.unwrap();
|
|
||||||
let muxer = match ClientHandler::connect(server_addr).await {
|
|
||||||
Ok(m) => m,
|
|
||||||
Err(e) => {
|
|
||||||
error!(error = %e, "Global tunnel failed.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let listener = TcpListener::bind(&addr).await.expect("SOCKS bind failed");
|
|
||||||
loop {
|
|
||||||
tokio::select! {
|
|
||||||
_ = token.cancelled() => break,
|
|
||||||
res = listener.accept() => {
|
|
||||||
if let Ok((stream, _client_addr)) = res {
|
|
||||||
let conn = Connection::new(stream, false);
|
|
||||||
let handler = ClientHandler{ conn, muxer: muxer.clone() };
|
|
||||||
tokio::spawn(async move {
|
|
||||||
if let Err(e) = handler.run().await {
|
|
||||||
error!(error = %e, "Client handler error");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ConnectionRole::Server => {
|
ConnectionRole::Server => {
|
||||||
|
info!("Starting Server mode on {}", addr);
|
||||||
let listener = TcpListener::bind(&addr).await.expect("Server bind failed");
|
let listener = TcpListener::bind(&addr).await.expect("Server bind failed");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = token.cancelled() => break,
|
_ = token.cancelled() => {
|
||||||
|
info!("Shutdown signal received, stopping server.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
res = listener.accept() => {
|
res = listener.accept() => {
|
||||||
if let Ok((stream, client_addr)) = res {
|
if let Ok((stream, client_addr)) = res {
|
||||||
|
info!("New connection from {}", client_addr);
|
||||||
|
|
||||||
|
// Создаем соединение (init = true для сервера)
|
||||||
let conn = Connection::new(stream, true);
|
let conn = Connection::new(stream, true);
|
||||||
let handler = ServerHandler { conn };
|
let handler = ServerHandler { conn };
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Err(e) = handler.run().await {
|
if let Err(e) = handler.run().await {
|
||||||
error!(client = %client_addr, error = %e, "Server handler error");
|
error!(client = %client_addr, error = %e, "Server handler error");
|
||||||
@@ -96,52 +82,51 @@ pub struct NetworkConfig {
|
|||||||
pub mtu: usize,
|
pub mtu: usize,
|
||||||
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 channel_capacity: usize,
|
pub muxer_capacity: usize, // Глобальные каналы (Muxer <-> Engine)
|
||||||
|
pub stream_capacity: usize, // Локальные каналы 1 сокета (Engine <-> Muxer)
|
||||||
|
|
||||||
|
pub smoltcp_socket_buf: usize,
|
||||||
|
pub tcp_max_pending: usize,
|
||||||
|
pub tcp_chunk_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkConfig {
|
impl NetworkConfig {
|
||||||
pub fn new(system_mtu: usize) -> Self {
|
pub fn new(system_mtu: usize) -> Self {
|
||||||
let transport_overhead = 28; // IPv4 + UDP
|
let transport_overhead = 28;
|
||||||
|
|
||||||
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(10).saturating_sub(255);
|
||||||
|
|
||||||
let safe_payload = max_wire_frame
|
let muxer_capacity = 64;
|
||||||
.saturating_sub(FRAME_HEADER_SIZE as usize)
|
let stream_capacity = 4;
|
||||||
.saturating_sub((MAX_PADDING_SIZE - 1) as usize);
|
|
||||||
|
|
||||||
let tcp_chunks_count = 65536 / safe_payload;
|
|
||||||
let tcp_buffer = safe_payload * tcp_chunks_count;
|
|
||||||
|
|
||||||
let udp_chunks_count = 16384 / safe_payload;
|
|
||||||
let udp_buffer = safe_payload * udp_chunks_count;
|
|
||||||
|
|
||||||
let channel_cap = 1024;
|
|
||||||
|
|
||||||
netrunner_logger::info!(
|
|
||||||
mtu = system_mtu,
|
|
||||||
payload = safe_payload,
|
|
||||||
tcp_buf = tcp_buffer,
|
|
||||||
"Network Optimizer: Calculations complete for current MTU"
|
|
||||||
);
|
|
||||||
|
|
||||||
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,
|
||||||
tcp_buffer_size: tcp_buffer,
|
|
||||||
udp_buffer_size: udp_buffer,
|
// Заменяем громоздкие вычисления на стандартные 64KB чанки для системных сокетов
|
||||||
channel_capacity: channel_cap,
|
tcp_buffer_size: 64 * 1024,
|
||||||
|
udp_buffer_size: 64 * 1024,
|
||||||
|
|
||||||
|
muxer_capacity,
|
||||||
|
stream_capacity,
|
||||||
|
smoltcp_socket_buf: 64 * 1024,
|
||||||
|
tcp_max_pending: 64 * 1024,
|
||||||
|
tcp_chunk_size: 16 * 1024,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_global(system_mtu: usize) {
|
pub fn init_global(system_mtu: usize) {
|
||||||
let config = Self::new(system_mtu);
|
let config = Self::new(system_mtu);
|
||||||
if GLOBAL_NET_CONFIG.set(config).is_err() {
|
if GLOBAL_NET_CONFIG.set(config).is_err() {
|
||||||
netrunner_logger::warn!("Global network config was already initialized!");
|
warn!("Global network config was already initialized!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn global() -> &'static Self {
|
pub fn global() -> &'static Self {
|
||||||
GLOBAL_NET_CONFIG
|
GLOBAL_NET_CONFIG
|
||||||
.get()
|
.get()
|
||||||
|
|||||||
@@ -3,11 +3,7 @@ mod bridge;
|
|||||||
mod codec;
|
mod codec;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod frame;
|
mod frame;
|
||||||
mod socks;
|
|
||||||
|
|
||||||
// Экспортируем для остального ядра только необходимые типы
|
// Экспортируем для остального ядра только необходимые типы
|
||||||
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};
|
||||||
pub use socks::TargetAddress;
|
|
||||||
pub(crate) use socks::{SocksReply, SocksRequest};
|
|
||||||
|
|||||||
@@ -1,345 +0,0 @@
|
|||||||
use std::fmt;
|
|
||||||
|
|
||||||
use bytes::{Buf, BufMut, BytesMut};
|
|
||||||
|
|
||||||
use crate::parser::Parser;
|
|
||||||
|
|
||||||
const SOCKS5_VERSION: u8 = 0x05;
|
|
||||||
const REPLY_SUCCESS: u8 = 0x00;
|
|
||||||
const REPLY_AUTH_FAILURE: u8 = 0xFF;
|
|
||||||
const SOCKS5_MIN_HEADER: usize = 4;
|
|
||||||
const ATYP_IPV4: u8 = 0x01;
|
|
||||||
const ATYP_DOMAIN: u8 = 0x03;
|
|
||||||
const ATYP_IPV6: u8 = 0x04;
|
|
||||||
const IPV4_SIZE: usize = 4;
|
|
||||||
const IPV6_SIZE: usize = 16;
|
|
||||||
const PORT_SIZE: usize = 2;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) enum SocksRequest {
|
|
||||||
Handshake { methods: Vec<u8> },
|
|
||||||
Connect { command: u8, target: SocksTarget },
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
impl SocksRequest {
|
|
||||||
pub async fn handle_handshake<S>(
|
|
||||||
stream: &mut S,
|
|
||||||
buf: &mut BytesMut,
|
|
||||||
) -> Result<SocksTarget, String>
|
|
||||||
where
|
|
||||||
S: tokio::io::AsyncReadExt + tokio::io::AsyncWriteExt + Unpin,
|
|
||||||
{
|
|
||||||
loop {
|
|
||||||
if let Some(req) = Self::parse(buf)? {
|
|
||||||
if let SocksRequest::Handshake { .. } = req {
|
|
||||||
let mut reply = BytesMut::with_capacity(2);
|
|
||||||
SocksReply::HandshakeSelect { method: 0x00 }.write_to(&mut reply);
|
|
||||||
stream.write_all(&reply).await.map_err(|e| e.to_string())?;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return Err("Expected Handshake, got something else".into());
|
|
||||||
}
|
|
||||||
if stream.read_buf(buf).await.map_err(|e| e.to_string())? == 0 {
|
|
||||||
return Err("Client closed during greeting".into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if let Some(req) = Self::parse(buf)? {
|
|
||||||
if let SocksRequest::Connect { command, target } = req {
|
|
||||||
if command != 0x01 {
|
|
||||||
return Err(format!("Unsupported SOCKS command: 0x{:02X}", command));
|
|
||||||
}
|
|
||||||
return Ok(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if stream.read_buf(buf).await.map_err(|e| e.to_string())? == 0 {
|
|
||||||
return Err("Client closed during connect request".into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn perform_client_handshake<S>(
|
|
||||||
stream: &mut S,
|
|
||||||
target_addr: &TargetAddress,
|
|
||||||
) -> Result<(), String>
|
|
||||||
where
|
|
||||||
S: tokio::io::AsyncReadExt + tokio::io::AsyncWriteExt + Unpin,
|
|
||||||
{
|
|
||||||
let greeting = [SOCKS5_VERSION, 0x01, 0x00];
|
|
||||||
stream
|
|
||||||
.write_all(&greeting)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let mut method_selection = [0u8; 2];
|
|
||||||
stream
|
|
||||||
.read_exact(&mut method_selection)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
if method_selection[0] != SOCKS5_VERSION || method_selection[1] != 0x00 {
|
|
||||||
return Err(format!(
|
|
||||||
"Proxy rejected auth method or version: {:02X?}",
|
|
||||||
method_selection
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut connect_req = BytesMut::with_capacity(32);
|
|
||||||
connect_req.put_u8(SOCKS5_VERSION);
|
|
||||||
connect_req.put_u8(0x01);
|
|
||||||
connect_req.put_u8(0x00);
|
|
||||||
|
|
||||||
match target_addr {
|
|
||||||
TargetAddress::Ipv4(ip, port) => {
|
|
||||||
connect_req.put_u8(ATYP_IPV4);
|
|
||||||
connect_req.put_slice(&ip.octets());
|
|
||||||
connect_req.put_u16(*port);
|
|
||||||
}
|
|
||||||
TargetAddress::Ipv6(ip, port) => {
|
|
||||||
connect_req.put_u8(ATYP_IPV6);
|
|
||||||
connect_req.put_slice(&ip.octets());
|
|
||||||
connect_req.put_u16(*port);
|
|
||||||
}
|
|
||||||
TargetAddress::Domain(host, port) => {
|
|
||||||
connect_req.put_u8(ATYP_DOMAIN);
|
|
||||||
|
|
||||||
let host_bytes = host.as_bytes();
|
|
||||||
connect_req.put_u8(host_bytes.len() as u8);
|
|
||||||
connect_req.put_slice(host_bytes);
|
|
||||||
connect_req.put_u16(*port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stream
|
|
||||||
.write_all(&connect_req)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let mut reply_header = [0u8; 4];
|
|
||||||
stream
|
|
||||||
.read_exact(&mut reply_header)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
if reply_header[1] != REPLY_SUCCESS {
|
|
||||||
return Err(format!(
|
|
||||||
"Proxy failed to connect, code: {:02X}",
|
|
||||||
reply_header[1]
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let atyp = reply_header[3];
|
|
||||||
let remain_len = match atyp {
|
|
||||||
ATYP_IPV4 => IPV4_SIZE + PORT_SIZE,
|
|
||||||
ATYP_IPV6 => IPV6_SIZE + PORT_SIZE,
|
|
||||||
ATYP_DOMAIN => {
|
|
||||||
let len = stream.read_u8().await.map_err(|e| e.to_string())?;
|
|
||||||
len as usize + PORT_SIZE
|
|
||||||
}
|
|
||||||
_ => return Err("Unknown ATYP in proxy response".into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut discard = vec![0u8; remain_len];
|
|
||||||
stream
|
|
||||||
.read_exact(&mut discard)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) enum SocksReply {
|
|
||||||
HandshakeSelect {
|
|
||||||
method: u8,
|
|
||||||
},
|
|
||||||
ConnectResult {
|
|
||||||
reply_code: u8,
|
|
||||||
atyp: u8,
|
|
||||||
addr: [u8; 4],
|
|
||||||
port: u16,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum TargetAddress {
|
|
||||||
Ipv4(std::net::Ipv4Addr, u16),
|
|
||||||
Domain(String, u16),
|
|
||||||
Ipv6(std::net::Ipv6Addr, u16),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for TargetAddress {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
TargetAddress::Ipv4(addr, port) => write!(f, "{}:{}", addr, port),
|
|
||||||
TargetAddress::Domain(domain, port) => write!(f, "{}:{}", domain, port),
|
|
||||||
TargetAddress::Ipv6(addr, port) => write!(f, "[{}]:{}", addr, port),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct SocksTarget {
|
|
||||||
pub addr: TargetAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SocksReply {
|
|
||||||
pub fn write_to(self, buf: &mut BytesMut) {
|
|
||||||
match self {
|
|
||||||
SocksReply::HandshakeSelect { method } => {
|
|
||||||
buf.put_u8(SOCKS5_VERSION);
|
|
||||||
buf.put_u8(method);
|
|
||||||
}
|
|
||||||
SocksReply::ConnectResult {
|
|
||||||
reply_code,
|
|
||||||
atyp,
|
|
||||||
addr,
|
|
||||||
port,
|
|
||||||
} => {
|
|
||||||
buf.put_u8(SOCKS5_VERSION);
|
|
||||||
buf.put_u8(reply_code);
|
|
||||||
buf.put_u8(0x00);
|
|
||||||
buf.put_u8(atyp);
|
|
||||||
buf.put_slice(&addr);
|
|
||||||
buf.put_u16(port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SocksTarget {
|
|
||||||
pub fn to_string(&self) -> String {
|
|
||||||
match &self.addr {
|
|
||||||
TargetAddress::Ipv4(ip, port) => {
|
|
||||||
format!("{}:{}", ip, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
TargetAddress::Ipv6(ip, port) => {
|
|
||||||
format!("[{}]:{}", ip, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
TargetAddress::Domain(domain, port) => {
|
|
||||||
let clean_domain = domain.replace('\0', "");
|
|
||||||
format!("{}:{}", clean_domain, port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parser for SocksTarget {
|
|
||||||
type Error = String;
|
|
||||||
|
|
||||||
fn can_parse(bytes: &BytesMut) -> bool {
|
|
||||||
if bytes.len() < 4 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let atyp = bytes[3];
|
|
||||||
match atyp {
|
|
||||||
ATYP_IPV4 => bytes.len() >= 4 + 4 + 2,
|
|
||||||
|
|
||||||
ATYP_IPV6 => bytes.len() >= 4 + 16 + 2,
|
|
||||||
|
|
||||||
ATYP_DOMAIN => {
|
|
||||||
if bytes.len() < 5 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let domain_len = bytes[4] as usize;
|
|
||||||
bytes.len() >= 4 + 1 + domain_len + 2
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
|
|
||||||
if !Self::can_parse(bytes) {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let atyp = bytes[3];
|
|
||||||
|
|
||||||
let total_len = match atyp {
|
|
||||||
ATYP_IPV4 => SOCKS5_MIN_HEADER + IPV4_SIZE + PORT_SIZE,
|
|
||||||
ATYP_DOMAIN => SOCKS5_MIN_HEADER + 1 + (bytes[4] as usize) + PORT_SIZE,
|
|
||||||
ATYP_IPV6 => SOCKS5_MIN_HEADER + IPV6_SIZE + PORT_SIZE,
|
|
||||||
_ => return Err("Unsupported address type".to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut packet = bytes.split_to(total_len);
|
|
||||||
packet.advance(4);
|
|
||||||
|
|
||||||
let addr = match atyp {
|
|
||||||
ATYP_IPV4 => {
|
|
||||||
let octets: [u8; 4] = packet.split_to(4)[..].try_into().unwrap();
|
|
||||||
let port = packet.get_u16();
|
|
||||||
TargetAddress::Ipv4(octets.into(), port)
|
|
||||||
}
|
|
||||||
ATYP_DOMAIN => {
|
|
||||||
let len = packet.get_u8() as usize;
|
|
||||||
let domain_bytes = packet.split_to(len);
|
|
||||||
let domain = String::from_utf8(domain_bytes.to_vec())
|
|
||||||
.map_err(|_| "Invalid UTF-8 domain".to_string())?;
|
|
||||||
let port = packet.get_u16();
|
|
||||||
TargetAddress::Domain(domain, port)
|
|
||||||
}
|
|
||||||
ATYP_IPV6 => {
|
|
||||||
let octets: [u8; 16] = packet.split_to(16)[..].try_into().unwrap();
|
|
||||||
let port = packet.get_u16();
|
|
||||||
TargetAddress::Ipv6(octets.into(), port)
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(SocksTarget { addr }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parser for SocksRequest {
|
|
||||||
type Error = String;
|
|
||||||
|
|
||||||
fn can_parse(bytes: &BytesMut) -> bool {
|
|
||||||
if bytes.len() < 2 || bytes[0] != SOCKS5_VERSION {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let nmethods = bytes[1] as usize;
|
|
||||||
if bytes.len() >= 2 + nmethods {
|
|
||||||
if bytes.len() >= SOCKS5_MIN_HEADER && SocksTarget::can_parse(bytes) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
|
|
||||||
if bytes.len() < 2 || bytes[0] != SOCKS5_VERSION {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if bytes.len() >= SOCKS5_MIN_HEADER && SocksTarget::can_parse(bytes) {
|
|
||||||
let command = bytes[1];
|
|
||||||
if let Some(target) = SocksTarget::parse(bytes)? {
|
|
||||||
return Ok(Some(SocksRequest::Connect { command, target }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let nmethods = bytes[1] as usize;
|
|
||||||
let total_handshake = 2 + nmethods;
|
|
||||||
|
|
||||||
if bytes.len() >= total_handshake {
|
|
||||||
let mut packet = bytes.split_to(total_handshake);
|
|
||||||
packet.advance(2);
|
|
||||||
let mut methods = vec![0u8; nmethods];
|
|
||||||
packet.copy_to_slice(&mut methods);
|
|
||||||
|
|
||||||
return Ok(Some(SocksRequest::Handshake { methods }));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
use bytes::Bytes;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
nrxp::{Frame, FrameType},
|
||||||
|
rawcast::frame::{LocalProtocol, RawCastEvent, RawCastFrame},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RawCastAdapter;
|
||||||
|
|
||||||
|
impl RawCastAdapter {
|
||||||
|
/// Перевод из RawCast (от локального TUN/smoltcp) в NRXP (в ядро/сеть)
|
||||||
|
pub fn to_nrxp(raw: RawCastFrame) -> Result<Frame, String> {
|
||||||
|
// Кастим ID сокета
|
||||||
|
let stream_id = raw.socket_id as u32;
|
||||||
|
|
||||||
|
let (frame_type, payload) = match (raw.protocol, raw.event) {
|
||||||
|
// Открытие TCP соединения
|
||||||
|
(LocalProtocol::Tcp, RawCastEvent::Connect) => {
|
||||||
|
let target = format!("{}:{}", raw.dst_ip, raw.dst_port);
|
||||||
|
(FrameType::Connect, Bytes::from(target))
|
||||||
|
}
|
||||||
|
// Открытие UDP сессии
|
||||||
|
(LocalProtocol::Udp, RawCastEvent::Connect) => {
|
||||||
|
let target = format!("{}:{}", raw.dst_ip, raw.dst_port);
|
||||||
|
(FrameType::UdpConnect, Bytes::from(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Передача данных (TCP)
|
||||||
|
(LocalProtocol::Tcp, RawCastEvent::Data) => (FrameType::Data, raw.payload),
|
||||||
|
// Передача данных (UDP)
|
||||||
|
(LocalProtocol::Udp, RawCastEvent::Data) => (FrameType::UdpData, raw.payload),
|
||||||
|
|
||||||
|
// Закрытие соединения (одинаково для TCP и UDP)
|
||||||
|
(_, RawCastEvent::Close) => (FrameType::Close, Bytes::new()),
|
||||||
|
|
||||||
|
// ICMP пока не поддерживается в NRXP, отбрасываем
|
||||||
|
(LocalProtocol::Icmp, _) => {
|
||||||
|
return Err("ICMP protocol is not supported by NRXP core".into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Используем твой новый удобный конструктор Frame!
|
||||||
|
Ok(Frame::new(stream_id, frame_type, payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Перевод из NRXP (от сервера) обратно в RawCast (в smoltcp)
|
||||||
|
///
|
||||||
|
/// ВАЖНО: Так как NRXP Frame не содержит IP и Port (только stream_id),
|
||||||
|
/// локальный клиент должен помнить, какому stream_id какой IP/Port принадлежит.
|
||||||
|
pub fn from_nrxp(
|
||||||
|
nrxp_frame: Frame,
|
||||||
|
dst_ip: Ipv4Addr,
|
||||||
|
dst_port: u16,
|
||||||
|
is_udp: bool,
|
||||||
|
) -> Result<RawCastFrame, String> {
|
||||||
|
let socket_id = nrxp_frame.header.stream_id as u64;
|
||||||
|
let protocol = if is_udp {
|
||||||
|
LocalProtocol::Udp
|
||||||
|
} else {
|
||||||
|
LocalProtocol::Tcp
|
||||||
|
};
|
||||||
|
|
||||||
|
let event = match nrxp_frame.header.frame_type {
|
||||||
|
// Сервер прислал успешный коннект
|
||||||
|
FrameType::Connect | FrameType::UdpConnect => RawCastEvent::Connect,
|
||||||
|
|
||||||
|
// Сервер прислал данные
|
||||||
|
FrameType::Data | FrameType::UdpData => RawCastEvent::Data,
|
||||||
|
|
||||||
|
// Сервер закрыл соединение
|
||||||
|
FrameType::Close => RawCastEvent::Close,
|
||||||
|
|
||||||
|
FrameType::Heartbeat => return Err("Heartbeat should be handled by muxer".into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(RawCastFrame {
|
||||||
|
protocol,
|
||||||
|
event,
|
||||||
|
socket_id,
|
||||||
|
dst_ip,
|
||||||
|
dst_port,
|
||||||
|
payload: nrxp_frame.payload,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,4 @@
|
|||||||
|
mod adapter;
|
||||||
mod frame;
|
mod frame;
|
||||||
|
|
||||||
|
pub use frame::{LocalProtocol, RawCastEvent, RawCastFrame};
|
||||||
|
|||||||
Reference in New Issue
Block a user