start remake local proxy
This commit is contained in:
+43
-13
@@ -3,12 +3,14 @@ uniffi::setup_scaffolding!();
|
|||||||
|
|
||||||
mod net;
|
mod net;
|
||||||
mod tun;
|
mod tun;
|
||||||
pub use crate::tun::{routing, tun::Tun}; //for desktop test in main.rs
|
pub use crate::tun::{routing, tun::Tun};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
net::engine::{EngineBuilder, EngineConfig},
|
net::engine::{EngineBuilder, EngineConfig},
|
||||||
tun::routing::reset_platform_routing,
|
tun::routing::reset_platform_routing,
|
||||||
};
|
};
|
||||||
|
use netrunner_core::proxy::connection::connection::ConnectionRole;
|
||||||
|
use netrunner_core::proxy::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;
|
||||||
@@ -16,7 +18,6 @@ use tokio_util::sync::CancellationToken;
|
|||||||
|
|
||||||
pub static RUNTIME: OnceLock<Runtime> = OnceLock::new();
|
pub static RUNTIME: OnceLock<Runtime> = OnceLock::new();
|
||||||
|
|
||||||
// Инициализация Tokio Runtime
|
|
||||||
fn get_runtime() -> &'static Runtime {
|
fn get_runtime() -> &'static Runtime {
|
||||||
RUNTIME.get_or_init(|| {
|
RUNTIME.get_or_init(|| {
|
||||||
tokio::runtime::Builder::new_multi_thread()
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
@@ -54,7 +55,7 @@ impl Drop for Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// SESSION MANAGER (Публичный API UniFFI)
|
// SESSION MANAGER
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|
||||||
#[derive(uniffi::Object)]
|
#[derive(uniffi::Object)]
|
||||||
@@ -75,14 +76,10 @@ impl SessionManager {
|
|||||||
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
|
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
|
||||||
let remote_proxy_ip = addr.ip().to_string();
|
let remote_proxy_ip = addr.ip().to_string();
|
||||||
|
|
||||||
// 1. Создаем базовый конфиг
|
|
||||||
let mut config = EngineConfig::new(&remote_address).with_cache_path(&cache_dir);
|
let mut config = EngineConfig::new(&remote_address).with_cache_path(&cache_dir);
|
||||||
|
|
||||||
// 2. Тонкая настройка под платформу
|
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
{
|
{
|
||||||
// На мобилках роутингом обычно управляет VpnService (Android) или NEPacketTunnelProvider (iOS)
|
|
||||||
// Поэтому отключаем попытки движка менять системные таблицы роутинга напрямую
|
|
||||||
config = config.disable_routing().with_mtu(1280);
|
config = config.disable_routing().with_mtu(1280);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,10 +88,44 @@ impl SessionManager {
|
|||||||
config = config.with_mtu(1350);
|
config = config.with_mtu(1350);
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.spawn(async move {
|
// --- 1. ЗАПУСК ЛОКАЛЬНОГО ПРОКСИ (NETWORK) ---
|
||||||
info!("Starting VPN session thread...");
|
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 {
|
||||||
|
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();
|
||||||
|
runtime.spawn(async move {
|
||||||
|
info!("Starting VPN Engine thread...");
|
||||||
|
|
||||||
// 3. Инициализация TUN устройства (специфично для платформ)
|
|
||||||
let tun_device = {
|
let tun_device = {
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
{
|
{
|
||||||
@@ -108,7 +139,7 @@ impl SessionManager {
|
|||||||
.tun_name("netr0")
|
.tun_name("netr0")
|
||||||
.address((10, 0, 0, 1))
|
.address((10, 0, 0, 1))
|
||||||
.netmask((255, 255, 255, 0))
|
.netmask((255, 255, 255, 0))
|
||||||
.mtu(config.mtu as u16) // Используем MTU из нашего конфига
|
.mtu(config.mtu as u16)
|
||||||
.up();
|
.up();
|
||||||
})
|
})
|
||||||
.expect("Failed to init TUN")
|
.expect("Failed to init TUN")
|
||||||
@@ -122,7 +153,6 @@ impl SessionManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 4. Используем обновленный билдер
|
|
||||||
let builder_result = EngineBuilder::new(config)
|
let builder_result = EngineBuilder::new(config)
|
||||||
.with_tun(tun_device)
|
.with_tun(tun_device)
|
||||||
.build()
|
.build()
|
||||||
@@ -135,7 +165,7 @@ impl SessionManager {
|
|||||||
res = engine.run(tun) => {
|
res = engine.run(tun) => {
|
||||||
info!("Engine loop finished: {:?}", res);
|
info!("Engine loop finished: {:?}", res);
|
||||||
},
|
},
|
||||||
_ = cancel_token.cancelled() => {
|
_ = engine_token.cancelled() => {
|
||||||
info!("Engine task shutting down via token");
|
info!("Engine task shutting down via token");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+56
-14
@@ -2,25 +2,60 @@ use netrunner_logger::{error, info};
|
|||||||
mod net;
|
mod net;
|
||||||
mod tun;
|
mod tun;
|
||||||
|
|
||||||
// Импортируем и Билдер, и Конфиг
|
// Импортируем Билдер, Конфиг и модули маршрутизации
|
||||||
use crate::tun::{routing::reset_platform_routing, tun::Tun};
|
use crate::tun::{routing::reset_platform_routing, tun::Tun};
|
||||||
use net::engine::{EngineBuilder, EngineConfig};
|
use net::engine::{EngineBuilder, EngineConfig};
|
||||||
|
|
||||||
|
// Импортируем компоненты локального прокси
|
||||||
|
use netrunner_core::proxy::connection::connection::ConnectionRole;
|
||||||
|
use netrunner_core::proxy::network::Network;
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
netrunner_logger::Logger::init();
|
netrunner_logger::Logger::init();
|
||||||
info!("Initializing NetRunner Stack...");
|
info!("Initializing NetRunner Stack...");
|
||||||
|
|
||||||
let remote_address = "147.45.43.70:443";
|
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; // Локальный порт прокси
|
||||||
|
|
||||||
// 1. Создаем конфигурацию движка
|
// Токен для управления жизненным циклом фоновых задач
|
||||||
// Здесь мы можем гибко настроить параметры, которые раньше были захардкожены
|
let cancel_token = CancellationToken::new();
|
||||||
let config = EngineConfig::new(remote_address)
|
|
||||||
|
// ==================================================
|
||||||
|
// 1. ЗАПУСК ЛОКАЛЬНОГО ПРОКСИ (NETWORK)
|
||||||
|
// ==================================================
|
||||||
|
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)
|
||||||
.with_cache_path(".")
|
.with_cache_path(".")
|
||||||
.with_mtu(1350); // Указываем MTU здесь, чтобы использовать его и для TUN, и для стека
|
.with_mtu(1350);
|
||||||
|
|
||||||
// 2. Создаем TUN интерфейс
|
|
||||||
// Используем значение 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")
|
||||||
@@ -34,34 +69,38 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
info!("TUN interface is UP: 10.0.0.1/24 (MTU: {})", config.mtu);
|
info!("TUN interface is UP: 10.0.0.1/24 (MTU: {})", config.mtu);
|
||||||
|
|
||||||
// 3. Собираем движок, передавая объект конфигурации
|
|
||||||
let builder_result = EngineBuilder::new(config)
|
let builder_result = EngineBuilder::new(config)
|
||||||
.with_tun(tun_device)
|
.with_tun(tun_device)
|
||||||
.build()
|
.build()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// 4. Обрабатываем результат и запускаем цикл
|
// ==================================================
|
||||||
|
// 3. ГЛАВНЫЙ ЦИКЛ ENGINE
|
||||||
|
// ==================================================
|
||||||
match builder_result {
|
match builder_result {
|
||||||
Ok((mut engine, tun)) => {
|
Ok((mut engine, tun)) => {
|
||||||
info!("Engine starting process loop...");
|
info!("Engine starting process loop...");
|
||||||
|
|
||||||
let ctrl_c = tokio::signal::ctrl_c();
|
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_res = engine.run(tun) => {
|
_res = engine.run(tun) => {
|
||||||
info!("Engine loop finished");
|
info!("Engine loop finished");
|
||||||
},
|
},
|
||||||
_ = ctrl_c => {
|
_ = tokio::signal::ctrl_c() => {
|
||||||
info!("Ctrl+C received, shutting down...");
|
info!("Ctrl+C received, shutting down...");
|
||||||
|
// Отменяем токен, чтобы Network.run завершился
|
||||||
|
cancel_token.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to build VPN Engine: {}", e);
|
error!("Failed to build VPN Engine: {}", e);
|
||||||
|
cancel_token.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Очистка системных роутов
|
// ==================================================
|
||||||
|
// 4. ОЧИСТКА РОУТИНГА
|
||||||
|
// ==================================================
|
||||||
info!("Restoring system routing...");
|
info!("Restoring system routing...");
|
||||||
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
|
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
|
||||||
let p_ip = addr.ip().to_string();
|
let p_ip = addr.ip().to_string();
|
||||||
@@ -72,5 +111,8 @@ 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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,22 +263,35 @@ impl UdpConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(&mut self, socket: &mut udp::Socket) -> bool {
|
pub fn tick(&mut self, socket: &mut udp::Socket) -> bool {
|
||||||
// Проверка таймаута
|
|
||||||
if self.last_activity.elapsed() > UDP_TIMEOUT {
|
if self.last_activity.elapsed() > UDP_TIMEOUT {
|
||||||
debug!(%self.core.handle, "UDP Session closed due to {}s timeout", UDP_TIMEOUT.as_secs());
|
debug!(%self.core.handle, "UDP Session closed due to {}s timeout", UDP_TIMEOUT.as_secs());
|
||||||
socket.close();
|
socket.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Читаем из smoltcp и шлем в сеть (через Muxer)
|
|
||||||
if socket.can_recv() {
|
if socket.can_recv() {
|
||||||
|
let target_endpoint = socket.endpoint();
|
||||||
while let Ok((data, metadata)) = socket.recv() {
|
while let Ok((data, metadata)) = socket.recv() {
|
||||||
if self.client_endpoint.is_none() {
|
let source_endpoint = metadata.endpoint;
|
||||||
debug!(%self.core.handle, endpoint = %metadata.endpoint, "UDP Endpoint pinned");
|
|
||||||
}
|
if self.client_endpoint.is_none() {
|
||||||
self.client_endpoint = Some(metadata.endpoint);
|
info!(
|
||||||
|
%self.core.handle,
|
||||||
|
source = %source_endpoint,
|
||||||
|
target = %target_endpoint,
|
||||||
|
"UDP Session Established. Pinning endpoint."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.client_endpoint = Some(source_endpoint);
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
%self.core.handle,
|
||||||
|
source = %source_endpoint,
|
||||||
|
target = %target_endpoint,
|
||||||
|
bytes = data.len(),
|
||||||
|
"Forwarded UDP datagram from smoltcp to Muxer"
|
||||||
|
);
|
||||||
|
|
||||||
trace!(%self.core.handle, "Forwarded UDP datagram ({} bytes) from smoltcp to Muxer", data.len());
|
|
||||||
if self.core.tx.try_send(Bytes::copy_from_slice(data)).is_ok() {
|
if self.core.tx.try_send(Bytes::copy_from_slice(data)).is_ok() {
|
||||||
self.last_activity = Instant::now();
|
self.last_activity = Instant::now();
|
||||||
} else {
|
} else {
|
||||||
@@ -287,9 +300,8 @@ impl UdpConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Читаем из сети (от Muxer) и шлем в smoltcp
|
|
||||||
if socket.can_send() {
|
if socket.can_send() {
|
||||||
if let Some(endpoint) = self.client_endpoint {
|
if let Some(client_endpoint) = self.client_endpoint {
|
||||||
while let Ok(data) = self.core.rx.try_recv() {
|
while let Ok(data) = self.core.rx.try_recv() {
|
||||||
if data.is_empty() {
|
if data.is_empty() {
|
||||||
debug!(%self.core.handle, "Received empty datagram (Close signal) from Muxer, closing UDP socket");
|
debug!(%self.core.handle, "Received empty datagram (Close signal) from Muxer, closing UDP socket");
|
||||||
@@ -297,9 +309,16 @@ impl UdpConnection {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
match socket.send_slice(&data, endpoint) {
|
match socket.send_slice(&data, client_endpoint) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
trace!(%self.core.handle, "Wrote UDP datagram ({} bytes) to smoltcp", data.len());
|
let proxy_endpoint = socket.endpoint();
|
||||||
|
info!(
|
||||||
|
%self.core.handle,
|
||||||
|
source = %proxy_endpoint,
|
||||||
|
target = %client_endpoint,
|
||||||
|
bytes = data.len(),
|
||||||
|
"Wrote UDP reply from Muxer back to smoltcp"
|
||||||
|
);
|
||||||
self.last_activity = Instant::now();
|
self.last_activity = Instant::now();
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use netrunner_core::{
|
use netrunner_core::protocol::codec::socks::TargetAddress;
|
||||||
protocol::codec::{frame::FrameType, socks::TargetAddress},
|
|
||||||
proxy::connection::muxer::{MuxMessage, Muxer},
|
|
||||||
};
|
|
||||||
use netrunner_logger::{debug, error, info, trace, warn};
|
use netrunner_logger::{debug, error, info, trace, warn};
|
||||||
use smoltcp::{
|
use smoltcp::{
|
||||||
iface::{SocketHandle, SocketSet},
|
iface::{SocketHandle, SocketSet},
|
||||||
@@ -10,10 +7,10 @@ use smoltcp::{
|
|||||||
wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket},
|
wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket},
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, time::Duration, time::Instant as StdInstant};
|
use std::{collections::HashMap, time::Duration, time::Instant as StdInstant};
|
||||||
use tokio::sync::mpsc;
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
use tokio::net::{TcpStream, UdpSocket};
|
||||||
|
|
||||||
use crate::net::{
|
use crate::net::{
|
||||||
CHANNEL_CAPACITY,
|
|
||||||
connection::{TcpConnection, UdpConnection},
|
connection::{TcpConnection, UdpConnection},
|
||||||
dns::DnsHandler,
|
dns::DnsHandler,
|
||||||
ip_store::FakeIpStore,
|
ip_store::FakeIpStore,
|
||||||
@@ -193,15 +190,13 @@ impl SocketFactory {
|
|||||||
pub struct ConnectionManager {
|
pub struct ConnectionManager {
|
||||||
tracker: SessionTracker,
|
tracker: SessionTracker,
|
||||||
resolver: TargetResolver,
|
resolver: TargetResolver,
|
||||||
muxer: Muxer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionManager {
|
impl ConnectionManager {
|
||||||
pub fn new(dns_handler: DnsHandler, muxer: Muxer) -> Self {
|
pub fn new(dns_handler: DnsHandler) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tracker: SessionTracker::new(),
|
tracker: SessionTracker::new(),
|
||||||
resolver: TargetResolver::new(dns_handler),
|
resolver: TargetResolver::new(dns_handler),
|
||||||
muxer,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +239,6 @@ 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()) {
|
if let Ok(tcp_packet) = TcpPacket::new_checked(ip_packet.payload()) {
|
||||||
// Реагируем только на чистые SYN-пакеты (начало нового соединения)
|
|
||||||
if tcp_packet.syn() && !tcp_packet.ack() {
|
if tcp_packet.syn() && !tcp_packet.ack() {
|
||||||
let dst_port = tcp_packet.dst_port();
|
let dst_port = tcp_packet.dst_port();
|
||||||
trace!(%dst_addr, dst_port, "Received TCP SYN");
|
trace!(%dst_addr, dst_port, "Received TCP SYN");
|
||||||
@@ -354,45 +348,45 @@ impl ConnectionManager {
|
|||||||
let (conn, mut rx_from_smol, tx_to_smol, handshake_tx) = TcpConnection::new(handle);
|
let (conn, mut rx_from_smol, tx_to_smol, handshake_tx) = TcpConnection::new(handle);
|
||||||
self.tracker.active_tcp.insert(handle, conn);
|
self.tracker.active_tcp.insert(handle, conn);
|
||||||
|
|
||||||
let muxer = self.muxer.clone();
|
// Конвертируем TargetAddress в строку, понятную для tokio::net
|
||||||
let stream_id = muxer.next_id();
|
let target_str = match target {
|
||||||
let connect_payload = target.to_string();
|
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 {
|
tokio::spawn(async move {
|
||||||
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
|
let mut upstream = match TcpStream::connect(&target_str).await {
|
||||||
muxer.register_stream(stream_id, v_tx);
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
if muxer
|
error!("Failed to connect to upstream TCP {}: {}", target_str, e);
|
||||||
.send_to_netwrok(MuxMessage {
|
|
||||||
stream_id,
|
|
||||||
frame_type: FrameType::Connect,
|
|
||||||
data: Bytes::from(connect_payload),
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
muxer.remove_stream(stream_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match tokio::time::timeout(Duration::from_secs(10), v_rx.recv()).await {
|
|
||||||
Ok(Some(data)) if data.len() >= 2 && data[1] == 0x00 => {
|
|
||||||
let _ = handshake_tx.send(());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
muxer.remove_stream(stream_id);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
let to_proxy = async {
|
// Сообщаем соединению smoltcp, что мы готовы (рукопожатие выполнено)
|
||||||
|
let _ = handshake_tx.send(());
|
||||||
|
|
||||||
|
let (mut r, mut w) = upstream.into_split();
|
||||||
|
|
||||||
|
// Читаем из tun (smoltcp) и пишем во внешнюю сеть
|
||||||
|
let to_upstream = async {
|
||||||
while let Some(data) = rx_from_smol.recv().await {
|
while let Some(data) = rx_from_smol.recv().await {
|
||||||
if muxer
|
if w.write_all(&data).await.is_err() {
|
||||||
.send_to_netwrok(MuxMessage {
|
break;
|
||||||
stream_id,
|
}
|
||||||
frame_type: FrameType::Data,
|
}
|
||||||
data,
|
};
|
||||||
})
|
|
||||||
|
// Читаем из внешней сети и пишем в tun (smoltcp)
|
||||||
|
let from_upstream = async {
|
||||||
|
let mut buf = vec![0u8; 8192]; // Читаем чанками
|
||||||
|
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
|
.await
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
@@ -401,24 +395,7 @@ impl ConnectionManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let from_proxy = async {
|
tokio::select! { _ = to_upstream => {}, _ = from_upstream => {} }
|
||||||
while let Some(data) = v_rx.recv().await {
|
|
||||||
if data.is_empty() || tx_to_smol.send(data).await.is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tokio::select! { _ = to_proxy => {}, _ = from_proxy => {} }
|
|
||||||
|
|
||||||
let _ = muxer
|
|
||||||
.send_to_netwrok(MuxMessage {
|
|
||||||
stream_id,
|
|
||||||
frame_type: FrameType::Close,
|
|
||||||
data: Bytes::new(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
muxer.remove_stream(stream_id);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,30 +441,48 @@ impl ConnectionManager {
|
|||||||
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);
|
self.tracker.active_udp.insert(handle, conn);
|
||||||
|
|
||||||
let muxer = self.muxer.clone();
|
// Конвертируем для tokio::net::UdpSocket
|
||||||
let stream_id = muxer.next_id();
|
let target_str = match target {
|
||||||
let connect_payload = target.to_string();
|
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 {
|
tokio::spawn(async move {
|
||||||
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
|
// Создаем локальный UDP сокет со случайным портом
|
||||||
muxer.register_stream(stream_id, v_tx);
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let _ = muxer
|
// "Подключаем" UDP сокет к цели (включает фильтр пакетов и позволяет использовать обычные send/recv)
|
||||||
.send_to_netwrok(MuxMessage {
|
if let Err(e) = upstream.connect(&target_str).await {
|
||||||
stream_id,
|
error!("Failed to connect UDP to {}: {}", target_str, e);
|
||||||
frame_type: FrameType::UdpConnect,
|
return;
|
||||||
data: Bytes::from(connect_payload),
|
}
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let to_proxy = async {
|
let upstream = std::sync::Arc::new(upstream);
|
||||||
|
let upstream_rx = upstream.clone();
|
||||||
|
let upstream_tx = upstream;
|
||||||
|
|
||||||
|
// Из smoltcp наружу
|
||||||
|
let to_upstream = async {
|
||||||
while let Some(data) = rx_from_smol.recv().await {
|
while let Some(data) = rx_from_smol.recv().await {
|
||||||
if muxer
|
if upstream_tx.send(&data).await.is_err() {
|
||||||
.send_to_netwrok(MuxMessage {
|
break;
|
||||||
stream_id,
|
}
|
||||||
frame_type: FrameType::UdpData,
|
}
|
||||||
data,
|
};
|
||||||
})
|
|
||||||
|
// Извне в 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
|
.await
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
@@ -496,24 +491,7 @@ impl ConnectionManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let from_proxy = async {
|
tokio::select! { _ = to_upstream => {}, _ = from_upstream => {} }
|
||||||
while let Some(data) = v_rx.recv().await {
|
|
||||||
if data.is_empty() || tx_to_smol.send(data).await.is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tokio::select! { _ = to_proxy => {}, _ = from_proxy => {} }
|
|
||||||
|
|
||||||
let _ = muxer
|
|
||||||
.send_to_netwrok(MuxMessage {
|
|
||||||
stream_id,
|
|
||||||
frame_type: FrameType::Close,
|
|
||||||
data: Bytes::new(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
muxer.remove_stream(stream_id);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,19 +39,14 @@ pub struct Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
pub fn new(
|
pub fn new(config: Config, caps: DeviceCapabilities, dns_handler: DnsHandler) -> Self {
|
||||||
config: Config,
|
|
||||||
caps: DeviceCapabilities,
|
|
||||||
dns_handler: DnsHandler,
|
|
||||||
muxer: Muxer,
|
|
||||||
) -> 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, muxer);
|
let manager = ConnectionManager::new(dns_handler);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
interface,
|
interface,
|
||||||
@@ -322,7 +317,7 @@ impl EngineBuilder {
|
|||||||
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, muxer);
|
let mut engine = Engine::new(smol_config, caps, dns_handler);
|
||||||
|
|
||||||
engine.set_any_ip(self.config.any_ip);
|
engine.set_any_ip(self.config.any_ip);
|
||||||
|
|
||||||
|
|||||||
@@ -3,3 +3,5 @@ pub mod codec;
|
|||||||
pub mod frame;
|
pub mod frame;
|
||||||
mod padding;
|
mod padding;
|
||||||
pub mod socks;
|
pub mod socks;
|
||||||
|
|
||||||
|
pub const MAX_PADDING_SIZE: u32 = 255;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
|
use crate::protocol::codec::MAX_PADDING_SIZE;
|
||||||
|
|
||||||
pub struct Padding {
|
pub struct Padding {
|
||||||
pub len: u16,
|
pub len: u16,
|
||||||
pub data: Bytes,
|
pub data: Bytes,
|
||||||
@@ -10,7 +12,7 @@ impl Padding {
|
|||||||
pub fn generate_padding() -> Padding {
|
pub fn generate_padding() -> Padding {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
let random_u32: u32 = rng.next_u32();
|
let random_u32: u32 = rng.next_u32();
|
||||||
let padding_len: u16 = (random_u32 % 255) as u16;
|
let padding_len: u16 = (random_u32 % MAX_PADDING_SIZE) as u16;
|
||||||
let mut padding = vec![0u8; padding_len as usize];
|
let mut padding = vec![0u8; padding_len as usize];
|
||||||
rng.fill_bytes(&mut padding);
|
rng.fill_bytes(&mut padding);
|
||||||
Padding {
|
Padding {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ pub async fn run_tcp_bridge<R, W>(
|
|||||||
frame_type: FrameType::Data,
|
frame_type: FrameType::Data,
|
||||||
data: buf.split().freeze(),
|
data: buf.split().freeze(),
|
||||||
};
|
};
|
||||||
if muxer.send_to_netwrok(msg).await.is_err() { break; }
|
if muxer.send_to_network(msg).await.is_err() { break; }
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(stream_id, error = %e, "Socket read error");
|
error!(stream_id, error = %e, "Socket read error");
|
||||||
@@ -60,7 +60,7 @@ pub async fn run_tcp_bridge<R, W>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _ = muxer
|
let _ = muxer
|
||||||
.send_to_netwrok(MuxMessage {
|
.send_to_network(MuxMessage {
|
||||||
stream_id,
|
stream_id,
|
||||||
frame_type: FrameType::Close,
|
frame_type: FrameType::Close,
|
||||||
data: Bytes::new(),
|
data: Bytes::new(),
|
||||||
@@ -91,7 +91,7 @@ pub async fn run_udp_bridge(
|
|||||||
frame_type: FrameType::UdpData,
|
frame_type: FrameType::UdpData,
|
||||||
data: Bytes::copy_from_slice(&buf[..n]),
|
data: Bytes::copy_from_slice(&buf[..n]),
|
||||||
};
|
};
|
||||||
if muxer.send_to_netwrok(msg).await.is_err() { break; }
|
if muxer.send_to_network(msg).await.is_err() { break; }
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(stream_id, error = %e, "UDP socket read error");
|
error!(stream_id, error = %e, "UDP socket read error");
|
||||||
@@ -119,7 +119,7 @@ pub async fn run_udp_bridge(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _ = muxer
|
let _ = muxer
|
||||||
.send_to_netwrok(MuxMessage {
|
.send_to_network(MuxMessage {
|
||||||
stream_id,
|
stream_id,
|
||||||
frame_type: FrameType::Close,
|
frame_type: FrameType::Close,
|
||||||
data: Bytes::new(),
|
data: Bytes::new(),
|
||||||
|
|||||||
@@ -54,10 +54,46 @@ impl Muxer {
|
|||||||
self.id_gen.next()
|
self.id_gen.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_to_netwrok(&self, message: MuxMessage) -> Result<(), SendError<MuxMessage>> {
|
pub async fn send_to_network(&self, message: MuxMessage) -> Result<(), SendError<MuxMessage>> {
|
||||||
self.data_tx.send(message).await
|
self.data_tx.send(message).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn send_data_safe(&self, stream_id: u32, mut data: Bytes) -> Result<(), String> {
|
||||||
|
// Лимит полезной нагрузки, чтобы вместе с заголовком и паддингом
|
||||||
|
// пакет оставался в пределах ~1400-1450 байт.
|
||||||
|
const MAX_PAYLOAD_CHUNK: usize = 1300;
|
||||||
|
|
||||||
|
if data.len() <= MAX_PAYLOAD_CHUNK {
|
||||||
|
return self
|
||||||
|
.send_to_network(MuxMessage {
|
||||||
|
stream_id,
|
||||||
|
frame_type: FrameType::Data,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если данных много (например, те самые 4096 байт), режем их на куски
|
||||||
|
while !data.is_empty() {
|
||||||
|
let chunk_size = std::cmp::min(data.len(), MAX_PAYLOAD_CHUNK);
|
||||||
|
let chunk = data.split_to(chunk_size);
|
||||||
|
|
||||||
|
self.send_to_network(MuxMessage {
|
||||||
|
stream_id,
|
||||||
|
frame_type: FrameType::Data,
|
||||||
|
data: chunk,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
// Небольшая уступка планировщику, чтобы не забить канал мгновенно
|
||||||
|
tokio::task::yield_now().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn send_control(
|
pub async fn send_control(
|
||||||
&self,
|
&self,
|
||||||
stream_id: u32,
|
stream_id: u32,
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
use crate::proxy::connection::connection::{
|
use crate::{
|
||||||
ClientHandler, Connection, ConnectionRole, ServerHandler, TunnelHandler,
|
protocol::codec::{frame::FRAME_HEADER_SIZE, MAX_PADDING_SIZE},
|
||||||
|
proxy::connection::connection::{
|
||||||
|
ClientHandler, Connection, ConnectionRole, ServerHandler, TunnelHandler,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use netrunner_logger::{error, info};
|
use netrunner_logger::{error, info};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
@@ -86,3 +89,54 @@ impl Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const IP_UDP_OVERHEAD: usize = 28;
|
||||||
|
|
||||||
|
pub struct NetworkConfig {
|
||||||
|
pub mtu: usize,
|
||||||
|
pub max_wire_frame_size: usize,
|
||||||
|
pub safe_payload_size: usize,
|
||||||
|
pub tcp_rx_buffer_size: usize,
|
||||||
|
pub tcp_tx_buffer_size: usize,
|
||||||
|
pub udp_rx_buffer_size: usize,
|
||||||
|
pub udp_tx_buffer_size: usize,
|
||||||
|
pub channel_capacity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkConfig {
|
||||||
|
pub fn new(system_mtu: usize) -> Self {
|
||||||
|
let transport_overhead = 28; // IPv4 + UDP
|
||||||
|
|
||||||
|
let max_wire_frame = system_mtu.saturating_sub(transport_overhead);
|
||||||
|
|
||||||
|
let safe_payload = max_wire_frame
|
||||||
|
.saturating_sub(FRAME_HEADER_SIZE as usize)
|
||||||
|
.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 {
|
||||||
|
mtu: system_mtu,
|
||||||
|
max_wire_frame_size: max_wire_frame,
|
||||||
|
safe_payload_size: safe_payload,
|
||||||
|
tcp_rx_buffer_size: tcp_buffer,
|
||||||
|
tcp_tx_buffer_size: tcp_buffer,
|
||||||
|
udp_rx_buffer_size: udp_buffer,
|
||||||
|
udp_tx_buffer_size: udp_buffer,
|
||||||
|
channel_capacity: channel_cap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user