structure refactoring
This commit is contained in:
@@ -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
@@ -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
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
pub mod connection_manager;
|
||||
pub mod device;
|
||||
pub mod engine;
|
||||
pub mod routing;
|
||||
pub mod tun;
|
||||
|
||||
@@ -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 }),
|
||||
|
||||
Reference in New Issue
Block a user