start remake local proxy

This commit is contained in:
2026-03-24 21:38:43 +07:00
parent 61e88559c8
commit 743c6bf05c
10 changed files with 310 additions and 152 deletions
+43 -13
View File
@@ -3,12 +3,14 @@ uniffi::setup_scaffolding!();
mod net;
mod tun;
pub use crate::tun::{routing, tun::Tun}; //for desktop test in main.rs
pub use crate::tun::{routing, tun::Tun};
use crate::{
net::engine::{EngineBuilder, EngineConfig},
tun::routing::reset_platform_routing,
};
use netrunner_core::proxy::connection::connection::ConnectionRole;
use netrunner_core::proxy::network::Network;
use netrunner_logger::{error, info};
use std::sync::{Arc, OnceLock};
use tokio::runtime::Runtime;
@@ -16,7 +18,6 @@ use tokio_util::sync::CancellationToken;
pub static RUNTIME: OnceLock<Runtime> = OnceLock::new();
// Инициализация Tokio Runtime
fn get_runtime() -> &'static Runtime {
RUNTIME.get_or_init(|| {
tokio::runtime::Builder::new_multi_thread()
@@ -54,7 +55,7 @@ impl Drop for Session {
}
// ==========================================
// SESSION MANAGER (Публичный API UniFFI)
// SESSION MANAGER
// ==========================================
#[derive(uniffi::Object)]
@@ -75,14 +76,10 @@ impl SessionManager {
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
let remote_proxy_ip = addr.ip().to_string();
// 1. Создаем базовый конфиг
let mut config = EngineConfig::new(&remote_address).with_cache_path(&cache_dir);
// 2. Тонкая настройка под платформу
#[cfg(any(target_os = "android", target_os = "ios"))]
{
// На мобилках роутингом обычно управляет VpnService (Android) или NEPacketTunnelProvider (iOS)
// Поэтому отключаем попытки движка менять системные таблицы роутинга напрямую
config = config.disable_routing().with_mtu(1280);
}
@@ -91,10 +88,44 @@ impl SessionManager {
config = config.with_mtu(1350);
}
runtime.spawn(async move {
info!("Starting VPN session thread...");
// --- 1. ЗАПУСК ЛОКАЛЬНОГО ПРОКСИ (NETWORK) ---
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 = {
#[cfg(any(target_os = "android", target_os = "ios"))]
{
@@ -108,7 +139,7 @@ impl SessionManager {
.tun_name("netr0")
.address((10, 0, 0, 1))
.netmask((255, 255, 255, 0))
.mtu(config.mtu as u16) // Используем MTU из нашего конфига
.mtu(config.mtu as u16)
.up();
})
.expect("Failed to init TUN")
@@ -122,7 +153,6 @@ impl SessionManager {
}
};
// 4. Используем обновленный билдер
let builder_result = EngineBuilder::new(config)
.with_tun(tun_device)
.build()
@@ -135,7 +165,7 @@ impl SessionManager {
res = engine.run(tun) => {
info!("Engine loop finished: {:?}", res);
},
_ = cancel_token.cancelled() => {
_ = engine_token.cancelled() => {
info!("Engine task shutting down via token");
}
}
+56 -14
View File
@@ -2,25 +2,60 @@ use netrunner_logger::{error, info};
mod net;
mod tun;
// Импортируем и Билдер, и Конфиг
// Импортируем Билдер, Конфиг и модули маршрутизации
use crate::tun::{routing::reset_platform_routing, tun::Tun};
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]
async fn main() -> anyhow::Result<()> {
netrunner_logger::Logger::init();
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 config = EngineConfig::new(remote_address)
// Токен для управления жизненным циклом фоновых задач
let cancel_token = CancellationToken::new();
// ==================================================
// 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_mtu(1350); // Указываем MTU здесь, чтобы использовать его и для TUN, и для стека
.with_mtu(1350);
// 2. Создаем TUN интерфейс
// Используем значение MTU из конфига, чтобы данные были синхронизированы
let tun_device = Tun::create(|tun_cfg| {
tun_cfg
.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);
// 3. Собираем движок, передавая объект конфигурации
let builder_result = EngineBuilder::new(config)
.with_tun(tun_device)
.build()
.await;
// 4. Обрабатываем результат и запускаем цикл
// ==================================================
// 3. ГЛАВНЫЙ ЦИКЛ ENGINE
// ==================================================
match builder_result {
Ok((mut engine, tun)) => {
info!("Engine starting process loop...");
let ctrl_c = tokio::signal::ctrl_c();
tokio::select! {
_res = engine.run(tun) => {
info!("Engine loop finished");
},
_ = ctrl_c => {
_ = tokio::signal::ctrl_c() => {
info!("Ctrl+C received, shutting down...");
// Отменяем токен, чтобы Network.run завершился
cancel_token.cancel();
}
}
}
Err(e) => {
error!("Failed to build VPN Engine: {}", e);
cancel_token.cancel();
}
}
// 5. Очистка системных роутов
// ==================================================
// 4. ОЧИСТКА РОУТИНГА
// ==================================================
info!("Restoring system routing...");
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
let p_ip = addr.ip().to_string();
@@ -72,5 +111,8 @@ async fn main() -> anyhow::Result<()> {
info!("System routing restored successfully.");
}
// Даем таске Network время на graceful shutdown (чтобы сокеты успели закрыться)
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
Ok(())
}
+30 -11
View File
@@ -263,22 +263,35 @@ impl UdpConnection {
}
pub fn tick(&mut self, socket: &mut udp::Socket) -> bool {
// Проверка таймаута
if self.last_activity.elapsed() > UDP_TIMEOUT {
debug!(%self.core.handle, "UDP Session closed due to {}s timeout", UDP_TIMEOUT.as_secs());
socket.close();
return false;
}
// Читаем из smoltcp и шлем в сеть (через Muxer)
if socket.can_recv() {
let target_endpoint = socket.endpoint();
while let Ok((data, metadata)) = socket.recv() {
if self.client_endpoint.is_none() {
debug!(%self.core.handle, endpoint = %metadata.endpoint, "UDP Endpoint pinned");
}
self.client_endpoint = Some(metadata.endpoint);
let source_endpoint = metadata.endpoint;
if self.client_endpoint.is_none() {
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() {
self.last_activity = Instant::now();
} else {
@@ -287,9 +300,8 @@ impl UdpConnection {
}
}
// Читаем из сети (от Muxer) и шлем в smoltcp
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() {
if data.is_empty() {
debug!(%self.core.handle, "Received empty datagram (Close signal) from Muxer, closing UDP socket");
@@ -297,9 +309,16 @@ impl UdpConnection {
return false;
}
match socket.send_slice(&data, endpoint) {
match socket.send_slice(&data, client_endpoint) {
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();
}
Err(e) => {
+76 -98
View File
@@ -1,8 +1,5 @@
use bytes::Bytes;
use netrunner_core::{
protocol::codec::{frame::FrameType, socks::TargetAddress},
proxy::connection::muxer::{MuxMessage, Muxer},
};
use netrunner_core::protocol::codec::socks::TargetAddress;
use netrunner_logger::{debug, error, info, trace, warn};
use smoltcp::{
iface::{SocketHandle, SocketSet},
@@ -10,10 +7,10 @@ use smoltcp::{
wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket},
};
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::{
CHANNEL_CAPACITY,
connection::{TcpConnection, UdpConnection},
dns::DnsHandler,
ip_store::FakeIpStore,
@@ -193,15 +190,13 @@ impl SocketFactory {
pub struct ConnectionManager {
tracker: SessionTracker,
resolver: TargetResolver,
muxer: Muxer,
}
impl ConnectionManager {
pub fn new(dns_handler: DnsHandler, muxer: Muxer) -> Self {
pub fn new(dns_handler: DnsHandler) -> Self {
Self {
tracker: SessionTracker::new(),
resolver: TargetResolver::new(dns_handler),
muxer,
}
}
@@ -244,7 +239,6 @@ impl ConnectionManager {
match ip_packet.next_header() {
IpProtocol::Tcp => {
if let Ok(tcp_packet) = TcpPacket::new_checked(ip_packet.payload()) {
// Реагируем только на чистые SYN-пакеты (начало нового соединения)
if tcp_packet.syn() && !tcp_packet.ack() {
let dst_port = tcp_packet.dst_port();
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);
self.tracker.active_tcp.insert(handle, conn);
let muxer = self.muxer.clone();
let stream_id = muxer.next_id();
let connect_payload = target.to_string();
// Конвертируем 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 {
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
muxer.register_stream(stream_id, v_tx);
if muxer
.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);
let mut upstream = match TcpStream::connect(&target_str).await {
Ok(s) => s,
Err(e) => {
error!("Failed to connect to upstream TCP {}: {}", target_str, e);
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 {
if muxer
.send_to_netwrok(MuxMessage {
stream_id,
frame_type: FrameType::Data,
data,
})
if w.write_all(&data).await.is_err() {
break;
}
}
};
// Читаем из внешней сети и пишем в 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
.is_err()
{
@@ -401,24 +395,7 @@ impl ConnectionManager {
}
};
let from_proxy = async {
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);
tokio::select! { _ = to_upstream => {}, _ = from_upstream => {} }
});
}
@@ -464,30 +441,48 @@ impl ConnectionManager {
let (conn, mut rx_from_smol, tx_to_smol) = UdpConnection::new(handle);
self.tracker.active_udp.insert(handle, conn);
let muxer = self.muxer.clone();
let stream_id = muxer.next_id();
let connect_payload = target.to_string();
// Конвертируем для tokio::net::UdpSocket
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 {
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
muxer.register_stream(stream_id, v_tx);
// Создаем локальный 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;
}
};
let _ = muxer
.send_to_netwrok(MuxMessage {
stream_id,
frame_type: FrameType::UdpConnect,
data: Bytes::from(connect_payload),
})
.await;
// "Подключаем" UDP сокет к цели (включает фильтр пакетов и позволяет использовать обычные send/recv)
if let Err(e) = upstream.connect(&target_str).await {
error!("Failed to connect UDP to {}: {}", target_str, e);
return;
}
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 {
if muxer
.send_to_netwrok(MuxMessage {
stream_id,
frame_type: FrameType::UdpData,
data,
})
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()
{
@@ -496,24 +491,7 @@ impl ConnectionManager {
}
};
let from_proxy = async {
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);
tokio::select! { _ = to_upstream => {}, _ = from_upstream => {} }
});
}
+3 -8
View File
@@ -39,19 +39,14 @@ pub struct Engine {
}
impl Engine {
pub fn new(
config: Config,
caps: DeviceCapabilities,
dns_handler: DnsHandler,
muxer: Muxer,
) -> Self {
pub fn new(config: Config, caps: DeviceCapabilities, dns_handler: DnsHandler) -> Self {
let now = Engine::current_time();
let (mut device, to_smoltcp_tx, from_smoltcp_rx, avail) = VirtTunDevice::new(caps);
let interface = Interface::new(config, &mut device, now);
let socket_set = ConnectionManager::setup_sockets(2);
let manager = ConnectionManager::new(dns_handler, muxer);
let manager = ConnectionManager::new(dns_handler);
Self {
interface,
@@ -322,7 +317,7 @@ impl EngineBuilder {
info!("Secure tunnel established, Muxer is ready.");
// 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);