structure refactoring

This commit is contained in:
2026-03-24 15:32:52 +07:00
parent 7603d2c92e
commit fc93665fb2
14 changed files with 401 additions and 332 deletions
-77
View File
@@ -1,77 +0,0 @@
use smoltcp::iface::SocketHandle;
use smoltcp::socket::udp;
use smoltcp::wire::IpEndpoint;
use std::time::{Duration, Instant};
use tokio::sync::mpsc;
use crate::connections::CHANNEL_CAPACITY;
use bytes::Bytes;
pub struct UdpConnection {
pub handle: SocketHandle,
tx: mpsc::Sender<Bytes>,
rx: mpsc::Receiver<Bytes>,
client_endpoint: Option<IpEndpoint>,
last_activity: Instant,
}
const UDP_TIMEOUT: Duration = Duration::from_secs(60);
impl UdpConnection {
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) {
let (tx_to_net, rx_from_smol) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
let conn = Self {
handle,
tx: tx_to_net,
rx: rx_from_net,
client_endpoint: None,
last_activity: Instant::now(),
};
(conn, rx_from_smol, tx_to_smol)
}
pub fn tick(&mut self, socket: &mut udp::Socket) -> bool {
if self.last_activity.elapsed() > UDP_TIMEOUT {
netrunner_logger::debug!(%self.handle, "UDP Session closed due to timeout");
socket.close();
return false;
}
if socket.can_recv() {
while let Ok((data, metadata)) = socket.recv() {
self.client_endpoint = Some(metadata.endpoint);
if self.tx.try_send(Bytes::copy_from_slice(data)).is_ok() {
self.last_activity = Instant::now();
}
}
}
if socket.can_send() {
if let Some(endpoint) = self.client_endpoint {
while let Ok(data) = self.rx.try_recv() {
if data.is_empty() {
socket.close();
return false;
}
match socket.send_slice(&data, endpoint) {
Ok(_) => {
self.last_activity = Instant::now();
}
Err(_) => {
break;
}
}
}
}
}
true
}
}
+148 -4
View File
@@ -1,10 +1,154 @@
use uniffi;
uniffi::setup_scaffolding!();
pub mod connections;
pub mod session;
pub mod tun;
mod net;
mod tun;
pub use crate::tun::{routing, tun::Tun}; //for desktop test in main.rs
use std::sync::OnceLock;
use crate::{
net::engine::{EngineBuilder, EngineConfig},
tun::routing::reset_platform_routing,
};
use netrunner_logger::{error, info};
use std::sync::{Arc, OnceLock};
use tokio::runtime::Runtime;
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()
.enable_all()
.build()
.expect("Failed to create tokio runtime")
})
}
// ==========================================
// SESSION
// ==========================================
#[derive(uniffi::Object)]
pub struct Session {
pub(crate) cancel_token: CancellationToken,
pub(crate) proxy_ip: String,
}
#[uniffi::export]
impl Session {
pub fn stop(&self) {
info!("Stopping session...");
self.cancel_token.cancel();
let _ = reset_platform_routing(Some(&self.proxy_ip));
}
}
impl Drop for Session {
fn drop(&mut self) {
info!("Session dropped, stopping all tasks...");
self.cancel_token.cancel();
let _ = reset_platform_routing(Some(&self.proxy_ip));
}
}
// ==========================================
// SESSION MANAGER (Публичный API UniFFI)
// ==========================================
#[derive(uniffi::Object)]
pub struct SessionManager;
#[uniffi::export]
impl SessionManager {
pub(crate) fn spawn_session(
&self,
remote_address: String,
tun_fd: Option<i32>,
cache_dir: String,
) -> Arc<Session> {
let runtime = get_runtime();
let cancel_token = CancellationToken::new();
let session_token = cancel_token.clone();
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);
}
#[cfg(target_os = "linux")]
{
config = config.with_mtu(1350);
}
runtime.spawn(async move {
info!("Starting VPN session thread...");
// 3. Инициализация TUN устройства (специфично для платформ)
let tun_device = {
#[cfg(any(target_os = "android", target_os = "ios"))]
{
Tun::from_fd(tun_fd.expect("TUN FD required on mobile"))
.expect("Failed to init TUN from FD")
}
#[cfg(target_os = "linux")]
{
Tun::create(|tun_cfg| {
tun_cfg
.tun_name("netr0")
.address((10, 0, 0, 1))
.netmask((255, 255, 255, 0))
.mtu(config.mtu as u16) // Используем MTU из нашего конфига
.up();
})
.expect("Failed to init TUN")
}
#[cfg(target_os = "windows")]
{
Tun::create(|tun_cfg| {
tun_cfg.tun_name("netr0");
})
.expect("Failed to init TUN")
}
};
// 4. Используем обновленный билдер
let builder_result = EngineBuilder::new(config)
.with_tun(tun_device)
.build()
.await;
match builder_result {
Ok((mut engine, tun)) => {
info!("Engine async task started");
tokio::select! {
res = engine.run(tun) => {
info!("Engine loop finished: {:?}", res);
},
_ = cancel_token.cancelled() => {
info!("Engine task shutting down via token");
}
}
}
Err(e) => {
error!("Failed to build VPN Engine: {}", e);
}
}
});
Arc::new(Session {
cancel_token: session_token,
proxy_ip: remote_proxy_ip,
})
}
}
+24 -12
View File
@@ -1,5 +1,10 @@
use netrunner_client::tun::{engine::EngineBuilder, routing::reset_platform_routing, tun::Tun};
use netrunner_logger::{error, info};
mod net;
mod tun;
// Импортируем и Билдер, и Конфиг
use crate::tun::{routing::reset_platform_routing, tun::Tun};
use net::engine::{EngineBuilder, EngineConfig};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
@@ -8,27 +13,34 @@ async fn main() -> anyhow::Result<()> {
let remote_address = "147.45.43.70:443";
// 1. Создаем TUN интерфейс (зависит от платформы, поэтому делаем тут)
let tun_device = Tun::create(|config| {
config
// 1. Создаем конфигурацию движка
// Здесь мы можем гибко настроить параметры, которые раньше были захардкожены
let config = EngineConfig::new(remote_address)
.with_cache_path(".")
.with_mtu(1350); // Указываем MTU здесь, чтобы использовать его и для TUN, и для стека
// 2. Создаем TUN интерфейс
// Используем значение MTU из конфига, чтобы данные были синхронизированы
let tun_device = Tun::create(|tun_cfg| {
tun_cfg
.tun_name("netr0")
.address((10, 0, 0, 1))
.netmask((255, 255, 255, 0))
.destination((10, 0, 0, 2))
.mtu(config.mtu as u16)
.up();
})
.expect("Failed to initialize TUN device");
info!("TUN interface is UP: 10.0.0.1/24");
info!("TUN interface is UP: 10.0.0.1/24 (MTU: {})", config.mtu);
// 2. Собираем движок через наш новый Builder
let builder_result = EngineBuilder::new(remote_address)
.with_cache_path(".")
// 3. Собираем движок, передавая объект конфигурации
let builder_result = EngineBuilder::new(config)
.with_tun(tun_device)
.build()
.await;
// 3. Обрабатываем результат и запускаем цикл
// 4. Обрабатываем результат и запускаем цикл
match builder_result {
Ok((mut engine, tun)) => {
info!("Engine starting process loop...");
@@ -36,8 +48,8 @@ async fn main() -> anyhow::Result<()> {
let ctrl_c = tokio::signal::ctrl_c();
tokio::select! {
res = engine.run(tun) => {
error!("Engine loop error: {:?}", res);
_res = engine.run(tun) => {
info!("Engine loop finished");
},
_ = ctrl_c => {
info!("Ctrl+C received, shutting down...");
@@ -49,7 +61,7 @@ async fn main() -> anyhow::Result<()> {
}
}
// 4. Очистка системных роутов при любом сценарии выхода (ошибка или Ctrl+C)
// 5. Очистка системных роутов
info!("Restoring system routing...");
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
let p_ip = addr.ip().to_string();
@@ -1,9 +1,46 @@
use crate::net::CHANNEL_CAPACITY;
use bytes::{Buf, Bytes, BytesMut};
use smoltcp::iface::SocketHandle;
use smoltcp::socket::tcp;
use tokio::sync::{mpsc, oneshot};
use smoltcp::{
iface::SocketHandle,
socket::{tcp, udp},
wire::IpEndpoint,
};
use std::time::Duration;
use tokio::{
sync::{mpsc, oneshot},
time::Instant,
};
use crate::connections::CHANNEL_CAPACITY;
// ============================================================================
// 1. БАЗОВАЯ СТРУКТУРА (ConnectionCore)
// ============================================================================
/// Фундамент для любого соединения.
/// Инициализирует и хранит каналы связи между smoltcp и Muxer'ом.
pub struct ConnectionCore {
pub handle: SocketHandle,
pub tx: mpsc::Sender<Bytes>,
pub rx: mpsc::Receiver<Bytes>,
}
impl ConnectionCore {
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) {
let (tx_to_net, rx_from_smol) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
let core = Self {
handle,
tx: tx_to_net,
rx: rx_from_net,
};
(core, rx_from_smol, tx_to_smol)
}
}
// ============================================================================
// 2. TCP СОЕДИНЕНИЕ (TcpConnection)
// ============================================================================
pub enum ConnectionState {
Established,
@@ -12,18 +49,16 @@ pub enum ConnectionState {
Closed,
}
const MAX_PENDING: usize = 64 * 1024;
const TCP_CHUNK_SIZE: usize = 1024 * 16;
pub struct TcpConnection {
pub handle: SocketHandle,
core: ConnectionCore,
state: ConnectionState,
tx: mpsc::Sender<Bytes>,
rx: mpsc::Receiver<Bytes>,
pending_data: BytesMut,
handshake_rx: Option<oneshot::Receiver<()>>,
}
const MAX_PENDING: usize = 64 * 1024;
const TCP_CHUNK_SIZE: usize = 1024 * 16;
impl TcpConnection {
pub fn new(
handle: SocketHandle,
@@ -33,15 +68,12 @@ impl TcpConnection {
mpsc::Sender<Bytes>,
oneshot::Sender<()>,
) {
let (tx_to_net, rx_from_smol) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(CHANNEL_CAPACITY);
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle);
let (handshake_tx, handshake_rx) = oneshot::channel();
let conn = Self {
handle,
core,
state: ConnectionState::Handshaking,
tx: tx_to_net,
rx: rx_from_net,
pending_data: BytesMut::new(),
handshake_rx: Some(handshake_rx),
};
@@ -49,6 +81,14 @@ impl TcpConnection {
(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 _is_active(&self) -> bool {
matches!(self.state, ConnectionState::Active)
}
pub fn tick(&mut self, socket: &mut tcp::Socket) -> bool {
let state = socket.state();
@@ -87,28 +127,21 @@ impl TcpConnection {
return false;
}
}
ConnectionState::Closed => {
return false;
}
_ => {}
}
true
}
pub fn is_finished(&self, socket: &tcp::Socket) -> bool {
use tcp::State;
matches!(socket.state(), State::Closed | State::TimeWait)
}
pub fn is_active(&self) -> bool {
matches!(self.state, ConnectionState::Active)
}
fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
// 1. Вычитываем данные из smoltcp и шлем в Muxer
while socket.can_recv() {
let mut full = false;
let mut temp = [0u8; TCP_CHUNK_SIZE];
if let Ok(n) = socket.peek_slice(&mut temp) {
@@ -117,7 +150,7 @@ impl TcpConnection {
}
let chunk = Bytes::copy_from_slice(&temp[..n]);
match self.tx.try_send(chunk) {
match self.core.tx.try_send(chunk) {
Ok(_) => {
socket.recv_slice(&mut temp[..n]).unwrap();
}
@@ -138,31 +171,31 @@ impl TcpConnection {
}
}
// 2. Читаем данные из Muxer'а с учетом Backpressure
let current_pending = self.pending_data.len();
let fill_ratio = (current_pending as f32 / MAX_PENDING as f32) * 100.0;
if current_pending >= MAX_PENDING {
netrunner_logger::warn!(
%self.handle,
%self.core.handle,
"Backpressure ACTIVE: Buffer is FULL ({} bytes). Stalling RX channel.",
current_pending
);
} else if fill_ratio > 80.0 {
netrunner_logger::info!(
%self.handle,
%self.core.handle,
"Bufferbloat Warning: Buffer {:.1}% full ({} bytes). Latency increasing.",
fill_ratio, current_pending
);
while let Ok(data) = self.rx.try_recv() {
while let Ok(data) = self.core.rx.try_recv() {
self.pending_data.extend_from_slice(&data);
if self.pending_data.len() >= MAX_PENDING {
break;
}
}
} else {
while let Ok(data) = self.rx.try_recv() {
while let Ok(data) = self.core.rx.try_recv() {
self.pending_data.extend_from_slice(&data);
if self.pending_data.len() >= MAX_PENDING {
break;
@@ -170,19 +203,90 @@ impl TcpConnection {
}
}
// 3. Отправляем буферизированные данные в smoltcp
if !self.pending_data.is_empty() && socket.can_send() {
match socket.send_slice(&self.pending_data) {
Ok(n) => {
self.pending_data.advance(n);
if n > 0 && self.pending_data.len() < (MAX_PENDING / 2) && fill_ratio > 90.0 {
netrunner_logger::info!(%self.handle, "Backpressure RELIEVED: Buffer drained to {} bytes", self.pending_data.len());
netrunner_logger::info!(
%self.core.handle,
"Backpressure RELIEVED: Buffer drained to {} bytes",
self.pending_data.len()
);
}
}
Err(e) => {
netrunner_logger::debug!(%self.handle, "Smoltcp socket send error: {:?}", e);
netrunner_logger::debug!(%self.core.handle, "Smoltcp socket send error: {:?}", e);
}
}
}
}
}
// ============================================================================
// 3. UDP СОЕДИНЕНИЕ (UdpConnection)
// ============================================================================
const UDP_TIMEOUT: Duration = Duration::from_secs(60);
pub struct UdpConnection {
core: ConnectionCore,
client_endpoint: Option<IpEndpoint>,
last_activity: Instant,
}
impl UdpConnection {
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) {
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle);
let conn = Self {
core,
client_endpoint: None,
last_activity: Instant::now(),
};
(conn, rx_from_smol, tx_to_smol)
}
pub fn tick(&mut self, socket: &mut udp::Socket) -> bool {
// Проверка таймаута
if self.last_activity.elapsed() > UDP_TIMEOUT {
netrunner_logger::debug!(%self.core.handle, "UDP Session closed due to timeout");
socket.close();
return false;
}
// Читаем из smoltcp и шлем в сеть
if socket.can_recv() {
while let Ok((data, metadata)) = socket.recv() {
self.client_endpoint = Some(metadata.endpoint);
if self.core.tx.try_send(Bytes::copy_from_slice(data)).is_ok() {
self.last_activity = Instant::now();
}
}
}
// Читаем из сети и шлем в smoltcp
if socket.can_send() {
if let Some(endpoint) = self.client_endpoint {
while let Ok(data) = self.core.rx.try_recv() {
if data.is_empty() {
socket.close();
return false;
}
if socket.send_slice(&data, endpoint).is_ok() {
self.last_activity = Instant::now();
} else {
break;
}
}
}
}
true
}
}
@@ -3,7 +3,7 @@ use netrunner_core::{
protocol::codec::{frame::FrameType, socks::TargetAddress},
proxy::connection::muxer::{MuxMessage, Muxer},
};
use netrunner_logger::{debug, error, info, warn};
use netrunner_logger::{debug, info, warn};
use smoltcp::{
iface::{SocketHandle, SocketSet},
socket::{AnySocket, icmp, tcp, udp},
@@ -12,9 +12,11 @@ use smoltcp::{
use std::{collections::HashMap, time::Duration, time::Instant as StdInstant};
use tokio::sync::mpsc;
use crate::connections::{
CHANNEL_CAPACITY, dns::DnsHandler, ip_store::FakeIpStore, tcp_connection::TcpConnection,
udp_connection::UdpConnection,
use crate::net::{
CHANNEL_CAPACITY,
connection::{TcpConnection, UdpConnection},
dns::DnsHandler,
ip_store::FakeIpStore,
};
// ============================================================================
@@ -1,4 +1,4 @@
use crate::connections::ip_store::FakeIpStore;
use crate::net::ip_store::FakeIpStore;
use hickory_proto::op::{Message, MessageType, ResponseCode};
use hickory_proto::rr::{RData, Record, RecordType};
use netrunner_logger::{debug, error, info};
@@ -20,8 +20,8 @@ use tun::{DeviceReader, DeviceWriter};
use netrunner_logger::{debug, error, info, warn};
use crate::connections::dns::DnsHandler;
use crate::tun::connection_manager::ConnectionManager;
use crate::net::connection_manager::ConnectionManager;
use crate::net::dns::DnsHandler;
use crate::tun::device::{TokenBuffer, VirtTunDevice};
use crate::tun::routing::setup_platform_routing;
use crate::tun::tun::Tun;
@@ -216,18 +216,31 @@ impl Engine {
}
}
pub struct EngineBuilder {
remote_address: String,
cache_path: String,
tun_device: Option<Tun>,
// ============================================================================
// КОНФИГУРАЦИЯ ДВИЖКА
// ============================================================================
#[derive(Clone, Debug)]
pub struct EngineConfig {
pub remote_address: String,
pub cache_path: String,
pub mtu: usize,
pub setup_routing: bool,
pub any_ip: bool,
pub transparent_mode: bool,
pub default_gateway: Ipv4Addr,
}
impl EngineBuilder {
impl EngineConfig {
pub fn new(remote_address: impl Into<String>) -> Self {
Self {
remote_address: remote_address.into(),
cache_path: ".".to_string(),
tun_device: None,
mtu: 1350,
setup_routing: true,
any_ip: true,
transparent_mode: true,
default_gateway: Ipv4Addr::new(10, 0, 0, 2),
}
}
@@ -236,6 +249,36 @@ impl EngineBuilder {
self
}
pub fn with_mtu(mut self, mtu: usize) -> Self {
self.mtu = mtu;
self
}
pub fn _disable_routing(mut self) -> Self {
self.setup_routing = false;
self
}
}
// ============================================================================
// БИЛДЕР ДВИЖКА
// ============================================================================
pub struct EngineBuilder {
config: EngineConfig,
tun_device: Option<Tun>,
}
impl EngineBuilder {
/// Инициализируем билдер на основе готового конфига
pub fn new(config: EngineConfig) -> Self {
Self {
config,
tun_device: None,
}
}
/// Передаем TUN интерфейс (зависит от платформы, поэтому не в конфиге)
pub fn with_tun(mut self, tun: Tun) -> Self {
self.tun_device = Some(tun);
self
@@ -244,35 +287,53 @@ impl EngineBuilder {
pub async fn build(self) -> Result<(Engine, Tun), String> {
let tun = self.tun_device.ok_or("TUN device is required")?;
info!("Initializing Engine components...");
info!(
"Initializing Engine components with config: {:?}",
self.config
);
let mut dns_handler = DnsHandler::new(&self.cache_path);
// 1. Инициализация DNS
let mut dns_handler = DnsHandler::new(&self.config.cache_path);
if let Err(e) = dns_handler.init().await {
error!("Failed to initialize DNS blocklist: {}", e);
}
if let Err(e) = setup_platform_routing(&self.remote_address) {
return Err(format!("Routing setup failed: {}", e));
// 2. Настройка системного роутинга (Опционально)
if self.config.setup_routing {
info!("Applying platform routing rules...");
if let Err(e) = setup_platform_routing(&self.config.remote_address) {
return Err(format!("Routing setup failed: {}", e));
}
} else {
info!("Platform routing setup skipped via config.");
}
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
// 3. Конфигурация интерфейса smoltcp
let smol_config = Config::new(smoltcp::wire::HardwareAddress::Ip);
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1350;
caps.max_transmission_unit = self.config.mtu; // Берем из конфига
caps.medium = smoltcp::phy::Medium::Ip;
// 4. Подключение к серверу
info!("Establishing secure tunnel to proxy server...");
let muxer = ClientHandler::connect(&self.remote_address)
let muxer = ClientHandler::connect(&self.config.remote_address)
.await
.map_err(|e| format!("Failed to establish secure tunnel: {}", e))?;
info!("Secure tunnel established, Muxer is ready.");
let mut engine = Engine::new(config, caps, dns_handler, muxer);
engine.set_any_ip(true);
engine.set_transparent_mode();
engine.set_default_gateway(Ipv4Addr::new(10, 0, 0, 2));
// 5. Инициализация и настройка Engine
let mut engine = Engine::new(smol_config, caps, dns_handler, muxer);
engine.set_any_ip(self.config.any_ip);
if self.config.transparent_mode {
engine.set_transparent_mode();
}
engine.set_default_gateway(self.config.default_gateway);
engine.activate();
info!("Stack IP initialized: 10.0.0.2");
info!("Stack IP initialized: {}", self.config.default_gateway);
Ok((engine, tun))
}
@@ -5,7 +5,7 @@ use std::num::NonZeroUsize;
pub struct FakeIpStore {
cache: LruCache<String, Ipv4Addr>,
pub rev_cache: LruCache<Ipv4Addr, String>,
rev_cache: LruCache<Ipv4Addr, String>,
next_ip: u32,
}
@@ -1,6 +1,7 @@
mod connection;
pub mod connection_manager;
pub mod dns;
pub mod engine;
pub mod ip_store;
pub mod tcp_connection;
pub mod udp_connection;
pub const CHANNEL_CAPACITY: usize = 16;
-8
View File
@@ -1,8 +0,0 @@
use super::{Session, SessionManager};
use std::sync::Arc;
impl SessionManager {
pub fn start_desktop(&self, remote_address: String) -> Arc<Session> {
self.spawn_session(remote_address)
}
}
-19
View File
@@ -1,19 +0,0 @@
use super::{Session, SessionManager};
use std::sync::Arc;
use uniffi;
#[uniffi::export]
impl SessionManager {
pub fn start_mobile(
&self,
remote_address: String,
tun_fd: i32,
cache_dir: String,
) -> Arc<Session> {
self.spawn_session(remote_address, Some(tun_fd), cache_dir)
}
pub fn start_desktop(&self, remote_address: String, cache_dir: String) -> Arc<Session> {
self.spawn_session(remote_address, None, cache_dir)
}
}
-145
View File
@@ -1,145 +0,0 @@
use crate::{
RUNTIME,
tun::{engine::EngineBuilder, routing::reset_platform_routing, tun::Tun},
};
use netrunner_logger::{error, info};
use std::sync::Arc;
use tokio::runtime::Runtime;
use tokio_util::sync::CancellationToken;
#[cfg(any(target_os = "linux", target_os = "windows"))]
pub mod desktop;
#[cfg(any(target_os = "android", target_os = "ios"))]
pub mod mobile;
fn get_runtime() -> &'static Runtime {
RUNTIME.get_or_init(|| {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed to create tokio runtime")
})
}
#[derive(uniffi::Object)]
pub struct Session {
pub(crate) cancel_token: CancellationToken,
pub(crate) proxy_ip: String,
}
#[uniffi::export]
impl Session {
pub fn stop(&self) {
info!("Stopping session...");
self.cancel_token.cancel();
let _ = reset_platform_routing(Some(&self.proxy_ip));
}
}
impl Drop for Session {
fn drop(&mut self) {
info!("Session dropped, stopping all tasks...");
self.cancel_token.cancel();
let _ = reset_platform_routing(Some(&self.proxy_ip));
}
}
#[derive(uniffi::Object)]
pub struct SessionManager;
#[uniffi::export]
impl SessionManager {
#[uniffi::constructor]
pub fn new() -> Self {
netrunner_logger::Logger::init();
info!("SessionManager initialized");
Self
}
}
impl SessionManager {
pub(crate) fn spawn_session(
&self,
remote_address: String,
#[cfg(any(target_os = "android", target_os = "ios"))] tun_fd: Option<i32>,
#[cfg(any(target_os = "android", target_os = "ios"))] cache_dir: String,
) -> Arc<Session> {
let runtime = get_runtime();
let cancel_token = CancellationToken::new();
let session_token = cancel_token.clone();
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
let remote_proxy_ip = addr.ip().to_string();
runtime.spawn(async move {
info!("Starting VPN session thread...");
let cache_path = {
#[cfg(any(target_os = "android", target_os = "ios"))]
{
cache_dir
}
#[cfg(any(target_os = "linux", target_os = "windows"))]
{
".".to_string()
}
};
let tun_device = {
#[cfg(any(target_os = "android", target_os = "ios"))]
{
Tun::from_fd(tun_fd.expect("TUN FD required on mobile"))
.expect("Failed to init TUN from FD")
}
#[cfg(target_os = "linux")]
{
Tun::create(|config| {
config
.tun_name("netr0")
.address((10, 0, 0, 1))
.netmask((255, 255, 255, 0))
.mtu(1350)
.up();
})
.expect("Failed to init TUN")
}
#[cfg(target_os = "windows")]
{
Tun::create(|config| {
config.tun_name("netr0");
})
.expect("Failed to init TUN")
}
};
let builder_result = EngineBuilder::new(&remote_address)
.with_cache_path(&cache_path)
.with_tun(tun_device)
.build()
.await;
match builder_result {
Ok((mut engine, tun)) => {
info!("Engine async task started");
tokio::select! {
res = engine.run(tun) => {
info!("Engine loop finished: {:?}", res);
},
_ = cancel_token.cancelled() => {
info!("Engine task shutting down via token");
}
}
}
Err(e) => {
error!("Failed to build VPN Engine: {}", e);
}
}
});
Arc::new(Session {
cancel_token: session_token,
proxy_ip: remote_proxy_ip,
})
}
}
-2
View File
@@ -1,5 +1,3 @@
pub mod connection_manager;
pub mod device;
pub mod engine;
pub mod routing;
pub mod tun;
-4
View File
@@ -7,10 +7,6 @@ pub struct Tun {
}
impl Tun {
pub fn build() -> Configuration {
Configuration::default()
}
pub fn new(config: &Configuration) -> io::Result<Self> {
match create_as_async(config) {
Ok(device) => Ok(Self { device }),