15 march works with app

This commit is contained in:
2026-03-15 23:47:15 +07:00
parent 102099e1cd
commit 7dbfaec60d
38 changed files with 659 additions and 542 deletions
+138 -129
View File
@@ -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(())
}