15 march works with app
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use lru::LruCache;
|
||||
use netrunner_logger::{debug, info};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::num::NonZeroUsize;
|
||||
use tracing::{debug, info};
|
||||
|
||||
pub struct FakeIpStore {
|
||||
cache: LruCache<String, Ipv4Addr>,
|
||||
|
||||
@@ -5,7 +5,6 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, info, trace, warn};
|
||||
|
||||
pub enum ConnectionState {
|
||||
Established,
|
||||
@@ -38,24 +37,24 @@ impl TcpConnection {
|
||||
tokio::spawn(async move {
|
||||
let mut stream = match TcpStream::connect(&proxy_addr).await {
|
||||
Ok(s) => {
|
||||
debug!(%handle, "Connected to proxy successfully");
|
||||
netrunner_logger::debug!(%handle, "Connected to proxy successfully");
|
||||
s
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(%handle, error = %e, "Failed to connect to proxy");
|
||||
netrunner_logger::debug!(%handle, error = %e, "Failed to connect to proxy");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = SocksRequest::perform_client_handshake(&mut stream, &target_addr).await
|
||||
{
|
||||
debug!(%handle, error = %e, "SOCKS handshake failed");
|
||||
netrunner_logger::debug!(%handle, error = %e, "SOCKS handshake failed");
|
||||
return;
|
||||
}
|
||||
|
||||
let _ = handshake_tx.send(());
|
||||
|
||||
debug!(%handle, "SOCKS handshake successful, starting data bridge");
|
||||
netrunner_logger::debug!(%handle, "SOCKS handshake successful, starting data bridge");
|
||||
|
||||
let (mut reader, mut writer) = stream.into_split();
|
||||
|
||||
@@ -84,7 +83,7 @@ impl TcpConnection {
|
||||
tokio::select! {
|
||||
_ = to_proxy => {}
|
||||
_ = from_proxy => {}
|
||||
_ = task_token.cancelled() => { debug!(%handle, "Task cancelled by Manager"); }
|
||||
_ = task_token.cancelled() => { netrunner_logger::debug!(%handle, "Task cancelled by Manager"); }
|
||||
}
|
||||
});
|
||||
|
||||
@@ -170,7 +169,7 @@ impl TcpConnection {
|
||||
|
||||
if !self.pending_data.is_empty() {
|
||||
if self.pending_data.len() > MAX_PENDING {
|
||||
warn!(%self.handle, "Buffer overflow! Aborting connection.");
|
||||
netrunner_logger::warn!(%self.handle, "Buffer overflow! Aborting connection.");
|
||||
socket.abort();
|
||||
self.token.cancel();
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::connections::dns::handle_dns_query;
|
||||
use crate::connections::ip_store::FakeIpStore;
|
||||
use smoltcp::socket::udp;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
pub struct UdpConnection;
|
||||
|
||||
@@ -16,7 +15,7 @@ impl UdpConnection {
|
||||
let endpoint = metadata.endpoint;
|
||||
|
||||
if let Some(response) = handle_dns_query(&data, store) {
|
||||
debug!(to = %endpoint, "Sending DNS response");
|
||||
netrunner_logger::debug!(to = %endpoint, "Sending DNS response");
|
||||
let _ = socket.send_slice(&response, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-24
@@ -1,46 +1,35 @@
|
||||
uniffi::setup_scaffolding!();
|
||||
mod connections;
|
||||
pub mod tun;
|
||||
use std::sync::Mutex;
|
||||
use netrunner_logger::info;
|
||||
use std::sync::OnceLock;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::sync::oneshot;
|
||||
use tracing::info;
|
||||
mod session;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::tun::routing::reset_platform_routing;
|
||||
pub mod session;
|
||||
|
||||
static RUNTIME: OnceLock<Runtime> = OnceLock::new();
|
||||
|
||||
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) shutdown_tx: Mutex<Option<oneshot::Sender<()>>>,
|
||||
pub(crate) cancel_token: CancellationToken,
|
||||
pub(crate) proxy_ip: String,
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl Session {
|
||||
pub fn stop(&self) {
|
||||
let mut guard = self.shutdown_tx.lock().unwrap();
|
||||
if let Some(tx) = guard.take() {
|
||||
let _ = tx.send(());
|
||||
}
|
||||
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, resetting platform routing...");
|
||||
if let Ok(mut tx) = self.shutdown_tx.lock() {
|
||||
if let Some(tx) = tx.take() {
|
||||
let _ = tx.send(());
|
||||
}
|
||||
}
|
||||
let _ = crate::tun::routing::reset_platform_routing();
|
||||
info!("Session dropped, stopping all tasks...");
|
||||
self.cancel_token.cancel();
|
||||
let _ = reset_platform_routing(Some(&self.proxy_ip));
|
||||
}
|
||||
}
|
||||
|
||||
+10
-12
@@ -5,16 +5,14 @@ use netrunner_client::tun::{
|
||||
routing::{reset_platform_routing, setup_platform_routing},
|
||||
tun::Tun,
|
||||
};
|
||||
use netrunner_core::{
|
||||
logger_init,
|
||||
proxy::{connection::connection::ConnectionRole, network::Network},
|
||||
};
|
||||
use netrunner_core::proxy::{connection::connection::ConnectionRole, network::Network};
|
||||
use netrunner_logger::{error, info};
|
||||
use smoltcp::{iface::Config, phy::DeviceCapabilities};
|
||||
use tracing::{error, info};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
logger_init();
|
||||
netrunner_logger::Logger::init();
|
||||
info!("Initializing NetRunner Stack...");
|
||||
let tun_device = Tun::create(|config| {
|
||||
config
|
||||
@@ -26,7 +24,7 @@ async fn main() {
|
||||
})
|
||||
.expect("Failed to initialize TUN device");
|
||||
let remote_address: String = "62.60.244.156:443".into();
|
||||
setup_platform_routing(&remote_address).expect("Failed to setup routing");
|
||||
setup_platform_routing(&remote_address);
|
||||
|
||||
info!("TUN interface is UP: 10.0.0.1/24");
|
||||
|
||||
@@ -41,14 +39,14 @@ async fn main() {
|
||||
"0.0.0.0".into(),
|
||||
8080,
|
||||
ConnectionRole::Client,
|
||||
Some(remote_address),
|
||||
Some(remote_address.clone()),
|
||||
);
|
||||
|
||||
let proxy_ip = network.get_self_local_address();
|
||||
|
||||
tokio::spawn(async move {
|
||||
info!("Network thread started");
|
||||
network.run().await;
|
||||
network.run(CancellationToken::new()).await;
|
||||
});
|
||||
|
||||
let mut engine = Engine::new(config, caps, proxy_ip);
|
||||
@@ -60,7 +58,6 @@ async fn main() {
|
||||
|
||||
info!("Engine starting process loop...");
|
||||
|
||||
// 3. Используем select для перехвата сигнала завершения
|
||||
let ctrl_c = tokio::signal::ctrl_c();
|
||||
|
||||
tokio::select! {
|
||||
@@ -72,9 +69,10 @@ async fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// 4. ГАРАНТИРОВАННЫЙ СБРОС
|
||||
info!("Restoring system routing...");
|
||||
if let Err(e) = reset_platform_routing() {
|
||||
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
|
||||
let proxy_ip = addr.ip().to_string();
|
||||
if let Err(e) = reset_platform_routing(Some(&proxy_ip)) {
|
||||
error!("Failed to reset routing: {}", e);
|
||||
} else {
|
||||
info!("System routing restored successfully.");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::session::{Session, SessionManager};
|
||||
use super::{Session, SessionManager};
|
||||
use std::sync::Arc;
|
||||
use uniffi;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::session::{Session, SessionManager};
|
||||
use super::{Session, SessionManager};
|
||||
use std::sync::Arc;
|
||||
use uniffi;
|
||||
|
||||
|
||||
+44
-26
@@ -1,27 +1,33 @@
|
||||
#[cfg(feature = "desktop")]
|
||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||
pub mod desktop;
|
||||
|
||||
#[cfg(feature = "mobile")]
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
pub mod mobile;
|
||||
|
||||
use crate::{
|
||||
Session, get_runtime,
|
||||
RUNTIME, Session,
|
||||
tun::{
|
||||
engine::Engine,
|
||||
routing::{reset_platform_routing, setup_platform_routing},
|
||||
tun::Tun,
|
||||
},
|
||||
};
|
||||
use netrunner_core::{
|
||||
logger_init,
|
||||
proxy::{connection::connection::ConnectionRole, network::Network},
|
||||
};
|
||||
use netrunner_core::proxy::{connection::connection::ConnectionRole, network::Network};
|
||||
use netrunner_logger::{error, info};
|
||||
use smoltcp::{iface::Config, phy::DeviceCapabilities};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::signal;
|
||||
use tokio::sync::oneshot;
|
||||
use tracing::{error, info};
|
||||
use std::sync::Arc;
|
||||
use tokio::{runtime::Runtime, signal};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
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 SessionManager;
|
||||
@@ -30,7 +36,7 @@ pub struct SessionManager;
|
||||
impl SessionManager {
|
||||
#[uniffi::constructor]
|
||||
pub fn new() -> Self {
|
||||
logger_init();
|
||||
netrunner_logger::Logger::init();
|
||||
info!("SessionManager initialized");
|
||||
Self
|
||||
}
|
||||
@@ -42,34 +48,47 @@ impl SessionManager {
|
||||
remote_address: String,
|
||||
tun_fd: Option<i32>,
|
||||
) -> Arc<Session> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let runtime = get_runtime();
|
||||
|
||||
let cancel_token = CancellationToken::new();
|
||||
let sesison_token = cancel_token.clone();
|
||||
let net_token = cancel_token.clone();
|
||||
let shutdown_signal = signal::ctrl_c();
|
||||
|
||||
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
|
||||
let remote_proxy_ip = addr.ip().to_string();
|
||||
let proxy_ip_for_thread = remote_proxy_ip.clone();
|
||||
|
||||
runtime.spawn(async move {
|
||||
info!("Starting VPN session thread...");
|
||||
|
||||
let tun_device = {
|
||||
#[cfg(feature = "mobile")]
|
||||
#[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(feature = "desktop")]
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
Tun::create(|config| {
|
||||
config
|
||||
.tun_name("netr0")
|
||||
.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.mtu(1200)
|
||||
.up();
|
||||
})
|
||||
.expect("Failed to init TUN")
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
Tun::create(|config| {
|
||||
config.tun_name("netr0");
|
||||
})
|
||||
.expect("Failed to init TUN")
|
||||
}
|
||||
};
|
||||
|
||||
setup_platform_routing(&remote_address).expect("Failed to setup routing");
|
||||
setup_platform_routing(&remote_address);
|
||||
|
||||
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
|
||||
let mut caps = DeviceCapabilities::default();
|
||||
@@ -84,8 +103,8 @@ impl SessionManager {
|
||||
);
|
||||
let proxy_ip = network.get_self_local_address();
|
||||
|
||||
let net_handle = tokio::spawn(async move {
|
||||
network.run().await;
|
||||
tokio::spawn(async move {
|
||||
network.run(net_token).await;
|
||||
});
|
||||
|
||||
let mut engine = Engine::new(config, caps, proxy_ip);
|
||||
@@ -96,21 +115,20 @@ impl SessionManager {
|
||||
|
||||
tokio::select! {
|
||||
res = engine.run(tun_device) => error!("Engine loop error: {:?}", res),
|
||||
_ = rx => {
|
||||
_ = cancel_token.cancelled() => {
|
||||
info!("Shutdown signal received");
|
||||
let _ = reset_platform_routing(); // Очистка при сигнале
|
||||
|
||||
let _ = reset_platform_routing(Some(&proxy_ip_for_thread));
|
||||
},
|
||||
_ = shutdown_signal => {
|
||||
info!("Ctrl+C detected, shutting down gracefully...");
|
||||
info!("Restoring routing...");
|
||||
let _ = reset_platform_routing();
|
||||
net_handle.abort();
|
||||
cancel_token.cancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Arc::new(Session {
|
||||
shutdown_tx: Mutex::new(Some(tx)),
|
||||
cancel_token: sesison_token,
|
||||
proxy_ip: remote_proxy_ip,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use netrunner_core::protocol::codec::socks::TargetAddress;
|
||||
use netrunner_logger::{debug, info, warn};
|
||||
use smoltcp::{
|
||||
iface::{SocketHandle, SocketSet},
|
||||
socket::{AnySocket, icmp, tcp, udp},
|
||||
@@ -8,7 +9,6 @@ use std::{
|
||||
collections::HashMap,
|
||||
time::{Duration, Instant as StdInstant},
|
||||
};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
use crate::{
|
||||
connections::{
|
||||
|
||||
@@ -14,7 +14,6 @@ use smoltcp::{
|
||||
time::Instant,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::trace;
|
||||
|
||||
pub struct VirtTunDevice {
|
||||
capabilities: DeviceCapabilities,
|
||||
|
||||
@@ -16,7 +16,7 @@ use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||
use tokio::time::{Duration, sleep};
|
||||
use tun::{DeviceReader, DeviceWriter};
|
||||
|
||||
use tracing::{debug, info, warn};
|
||||
use netrunner_logger::{debug, info, warn};
|
||||
|
||||
use crate::tun::connection_manager::ConnectionManager;
|
||||
use crate::tun::device::{TokenBuffer, VirtTunDevice};
|
||||
|
||||
+138
-129
@@ -1,160 +1,169 @@
|
||||
use crate::tun::tun::Tun;
|
||||
use netrunner_logger::{error, info, warn};
|
||||
use std::io;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
#[cfg(any(feature = "linux", feature = "windows"))]
|
||||
use std::process::Command;
|
||||
|
||||
fn run_cmd_ext(full_cmd: &str, ignore_errors: bool) -> io::Result<()> {
|
||||
let parts = shlex::split(full_cmd)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Invalid syntax"))?;
|
||||
|
||||
if parts.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let status = Command::new(&parts[0]).args(&parts[1..]).status()?;
|
||||
|
||||
if !status.success() && !ignore_errors {
|
||||
let err = format!("Command failed: {} with status {}", full_cmd, status);
|
||||
error!("{}", err);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, err));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn get_active_interface_index() -> Option<u32> {
|
||||
// Получаем список интерфейсов через netsh
|
||||
let output = Command::new("netsh")
|
||||
.args(["interface", "ipv4", "show", "interfaces"])
|
||||
.output()
|
||||
.ok()?;
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
for line in stdout.lines() {
|
||||
if line.contains("Connected") && !line.contains("netr0") {
|
||||
if let Some(idx_str) = line.split_whitespace().next() {
|
||||
if let Ok(idx) = idx_str.parse::<u32>() {
|
||||
return Some(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
|
||||
let proxy_ip = remote_address.split(':').next().unwrap_or(remote_address);
|
||||
|
||||
#[cfg(feature = "linux")]
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// 1. Бэкап и получение текущего маршрута по умолчанию
|
||||
let _ = Command::new("sudo")
|
||||
.args(&["cp", "/etc/resolv.conf", "/etc/resolv.conf.bak"])
|
||||
.status();
|
||||
// 1. Предварительная настройка ядра (rp_filter и пересылка)
|
||||
let _ = run_cmd_ext("sysctl -w net.ipv4.conf.all.rp_filter=0", true);
|
||||
let _ = run_cmd_ext("sysctl -w net.ipv4.conf.netr0.rp_filter=0", true);
|
||||
let _ = run_cmd_ext("sysctl -w net.ipv4.ip_forward=1", true);
|
||||
|
||||
let output = Command::new("ip")
|
||||
.args(&["route", "show", "default"])
|
||||
.output()?;
|
||||
let default_route = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
// 3. Маршрутизация (игнорируем ошибки, если правила уже есть)
|
||||
let _ = run_cmd_ext("ip rule add fwmark 0x1 table 100", true);
|
||||
let _ = run_cmd_ext("ip route add default dev netr0 table 100", true);
|
||||
|
||||
if default_route.is_empty() {
|
||||
error!("Default route не найден!");
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"No default route found",
|
||||
));
|
||||
}
|
||||
std::fs::write("/tmp/netrunner_default_route", &default_route)?;
|
||||
// 4. NFTables
|
||||
let _ = run_cmd_ext("nft delete table ip netrunner", true);
|
||||
run_cmd_ext("nft add table ip netrunner", false)?;
|
||||
run_cmd_ext(
|
||||
"nft add chain ip netrunner output { type route hook output priority 0; }",
|
||||
false,
|
||||
)?;
|
||||
|
||||
// 2. Парсинг параметров из маршрута по умолчанию
|
||||
let parts: Vec<&str> = default_route.split_whitespace().collect();
|
||||
let dev = parts
|
||||
.iter()
|
||||
.position(|&x| x == "dev")
|
||||
.and_then(|i| parts.get(i + 1))
|
||||
.copied()
|
||||
.unwrap_or("eth0");
|
||||
let via = parts
|
||||
.iter()
|
||||
.position(|&x| x == "via")
|
||||
.and_then(|i| parts.get(i + 1))
|
||||
.copied();
|
||||
|
||||
// 3. Добавляем маршрут до прокси-сервера
|
||||
let mut proxy_route = vec!["ip", "route", "add", proxy_ip, "dev", dev];
|
||||
if let Some(gw) = via {
|
||||
proxy_route.extend_from_slice(&["via", gw]);
|
||||
}
|
||||
let _ = Command::new("sudo").args(proxy_route).status();
|
||||
|
||||
// 4. Заменяем default маршрут на наш TUN
|
||||
let _ = Command::new("sudo")
|
||||
.args(&["ip", "route", "del", "default"])
|
||||
.status();
|
||||
let status = Command::new("sudo")
|
||||
.args(&[
|
||||
"ip", "route", "add", "default", "via", "10.0.0.2", "dev", "netr0", "metric", "1",
|
||||
])
|
||||
.status()?;
|
||||
|
||||
if status.success() {
|
||||
info!("Linux TUN default set.");
|
||||
}
|
||||
let mark_rule = format!(
|
||||
"nft add rule ip netrunner output ip daddr != {} oifname != \"netr0\" mark set 0x1",
|
||||
proxy_ip
|
||||
);
|
||||
run_cmd_ext(&mark_rule, false)?;
|
||||
|
||||
// 5. DNS
|
||||
std::fs::write("/tmp/resolv.conf.netrunner", "nameserver 10.0.0.2\n")?;
|
||||
Command::new("sudo")
|
||||
.args(&["cp", "/tmp/resolv.conf.netrunner", "/etc/resolv.conf"])
|
||||
.status()?;
|
||||
}
|
||||
let _ = Command::new("resolvectl")
|
||||
.args(["dns", "netr0", "10.0.0.2"])
|
||||
.status();
|
||||
let _ = Command::new("resolvectl")
|
||||
.args(["domain", "netr0", "~."])
|
||||
.status();
|
||||
|
||||
#[cfg(feature = "windows")]
|
||||
info!("Linux network auto-configured: RPF=0, MTU=1280, Rules active.");
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Логика Windows
|
||||
Command::new("route")
|
||||
.args(&[
|
||||
"add",
|
||||
proxy_ip,
|
||||
"mask",
|
||||
"255.255.255.255",
|
||||
"0.0.0.0",
|
||||
"metric",
|
||||
"1",
|
||||
])
|
||||
.status()?;
|
||||
Command::new("netsh")
|
||||
.args(&[
|
||||
"interface",
|
||||
"ipv4",
|
||||
"set",
|
||||
"address",
|
||||
"name=netr0",
|
||||
"static",
|
||||
"10.0.0.1",
|
||||
"255.255.255.0",
|
||||
"10.0.0.2",
|
||||
])
|
||||
.status()?;
|
||||
use std::{process::Command, thread, time::Duration};
|
||||
|
||||
// DNS
|
||||
Command::new("netsh")
|
||||
.args(&[
|
||||
"interface",
|
||||
"ipv4",
|
||||
"set",
|
||||
"dnsservers",
|
||||
"name=netr0",
|
||||
"static",
|
||||
"10.0.0.2",
|
||||
"primary",
|
||||
])
|
||||
.status()?;
|
||||
// 1. Инициализация Wintun
|
||||
let wintun = unsafe { wintun::load_from_path("wintun.dll") }.map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, format!("Wintun load error: {}", e))
|
||||
})?;
|
||||
|
||||
let adapter = match wintun::Adapter::open(&wintun, "netr0") {
|
||||
Ok(a) => a,
|
||||
Err(_) => {
|
||||
wintun::Adapter::create(&wintun, "netr0", "Wintun Tunnel", None).map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, format!("Wintun create error: {}", e))
|
||||
})?
|
||||
}
|
||||
};
|
||||
|
||||
// 2. Получаем индекс активного интерфейса для маршрутизации прокси
|
||||
let if_idx = get_active_interface_index().unwrap_or(1);
|
||||
info!(
|
||||
"Wintun adapter active. Routing proxy traffic via interface index: {}",
|
||||
if_idx
|
||||
);
|
||||
|
||||
// 3. Добавляем маршрут к IP прокси через ИНДЕКС (самый надежный способ)
|
||||
let route_cmd = format!(
|
||||
"netsh interface ipv4 add route {}/32 interface={} metric=1",
|
||||
proxy_ip, if_idx
|
||||
);
|
||||
// Игнорируем ошибку, если маршрут уже существует
|
||||
let _ = run_cmd_ext(&route_cmd, true);
|
||||
|
||||
// 4. Настраиваем адрес (БЕЗ шлюза 10.0.0.2, чтобы не перехватить весь трафик)
|
||||
let addr_cmd =
|
||||
"netsh interface ipv4 set address name=\"netr0\" static 10.0.0.1 255.255.255.0";
|
||||
|
||||
// 5. Задаем DNS и применяем настройки
|
||||
let dns_cmd = "netsh interface ipv4 set dnsservers name=\"netr0\" static 10.0.0.2 primary validate=no";
|
||||
|
||||
let mut attempt = 0;
|
||||
while attempt < 5 {
|
||||
if run_cmd_ext(addr_cmd, false).is_ok() {
|
||||
let _ = run_cmd_ext(dns_cmd, false);
|
||||
break;
|
||||
}
|
||||
attempt += 1;
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mobile")]
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
{
|
||||
eprintln!("Android/Mobile routing on native side");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reset_platform_routing() -> io::Result<()> {
|
||||
#[cfg(feature = "linux")]
|
||||
pub fn reset_platform_routing(proxy_ip: Option<&str>) -> io::Result<()> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// 1. Возвращаем дефолтный маршрут из файла
|
||||
if let Ok(saved_route) = std::fs::read_to_string("/tmp/netrunner_default_route") {
|
||||
let _ = Command::new("sudo")
|
||||
.args(&["ip", "route", "del", "default"])
|
||||
.status();
|
||||
let args: Vec<&str> = saved_route.split_whitespace().collect();
|
||||
let _ = Command::new("sudo")
|
||||
.args(&["ip", "route", "add"])
|
||||
.args(args)
|
||||
.status();
|
||||
let _ = run_cmd_ext("ip rule del fwmark 0x1 table 100", true);
|
||||
let _ = run_cmd_ext("ip route flush table 100", true);
|
||||
let _ = run_cmd_ext("nft delete table ip netrunner", true);
|
||||
info!("Linux routing reset.");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if let Some(ip) = proxy_ip {
|
||||
// Удаляем только маршрут к конкретному IP прокси-сервера.
|
||||
// Это гораздо безопаснее, чем удалять маршрут по умолчанию (0.0.0.0).
|
||||
let cmd = format!("route delete {}", ip);
|
||||
let _ = run_cmd_ext(&cmd, true);
|
||||
let _ = run_cmd_ext("netsh interface delete interface name=\"netr0\"", true);
|
||||
info!("Windows routing for proxy {} removed.", ip);
|
||||
} else {
|
||||
error!("Cannot reset Windows routing: proxy_ip is missing.");
|
||||
}
|
||||
|
||||
// 2. Удаляем наш специфичный маршрут к прокси
|
||||
// (Опционально, если он был добавлен)
|
||||
|
||||
// 3. Восстанавливаем DNS (если есть бэкап)
|
||||
let _ = Command::new("sudo")
|
||||
.args(&["cp", "/etc/resolv.conf.bak", "/etc/resolv.conf"])
|
||||
.status();
|
||||
|
||||
info!("Linux routing restored.");
|
||||
}
|
||||
|
||||
#[cfg(feature = "windows")]
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
{
|
||||
// В Windows можно просто удалить маршрут и интерфейс
|
||||
let _ = Command::new("route").args(&["delete", "0.0.0.0"]).status();
|
||||
// При переподключении сети (или ipconfig /renew) Windows сам подтянет настройки
|
||||
info!("Windows routing reset requested.");
|
||||
eprintln!("Android/Mobile routing on native side");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use netrunner_logger::{error, info, warn};
|
||||
use std::io;
|
||||
use tracing::{error, info, warn};
|
||||
use tun::{AsyncDevice, Configuration, DeviceReader, DeviceWriter, create_as_async};
|
||||
|
||||
pub struct Tun {
|
||||
@@ -21,7 +21,6 @@ impl Tun {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "desktop")]
|
||||
pub fn create<F>(f: F) -> io::Result<Self>
|
||||
where
|
||||
F: FnOnce(&mut Configuration),
|
||||
@@ -31,7 +30,6 @@ impl Tun {
|
||||
Self::new(&config)
|
||||
}
|
||||
|
||||
#[cfg(feature = "mobile")]
|
||||
pub fn from_fd(fd: i32) -> io::Result<Self> {
|
||||
let mut config = Configuration::default();
|
||||
config.raw_fd(fd);
|
||||
|
||||
Reference in New Issue
Block a user