comments remove

This commit is contained in:
2026-03-20 15:05:22 +07:00
parent bc8662034f
commit ea3492c6a7
19 changed files with 43 additions and 149 deletions
+2 -4
View File
@@ -40,10 +40,10 @@ impl DnsHandler {
if needs_update { if needs_update {
let p_clone = self.cache_path.clone(); let p_clone = self.cache_path.clone();
// Просто спавним асинхронную задачу
tokio::spawn(async move { tokio::spawn(async move {
tokio::time::sleep(Duration::from_secs(10)).await; tokio::time::sleep(Duration::from_secs(10)).await;
// Теперь вызываем асинхронно через .await
if let Err(e) = Self::download_blocklist_async(p_clone).await { if let Err(e) = Self::download_blocklist_async(p_clone).await {
netrunner_logger::error!("DNS: Background update failed: {}", e); netrunner_logger::error!("DNS: Background update failed: {}", e);
} }
@@ -53,7 +53,6 @@ impl DnsHandler {
Ok(()) Ok(())
} }
// Делаем функцию асинхронной и убираем block_on
async fn download_blocklist_async(cache_path: String) -> anyhow::Result<()> { async fn download_blocklist_async(cache_path: String) -> anyhow::Result<()> {
info!("DNS: Starting background download to {}", cache_path); info!("DNS: Starting background download to {}", cache_path);
@@ -112,7 +111,6 @@ impl DnsHandler {
.set_recursion_available(true) .set_recursion_available(true)
.add_query(query.clone()); .add_query(query.clone());
// Проверка блокировок
if self.forbidden_suffixes.iter().any(|s| name.ends_with(s)) if self.forbidden_suffixes.iter().any(|s| name.ends_with(s))
|| self.block_list.contains(&name) || self.block_list.contains(&name)
{ {
+1 -1
View File
@@ -1,4 +1,4 @@
use crate::connections::dns::DnsHandler; // проверь путь use crate::connections::dns::DnsHandler;
use crate::connections::ip_store::FakeIpStore; use crate::connections::ip_store::FakeIpStore;
use smoltcp::socket::udp; use smoltcp::socket::udp;
-8
View File
@@ -14,18 +14,14 @@ use tokio_util::sync::CancellationToken;
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
// 1. Инициализация логгера (теперь он внутри асинхронного контекста)
netrunner_logger::Logger::init(); netrunner_logger::Logger::init();
info!("Initializing NetRunner Stack..."); info!("Initializing NetRunner Stack...");
// 2. Инициализация DNS асинхронно
// Передаем пустую строку или путь к кэшу (на десктопе можно ".")
let mut dns_handler = DnsHandler::new("."); let mut dns_handler = DnsHandler::new(".");
if let Err(e) = dns_handler.init().await { if let Err(e) = dns_handler.init().await {
error!("Failed to initialize DNS blocklist: {}", e); error!("Failed to initialize DNS blocklist: {}", e);
} }
// 3. Настройка TUN устройства
let tun_device = Tun::create(|config| { let tun_device = Tun::create(|config| {
config config
.tun_name("netr0") .tun_name("netr0")
@@ -41,7 +37,6 @@ async fn main() -> anyhow::Result<()> {
info!("TUN interface is UP: 10.0.0.1/24"); info!("TUN interface is UP: 10.0.0.1/24");
// 4. Конфигурация стека протоколов (MTU 1280 для стабильности)
let config = Config::new(smoltcp::wire::HardwareAddress::Ip); let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
let mut caps = DeviceCapabilities::default(); let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1280; caps.max_transmission_unit = 1280;
@@ -56,7 +51,6 @@ async fn main() -> anyhow::Result<()> {
let proxy_ip = network.get_self_local_address(); let proxy_ip = network.get_self_local_address();
// 5. Запуск сетевого прокси в отдельной задаче
let network_token = CancellationToken::new(); let network_token = CancellationToken::new();
let net_token_for_spawn = network_token.clone(); let net_token_for_spawn = network_token.clone();
tokio::spawn(async move { tokio::spawn(async move {
@@ -64,7 +58,6 @@ async fn main() -> anyhow::Result<()> {
network.run(net_token_for_spawn).await; network.run(net_token_for_spawn).await;
}); });
// 6. Инициализация Engine
let mut engine = Engine::new(config, caps, proxy_ip, dns_handler); let mut engine = Engine::new(config, caps, proxy_ip, dns_handler);
engine.set_any_ip(true); engine.set_any_ip(true);
engine.set_transparent_mode(); engine.set_transparent_mode();
@@ -84,7 +77,6 @@ async fn main() -> anyhow::Result<()> {
} }
} }
// 8. Очистка ресурсов
network_token.cancel(); network_token.cancel();
info!("Restoring system routing..."); info!("Restoring system routing...");
let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format"); let addr: std::net::SocketAddr = remote_address.parse().expect("Invalid address format");
+5 -6
View File
@@ -43,8 +43,7 @@ impl SessionManager {
pub(crate) fn spawn_session( pub(crate) fn spawn_session(
&self, &self,
remote_address: String, remote_address: String,
#[cfg(any(target_os = "android", target_os = "ios"))] // Путь обязателен для мобилок #[cfg(any(target_os = "android", target_os = "ios"))] tun_fd: Option<i32>,
tun_fd: Option<i32>,
#[cfg(any(target_os = "android", target_os = "ios"))] cache_dir: String, #[cfg(any(target_os = "android", target_os = "ios"))] cache_dir: String,
) -> Arc<Session> { ) -> Arc<Session> {
let runtime = get_runtime(); let runtime = get_runtime();
@@ -62,11 +61,11 @@ impl SessionManager {
#[cfg(any(target_os = "android", target_os = "ios"))] #[cfg(any(target_os = "android", target_os = "ios"))]
{ {
cache_dir cache_dir
} // Используем путь из мобильного приложения }
#[cfg(any(target_os = "linux", target_os = "windows"))] #[cfg(any(target_os = "linux", target_os = "windows"))]
{ {
".".to_string() ".".to_string()
} // На ПК пишем в локальную папку }
}; };
let mut dns_handler = DnsHandler::new(&cache_path); let mut dns_handler = DnsHandler::new(&cache_path);
@@ -132,9 +131,9 @@ impl SessionManager {
info!("Engine async task started"); info!("Engine async task started");
tokio::select! { tokio::select! {
// Добавляем обработку результата прямо здесь
res = engine.run(tun_device) => { res = engine.run(tun_device) => {
// Теперь эта ветка возвращает (), так как мы обработали результат
info!("Engine loop finished: {:?}", res); info!("Engine loop finished: {:?}", res);
}, },
_ = cancel_token_for_engine.cancelled() => { _ = cancel_token_for_engine.cancelled() => {
+4 -12
View File
@@ -114,22 +114,18 @@ impl ConnectionManager {
fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) { fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) {
use tcp::State; use tcp::State;
// 1. Очистка закрытых сокетов
if socket.state() == State::Closed { if socket.state() == State::Closed {
// Если сокет закрыт, удаляем его сессию и помечаем на удаление из сета
if self.active_tcp_sessions.contains_key(&handle) { if self.active_tcp_sessions.contains_key(&handle) {
debug!(%handle, "TCP session closed, removing from active sessions"); debug!(%handle, "TCP session closed, removing from active sessions");
self.active_tcp_sessions.remove(&handle); self.active_tcp_sessions.remove(&handle);
} }
// Добавляем в очередь на удаление из SocketSet (чтобы освободить память)
if !self.sockets_to_remove.contains(&handle) { if !self.sockets_to_remove.contains(&handle) {
self.sockets_to_remove.push(handle); self.sockets_to_remove.push(handle);
} }
return; return;
} }
// 2. Инициализация сессии при установке соединения
if socket.state() == State::Established && !self.active_tcp_sessions.contains_key(&handle) { if socket.state() == State::Established && !self.active_tcp_sessions.contains_key(&handle) {
let target = self.resolve_target(socket); let target = self.resolve_target(socket);
@@ -145,12 +141,10 @@ impl ConnectionManager {
self.active_tcp_sessions.insert(handle, conn); self.active_tcp_sessions.insert(handle, conn);
} }
// 3. Тик активной сессии (проброс данных в прокси)
if let Some(conn) = self.active_tcp_sessions.get_mut(&handle) { if let Some(conn) = self.active_tcp_sessions.get_mut(&handle) {
if !conn.tick(socket) { if !conn.tick(socket) {
debug!(%handle, "Connection tick failed, aborting."); debug!(%handle, "Connection tick failed, aborting.");
socket.abort(); socket.abort();
// Сессия удалится на следующем проходе, когда стейт станет Closed
} }
} }
@@ -190,9 +184,9 @@ impl ConnectionManager {
fn create_dynamic_tcp_socket<'a>(port: u16) -> tcp::Socket<'a> { fn create_dynamic_tcp_socket<'a>(port: u16) -> tcp::Socket<'a> {
let buf_size = match port { let buf_size = match port {
443 | 80 => 256 * 1024, // 256 KB для веба 443 | 80 => 256 * 1024,
22 | 53 | 123 => 16 * 1024, // 16 KB для мелких протоколов (SSH, DNS over TCP, NTP) 22 | 53 | 123 => 16 * 1024,
_ => 64 * 1024, // Дефолт _ => 64 * 1024,
}; };
let mut socket = tcp::Socket::new( let mut socket = tcp::Socket::new(
@@ -200,7 +194,7 @@ impl ConnectionManager {
tcp::SocketBuffer::new(vec![0; buf_size]), tcp::SocketBuffer::new(vec![0; buf_size]),
); );
socket.set_nagle_enabled(false); // Для отзывчивости (особенно в играх типа Silent Hill, если через VPN) socket.set_nagle_enabled(false);
socket.set_ack_delay(None); socket.set_ack_delay(None);
socket socket
} }
@@ -216,12 +210,10 @@ impl ConnectionManager {
return; return;
}; };
// Ищем только SYN (начало соединения)
if tcp_packet.syn() && !tcp_packet.ack() { if tcp_packet.syn() && !tcp_packet.ack() {
let dst_port = tcp_packet.dst_port(); let dst_port = tcp_packet.dst_port();
let dst_addr = ip_packet.dst_addr(); let dst_addr = ip_packet.dst_addr();
// Проверяем, не создали ли мы уже такой сокет на предыдущем шаге
if !self.has_active_session(socket_set, dst_addr.into(), dst_port) { if !self.has_active_session(socket_set, dst_addr.into(), dst_port) {
debug!(target: "netrunner", "Dynamic TCP: Creating socket for {}:{}", dst_addr, dst_port); debug!(target: "netrunner", "Dynamic TCP: Creating socket for {}:{}", dst_addr, dst_port);
+4 -6
View File
@@ -15,7 +15,6 @@ use smoltcp::{
}; };
use tokio::sync::mpsc; use tokio::sync::mpsc;
// --- TokenBuffer (без изменений, он у тебя отличный) ---
const TOKEN_BUFFER_LIST_MAX_SIZE: usize = 64; const TOKEN_BUFFER_LIST_MAX_SIZE: usize = 64;
static TOKEN_BUFFER_LIST: LazyLock<Mutex<Vec<BytesMut>>> = LazyLock::new(|| Mutex::new(Vec::new())); static TOKEN_BUFFER_LIST: LazyLock<Mutex<Vec<BytesMut>>> = LazyLock::new(|| Mutex::new(Vec::new()));
@@ -61,11 +60,10 @@ impl DerefMut for TokenBuffer {
} }
} }
// --- VirtTunDevice ---
pub struct VirtTunDevice { pub struct VirtTunDevice {
capabilities: DeviceCapabilities, capabilities: DeviceCapabilities,
rx_queue: mpsc::UnboundedReceiver<TokenBuffer>, // smoltcp читает отсюда rx_queue: mpsc::UnboundedReceiver<TokenBuffer>,
tx_queue: mpsc::UnboundedSender<TokenBuffer>, // smoltcp пишет сюда tx_queue: mpsc::UnboundedSender<TokenBuffer>,
rx_avail: Arc<AtomicBool>, rx_avail: Arc<AtomicBool>,
} }
@@ -74,8 +72,8 @@ impl VirtTunDevice {
capabilities: DeviceCapabilities, capabilities: DeviceCapabilities,
) -> ( ) -> (
Self, Self,
mpsc::UnboundedSender<TokenBuffer>, // Канал, чтобы закидывать пакеты в smoltcp mpsc::UnboundedSender<TokenBuffer>,
mpsc::UnboundedReceiver<TokenBuffer>, // Канал, чтобы забирать готовые пакеты из smoltcp mpsc::UnboundedReceiver<TokenBuffer>,
Arc<AtomicBool>, Arc<AtomicBool>,
) { ) {
let (to_smoltcp_tx, to_smoltcp_rx) = mpsc::unbounded_channel(); let (to_smoltcp_tx, to_smoltcp_rx) = mpsc::unbounded_channel();
+1 -13
View File
@@ -29,11 +29,7 @@ pub struct Engine {
socket_set: SocketSet<'static>, socket_set: SocketSet<'static>,
manager: ConnectionManager, manager: ConnectionManager,
device: VirtTunDevice, device: VirtTunDevice,
// Канал для инъекции пакетов в smoltcp
to_smoltcp_tx: UnboundedSender<TokenBuffer>, to_smoltcp_tx: UnboundedSender<TokenBuffer>,
// Временное хранилище канала выхода из smoltcp (отдадим воркеру в run)
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>, from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
avail: Arc<AtomicBool>, avail: Arc<AtomicBool>,
@@ -47,7 +43,7 @@ impl Engine {
dns_handler: DnsHandler, dns_handler: DnsHandler,
) -> Self { ) -> Self {
let now = Engine::current_time(); let now = Engine::current_time();
// Смотри, как чисто мы получаем компоненты:
let (mut device, to_smoltcp_tx, from_smoltcp_rx, avail) = VirtTunDevice::new(caps); let (mut device, to_smoltcp_tx, from_smoltcp_rx, avail) = VirtTunDevice::new(caps);
let interface = Interface::new(config, &mut device, now); let interface = Interface::new(config, &mut device, now);
@@ -69,25 +65,19 @@ impl Engine {
info!("Current routes: {:?}", self.interface.routes()); info!("Current routes: {:?}", self.interface.routes());
let (writer, reader) = tun.split().expect("Failed to split TUN"); let (writer, reader) = tun.split().expect("Failed to split TUN");
// 1. Создаем трубу для ВХОДЯЩЕГО трафика: TUN -> Engine
let (tun_to_engine_tx, mut tun_to_engine_rx) = mpsc::unbounded_channel(); let (tun_to_engine_tx, mut tun_to_engine_rx) = mpsc::unbounded_channel();
Self::spawn_tun_reader(reader, tun_to_engine_tx, self.avail.clone()); Self::spawn_tun_reader(reader, tun_to_engine_tx, self.avail.clone());
// 2. Запускаем трубу ИСХОДЯЩЕГО трафика: smoltcp -> TUN
let from_smoltcp_rx = self.from_smoltcp_rx.take().expect("Engine started twice"); let from_smoltcp_rx = self.from_smoltcp_rx.take().expect("Engine started twice");
Self::spawn_tun_writer(writer, from_smoltcp_rx); Self::spawn_tun_writer(writer, from_smoltcp_rx);
let mut last_log = StdInstant::now(); let mut last_log = StdInstant::now();
// 3. Главный цикл (ДИСПЕТЧЕР)
loop { loop {
// Читаем всё, что прилетело из операционной системы
while let Ok(token) = tun_to_engine_rx.try_recv() { while let Ok(token) = tun_to_engine_rx.try_recv() {
// АНАЛИЗ: Создаем сокет, если это новый SYN
self.manager self.manager
.try_create_socket_from_packet(&token, &mut self.socket_set); .try_create_socket_from_packet(&token, &mut self.socket_set);
// ПЕРЕДАЧА: Закидываем пакет в виртуальную сетевую карту smoltcp
if self.to_smoltcp_tx.send(token).is_ok() { if self.to_smoltcp_tx.send(token).is_ok() {
self.device.mark_rx_available(); self.device.mark_rx_available();
} }
@@ -131,7 +121,6 @@ impl Engine {
sleep(sleep_duration).await; sleep(sleep_duration).await;
} }
// Воркер: Читает ОС -> Шлет в Engine
fn spawn_tun_reader( fn spawn_tun_reader(
mut reader: DeviceReader, mut reader: DeviceReader,
to_engine: UnboundedSender<TokenBuffer>, to_engine: UnboundedSender<TokenBuffer>,
@@ -173,7 +162,6 @@ impl Engine {
}); });
} }
// Воркер: Читает smoltcp -> Шлет в ОС
fn spawn_tun_writer( fn spawn_tun_writer(
mut writer: DeviceWriter, mut writer: DeviceWriter,
mut from_smoltcp: UnboundedReceiver<TokenBuffer>, mut from_smoltcp: UnboundedReceiver<TokenBuffer>,
+2 -14
View File
@@ -46,7 +46,7 @@ fn get_default_gateway() -> Option<String> {
.output() .output()
.ok()?; .ok()?;
let stdout = String::from_utf8_lossy(&output.stdout); let stdout = String::from_utf8_lossy(&output.stdout);
// Простой парсинг: ищем строку с 0.0.0.0 и берем IP из колонки шлюза
stdout stdout
.lines() .lines()
.find(|line| line.contains("0.0.0.0")) .find(|line| line.contains("0.0.0.0"))
@@ -59,19 +59,15 @@ pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
// 1. Ядро
let _ = run_cmd_ext("sysctl -w net.ipv4.conf.all.rp_filter=0", true); 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.conf.netr0.rp_filter=0", true);
let _ = run_cmd_ext("sysctl -w net.ipv4.ip_forward=1", true); let _ = run_cmd_ext("sysctl -w net.ipv4.ip_forward=1", true);
// 2. Маршрутизация
let _ = run_cmd_ext("ip rule add fwmark 0x1 table 100", true); 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); let _ = run_cmd_ext("ip route add default dev netr0 table 100", true);
// 3. NFTables (Чиним ошибку удаления и синтаксис)
// Добавляем таблицу (если есть — ничего не сделает, если нет — создаст)
run_cmd_ext("nft add table ip netrunner", true)?; run_cmd_ext("nft add table ip netrunner", true)?;
// Очищаем таблицу перед работой, чтобы не плодить дубликаты
run_cmd_ext("nft flush table ip netrunner", true)?; run_cmd_ext("nft flush table ip netrunner", true)?;
run_cmd_ext( run_cmd_ext(
@@ -95,7 +91,6 @@ pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
); );
run_cmd_ext(&dns_redir, false)?; run_cmd_ext(&dns_redir, false)?;
// 4. DNS (resolvectl не знает 'metric', используем стандартный синтаксис)
let _ = run_cmd_ext("resolvectl dns netr0 10.0.0.2", true); let _ = run_cmd_ext("resolvectl dns netr0 10.0.0.2", true);
let _ = run_cmd_ext("resolvectl domain netr0 ~.", true); let _ = run_cmd_ext("resolvectl domain netr0 ~.", true);
@@ -106,17 +101,14 @@ pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
{ {
let gateway = get_default_gateway().unwrap_or_else(|| "192.168.1.1".to_string()); let gateway = get_default_gateway().unwrap_or_else(|| "192.168.1.1".to_string());
// 1. Устанавливаем IP для адаптера
let _ = run_cmd_ext( let _ = run_cmd_ext(
"netsh interface ipv4 set address name=\"netr0\" static 10.0.0.1 255.255.255.0 none", "netsh interface ipv4 set address name=\"netr0\" static 10.0.0.1 255.255.255.0 none",
true, true,
); );
// 2. Ждем и получаем индекс именно нашего туннеля
let tun_idx = get_adapter_index("netr0") let tun_idx = get_adapter_index("netr0")
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Interface netr0 not found"))?; .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Interface netr0 not found"))?;
// 3. Маршрут-исключение для прокси через физический шлюз
let _ = run_cmd_ext( let _ = run_cmd_ext(
&format!( &format!(
"route add {} mask 255.255.255.255 {} metric 1", "route add {} mask 255.255.255.255 {} metric 1",
@@ -125,7 +117,6 @@ pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
true, true,
); );
// 4. Глобальные маршруты через TUN (теперь с правильным if_idx)
run_cmd_ext( run_cmd_ext(
&format!( &format!(
"route add 0.0.0.0 mask 128.0.0.0 10.0.0.2 if {} metric 5", "route add 0.0.0.0 mask 128.0.0.0 10.0.0.2 if {} metric 5",
@@ -141,7 +132,6 @@ pub fn setup_platform_routing(remote_address: &str) -> io::Result<()> {
true, true,
)?; )?;
// 5. DNS
let _ = run_cmd_ext( let _ = run_cmd_ext(
"netsh interface ipv4 set dnsservers name=\"netr0\" static 10.0.0.2 primary", "netsh interface ipv4 set dnsservers name=\"netr0\" static 10.0.0.2 primary",
true, true,
@@ -172,7 +162,6 @@ pub fn reset_platform_routing(proxy_ip: Option<&str>) -> io::Result<()> {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
// Очищаем глобальные маршруты, иначе интернет не вернется
let _ = run_cmd_ext("route delete 0.0.0.0 mask 128.0.0.0", true); let _ = run_cmd_ext("route delete 0.0.0.0 mask 128.0.0.0", true);
let _ = run_cmd_ext("route delete 128.0.0.0 mask 128.0.0.0", true); let _ = run_cmd_ext("route delete 128.0.0.0 mask 128.0.0.0", true);
@@ -180,7 +169,6 @@ pub fn reset_platform_routing(proxy_ip: Option<&str>) -> io::Result<()> {
let _ = run_cmd_ext(&format!("route delete {}", ip), true); let _ = run_cmd_ext(&format!("route delete {}", ip), true);
} }
// Сбрасываем DNS на автоматический (или удаляем наш)
let _ = run_cmd_ext( let _ = run_cmd_ext(
"netsh interface ipv4 set dnsservers name=\"netr0\" source=dhcp", "netsh interface ipv4 set dnsservers name=\"netr0\" source=dhcp",
true, true,
+1 -2
View File
@@ -56,7 +56,7 @@ pub struct SessionKeys {
pub ecdh: ECDH, pub ecdh: ECDH,
pub auth_key: [u8; 32], pub auth_key: [u8; 32],
pub current_aead: Option<([u8; 32], [u8; 12], [u8; 32], [u8; 12])>, pub current_aead: Option<([u8; 32], [u8; 12], [u8; 32], [u8; 12])>,
is_initiator: bool, // Сохраним роль для правильного возврата параметров is_initiator: bool,
} }
impl SessionKeys { impl SessionKeys {
@@ -159,7 +159,6 @@ impl SessionKeys {
self.auth_key = HKDF::expand_key::<32>(&hkdf, b"auth_key").map_err(|e| e.to_string())?; self.auth_key = HKDF::expand_key::<32>(&hkdf, b"auth_key").map_err(|e| e.to_string())?;
// Определяем порядок: (Write Key, Write IV, Read Key, Read IV)
let keys = if is_server { let keys = if is_server {
(s_key, s_iv, c_key, c_iv) (s_key, s_iv, c_key, c_iv)
} else { } else {
-4
View File
@@ -144,7 +144,6 @@ impl TlsBridge {
profile: &ServerProfile, profile: &ServerProfile,
) -> Result<Bytes, TlsError> { ) -> Result<Bytes, TlsError> {
if let HandshakeMessage::Client { base, extensions } = client_msg { if let HandshakeMessage::Client { base, extensions } = client_msg {
// 1. Проверка Auth Tag в Session ID (последние 16 байт)
if base.session_id.len() != 32 { if base.session_id.len() != 32 {
return Err(TlsError::new( return Err(TlsError::new(
ErrorStage::Handshake("Invalid SessionID len"), ErrorStage::Handshake("Invalid SessionID len"),
@@ -165,8 +164,6 @@ impl TlsBridge {
)); ));
} }
// 2. Выполняем Key Exchange (находим KeyShare клиента и считаем Shared Secret)
// Это обновит внутреннее состояние keys (auth_key и AEAD ключи)
keys.update_keys(base.random, extensions, true) keys.update_keys(base.random, extensions, true)
.map_err(|e| { .map_err(|e| {
netrunner_logger::error!(error = %e, "Server failed key update"); netrunner_logger::error!(error = %e, "Server failed key update");
@@ -177,7 +174,6 @@ impl TlsBridge {
) )
})?; })?;
// 3. Генерируем ServerHello, используя наш свежий публичный ключ и локальную соль
let server_pub_key = keys.ecdh.public_key.to_bytes(); let server_pub_key = keys.ecdh.public_key.to_bytes();
Ok(ServerHello::make_server_hello( Ok(ServerHello::make_server_hello(
-4
View File
@@ -46,16 +46,12 @@ impl Codec {
) )
})?; })?;
// ВАЖНО: TlsBridge сам проверит Auth Tag в Session ID клиента
// и вызовет update_keys для генерации общего секрета.
let server_hello_record = TlsBridge::wrap_server_hello( let server_hello_record = TlsBridge::wrap_server_hello(
&client_msg, &client_msg,
&mut self.session_keys, &mut self.session_keys,
&ServerProfile::MODERN, &ServerProfile::MODERN,
)?; )?;
// Ключи уже обновлены внутри session_keys.
// Просто забираем их и устанавливаем в AEAD шифратор.
let (w_key, w_iv, r_key, r_iv) = self.session_keys.get_aead_parameters(); let (w_key, w_iv, r_key, r_iv) = self.session_keys.get_aead_parameters();
self.crypto.set_keys(w_key, w_iv, r_key, r_iv); self.crypto.set_keys(w_key, w_iv, r_key, r_iv);
+2 -9
View File
@@ -115,29 +115,25 @@ impl Parser for ClientHello {
fn can_parse(bytes: &BytesMut) -> bool { fn can_parse(bytes: &BytesMut) -> bool {
let mut reader = &bytes[..]; let mut reader = &bytes[..];
// 1. Version (2) + Random (32) + SessionID Len (1) = 35
if reader.len() < 35 { if reader.len() < 35 {
return false; return false;
} }
reader.advance(34); reader.advance(34);
// 2. Session ID
let sid_len = reader[0] as usize; let sid_len = reader[0] as usize;
reader.advance(1); reader.advance(1);
if reader.len() < sid_len + 2 { if reader.len() < sid_len + 2 {
return false; return false;
} // +2 для Cipher Suites Len }
reader.advance(sid_len); reader.advance(sid_len);
// 3. Cipher Suites
let ciphers_len = u16::from_be_bytes([reader[0], reader[1]]) as usize; let ciphers_len = u16::from_be_bytes([reader[0], reader[1]]) as usize;
reader.advance(2); reader.advance(2);
if reader.len() < ciphers_len + 1 { if reader.len() < ciphers_len + 1 {
return false; return false;
} // +1 для Compression Len }
reader.advance(ciphers_len); reader.advance(ciphers_len);
// 4. Compression Methods
let comp_len = reader[0] as usize; let comp_len = reader[0] as usize;
reader.advance(1); reader.advance(1);
if reader.len() < comp_len { if reader.len() < comp_len {
@@ -145,7 +141,6 @@ impl Parser for ClientHello {
} }
reader.advance(comp_len); reader.advance(comp_len);
// 5. Extensions (опционально в TLS, но обычно есть)
if reader.len() >= 2 { if reader.len() >= 2 {
let ext_len = u16::from_be_bytes([reader[0], reader[1]]) as usize; let ext_len = u16::from_be_bytes([reader[0], reader[1]]) as usize;
reader.advance(2); reader.advance(2);
@@ -158,8 +153,6 @@ impl Parser for ClientHello {
} }
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> { fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
// Мы уже проверили всё в can_parse, поэтому здесь просто
// последовательно забираем данные через get_*
if !Self::can_parse(bytes) { if !Self::can_parse(bytes) {
return Ok(None); return Ok(None);
} }
+1 -9
View File
@@ -64,7 +64,7 @@ impl Connection {
pub fn new_raw(inbound: OwnedReadHalf, outbound: OwnedWriteHalf) -> Self { pub fn new_raw(inbound: OwnedReadHalf, outbound: OwnedWriteHalf) -> Self {
Self { Self {
addr: "0.0.0.0:0".parse().unwrap(), // заглушка, если адрес не важен addr: "0.0.0.0:0".parse().unwrap(),
inbound, inbound,
outbound, outbound,
read_buf: BytesMut::with_capacity(BUF_SIZE), read_buf: BytesMut::with_capacity(BUF_SIZE),
@@ -105,7 +105,6 @@ pub struct ClientHandler {
pub muxer: Muxer, pub muxer: Muxer,
} }
// Внутри connection.rs или где у тебя ClientHandler
impl ClientHandler { impl ClientHandler {
pub async fn connect( pub async fn connect(
remote_proxy_addr: &str, remote_proxy_addr: &str,
@@ -118,7 +117,6 @@ impl ClientHandler {
let mut conn = Connection::new_raw(inbound, outbound); let mut conn = Connection::new_raw(inbound, outbound);
// TLS Handshake
let ch = conn let ch = conn
.codec .codec
.make_client_handshake(&BrowserProfile::CHROME_131, "google.com") .make_client_handshake(&BrowserProfile::CHROME_131, "google.com")
@@ -177,7 +175,6 @@ impl ClientHandler {
let mut buf = [0u8; 1024]; let mut buf = [0u8; 1024];
loop { loop {
// Просто ждем, пока клиент не разорвет TCP-соединение
if self if self
.conn .conn
.inbound .inbound
@@ -198,17 +195,14 @@ impl TunnelHandler for ClientHandler {
async fn run(mut self) -> Result<(), String> { async fn run(mut self) -> Result<(), String> {
info!("Starting SOCKS multiplexed handling"); info!("Starting SOCKS multiplexed handling");
// 1. Приветствие (Handshake)
self.conn.read_socks_request().await?; self.conn.read_socks_request().await?;
self.conn self.conn
.send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 }) .send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 })
.await?; .await?;
// 2. Получаем основной запрос (Connect или UDP Associate)
let req = self.conn.read_socks_request().await?; let req = self.conn.read_socks_request().await?;
match req { match req {
// Ветка TCP CONNECT
SocksRequest::Connect { SocksRequest::Connect {
command: 0x01, command: 0x01,
target, target,
@@ -253,13 +247,11 @@ impl TunnelHandler for ClientHandler {
Ok(()) Ok(())
} }
// Ветка UDP ASSOCIATE
SocksRequest::Connect { command: 0x03, .. } => { SocksRequest::Connect { command: 0x03, .. } => {
info!("Handling UDP Associate request"); info!("Handling UDP Associate request");
self.handle_udp_associate().await self.handle_udp_associate().await
} }
// Всё остальное (BIND и т.д.)
_ => Err("Unsupported SOCKS command".into()), _ => Err("Unsupported SOCKS command".into()),
} }
} }
+3 -5
View File
@@ -35,7 +35,6 @@ impl StreamHandler {
let target_str = String::from_utf8_lossy(&payload).to_string(); let target_str = String::from_utf8_lossy(&payload).to_string();
let muxer = self.muxer.clone(); let muxer = self.muxer.clone();
// Канал для передачи данных из мультиплексора в мост (bridge)
let (v_tx, v_rx) = tokio::sync::mpsc::channel(512); let (v_tx, v_rx) = tokio::sync::mpsc::channel(512);
muxer.register_stream(stream_id, v_tx).await; muxer.register_stream(stream_id, v_tx).await;
@@ -43,7 +42,6 @@ impl StreamHandler {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
info!(stream_id, target = %target_str, "Attempting remote connection"); info!(stream_id, target = %target_str, "Attempting remote connection");
// Обертываем коннект в таймаут, чтобы не плодить зомби-таски
let connect_timeout = tokio::time::timeout( let connect_timeout = tokio::time::timeout(
std::time::Duration::from_secs(5), std::time::Duration::from_secs(5),
tokio::net::TcpStream::connect(&target_str), tokio::net::TcpStream::connect(&target_str),
@@ -62,7 +60,7 @@ impl StreamHandler {
let mut reply_buf = BytesMut::with_capacity(10); let mut reply_buf = BytesMut::with_capacity(10);
let reply = SocksReply::ConnectResult { let reply = SocksReply::ConnectResult {
reply_code: 0x00, // Success reply_code: 0x00,
atyp: 0x01, atyp: 0x01,
addr: [0, 0, 0, 0], addr: [0, 0, 0, 0],
port: 0, port: 0,
@@ -78,11 +76,11 @@ impl StreamHandler {
} }
Ok(Err(e)) => { Ok(Err(e)) => {
error!(stream_id, target = %target_str, error = %e, "TCP connection failed"); error!(stream_id, target = %target_str, error = %e, "TCP connection failed");
Self::send_error_reply(&muxer, stream_id, 0x01).await; // 0x01 = General failure Self::send_error_reply(&muxer, stream_id, 0x01).await;
} }
Err(_) => { Err(_) => {
error!(stream_id, target = %target_str, "Connection timed out (DNS/TCP)"); error!(stream_id, target = %target_str, "Connection timed out (DNS/TCP)");
Self::send_error_reply(&muxer, stream_id, 0x04).await; // 0x04 = Host unreachable Self::send_error_reply(&muxer, stream_id, 0x04).await;
} }
} }
}); });
+1 -2
View File
@@ -260,14 +260,13 @@ impl ExtensionBuilder {
TlsExtensions::RENEGOTIATION_INFO => self.renegotiation_info(), TlsExtensions::RENEGOTIATION_INFO => self.renegotiation_info(),
TlsExtensions::PADDING => { TlsExtensions::PADDING => {
if profile.target_padding_len > 0 { if profile.target_padding_len > 0 {
// Передаем накопленный оверхед всего пакета
self.padding(profile.target_padding_len as usize, overhead); self.padding(profile.target_padding_len as usize, overhead);
} }
} }
id if TlsExtensions::is_grease(id) => { id if TlsExtensions::is_grease(id) => {
if profile.has_grease { if profile.has_grease {
self.grease_with_id(id); // Используем конкретный ID из ExtensionOrder self.grease_with_id(id);
} }
} }
_ => self.apply_generic_extension(ext_id, profile), _ => self.apply_generic_extension(ext_id, profile),
+7 -30
View File
@@ -36,36 +36,29 @@ impl ClientHello {
buf.put_u8(HANDSHAKE_TYPE_CLIENT_HELLO); buf.put_u8(HANDSHAKE_TYPE_CLIENT_HELLO);
// Резервируем 3 байта под длину Handshake (U24)
let length_pos = buf.len(); let length_pos = buf.len();
buf.put_bytes(0, 3); buf.put_bytes(0, 3);
buf.put_u16(0x0303); // Legacy Version buf.put_u16(0x0303);
buf.put_slice(&self.random); buf.put_slice(&self.random);
// Session ID
buf.put_u8(self.session_id.len() as u8); buf.put_u8(self.session_id.len() as u8);
buf.put_slice(&self.session_id); buf.put_slice(&self.session_id);
// Cipher Suites
buf.put_u16((self.cipher_suites.len() * 2) as u16); buf.put_u16((self.cipher_suites.len() * 2) as u16);
for &suite in &self.cipher_suites { for &suite in &self.cipher_suites {
buf.put_u16(suite); buf.put_u16(suite);
} }
// Compression Methods (всегда 0x01 0x00 для маскировки)
buf.put_u8(1); buf.put_u8(1);
buf.put_u8(0x00); buf.put_u8(0x00);
// Extensions
buf.put_u16(self.extensions.len() as u16); buf.put_u16(self.extensions.len() as u16);
buf.put_slice(&self.extensions); buf.put_slice(&self.extensions);
// ПРАВИЛЬНЫЙ РАСЧЕТ ДЛИНЫ:
// Длина сообщения Handshake не включает сам тип (1 байт) и поле длины (3 байта)
let total_len = (buf.len() - length_pos - 3) as u32; let total_len = (buf.len() - length_pos - 3) as u32;
let len_bytes = total_len.to_be_bytes(); let len_bytes = total_len.to_be_bytes();
// Записываем 3 байта длины (Big Endian)
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]); buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
buf.freeze() buf.freeze()
@@ -126,14 +119,12 @@ impl ServerHello {
salt: [u8; 32], salt: [u8; 32],
profile: &ServerProfile, profile: &ServerProfile,
) -> Bytes { ) -> Bytes {
// Создаем инстанс ServerHello
let server_hello = Self::from_client_hello(client_hello, server_public_key, salt, profile); let server_hello = Self::from_client_hello(client_hello, server_public_key, salt, profile);
// Оборачиваем в Record Layer
let record = TlsRecord::new( let record = TlsRecord::new(
ContentType::Handshake, ContentType::Handshake,
profile.record_layer_version, // 0x0303 обычно profile.record_layer_version,
server_hello.serialize(), // Сериализация самого SH server_hello.serialize(),
); );
record.serialize() record.serialize()
@@ -145,10 +136,8 @@ impl ServerHello {
salt: [u8; 32], salt: [u8; 32],
profile: &ServerProfile, profile: &ServerProfile,
) -> Self { ) -> Self {
// 1. Используем соль как рандом сервера
let server_random = salt; let server_random = salt;
// 2. Выбор Cipher Suite (твой код здесь хорош, оставляем)
let selected_suite = if profile.honor_cipher_order { let selected_suite = if profile.honor_cipher_order {
profile profile
.cipher_suites .cipher_suites
@@ -167,27 +156,22 @@ impl ServerHello {
let mut extensions = BytesMut::new(); let mut extensions = BytesMut::new();
// 3. Работа с версией.
// Выбираем макс. версию из профиля (например, TLS 1.3)
let selected_version = profile.versions.max(); let selected_version = profile.versions.max();
// Добавляем расширение Supported Versions (обязательно для TLS 1.3)
extensions.put_u16(0x002b); extensions.put_u16(0x002b);
extensions.put_u16(2); extensions.put_u16(2);
extensions.put_u16(selected_version as u16); extensions.put_u16(selected_version as u16);
// 4. Key Share (исправляем расчет длины, чтобы не было как в прошлый раз)
let key_len = server_public_key.len() as u16; let key_len = server_public_key.len() as u16;
extensions.put_u16(0x0033); extensions.put_u16(0x0033);
extensions.put_u16(key_len + 4); // Группа(2) + Длина(2) + Ключ extensions.put_u16(key_len + 4);
extensions.put_u16(0x001d); // x25519 extensions.put_u16(0x001d);
extensions.put_u16(key_len); extensions.put_u16(key_len);
extensions.put_slice(server_public_key); extensions.put_slice(server_public_key);
Self { Self {
// Это поле будет использоваться как legacy_version (0x0303)
version: ProtocolVersion::Tls12, version: ProtocolVersion::Tls12,
random: server_random, // ИСПОЛЬЗУЕМ переменную random: server_random,
session_id: client_hello.session_id.clone(), session_id: client_hello.session_id.clone(),
cipher_suite: selected_suite, cipher_suite: selected_suite,
extensions, extensions,
@@ -199,26 +183,20 @@ impl ServerHello {
buf.put_u8(HANDSHAKE_TYPE_SERVER_HELLO); buf.put_u8(HANDSHAKE_TYPE_SERVER_HELLO);
// Резервируем место под длину Handshake (3 байта)
let length_pos = buf.len(); let length_pos = buf.len();
buf.put_slice(&[0, 0, 0]); buf.put_slice(&[0, 0, 0]);
// Используем версию из структуры (Legacy Version)
buf.put_u16(self.version as u16); buf.put_u16(self.version as u16);
// Используем рандом из структуры
buf.put_slice(&self.random); buf.put_slice(&self.random);
// Session ID (Echo)
buf.put_u8(self.session_id.len() as u8); buf.put_u8(self.session_id.len() as u8);
buf.put_slice(&self.session_id); buf.put_slice(&self.session_id);
buf.put_u16(self.cipher_suite); buf.put_u16(self.cipher_suite);
// Compression (0x00)
buf.put_u8(0x00); buf.put_u8(0x00);
// Extensions
if !self.extensions.is_empty() { if !self.extensions.is_empty() {
buf.put_u16(self.extensions.len() as u16); buf.put_u16(self.extensions.len() as u16);
buf.put_slice(&self.extensions); buf.put_slice(&self.extensions);
@@ -226,7 +204,6 @@ impl ServerHello {
buf.put_u16(0); buf.put_u16(0);
} }
// Заполняем длину handshake-сообщения
let total_handshake_body_len = (buf.len() - length_pos - 3) as u32; let total_handshake_body_len = (buf.len() - length_pos - 3) as u32;
let len_bytes = total_handshake_body_len.to_be_bytes(); let len_bytes = total_handshake_body_len.to_be_bytes();
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]); buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
+8 -18
View File
@@ -25,49 +25,41 @@ impl BrowserProfile {
delegated_signatures: TlsSignatures::BROWSER_STANDARD, delegated_signatures: TlsSignatures::BROWSER_STANDARD,
versions: TlsVersions::TLS_13_ONLY, versions: TlsVersions::TLS_13_ONLY,
// 0x0301 (TLS 1.0) — классика для ClientHello в исполнении Chrome
record_layer_version: ProtocolVersion::Tls10, record_layer_version: ProtocolVersion::Tls10,
cipher_suites: &[ cipher_suites: &[
0x1301, 0x1302, 0x1303, // TLS 1.3 Suites 0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0xc030, 0xcca9, 0xcca8,
0xc02b, 0xc02f, 0xc02c, 0xc030, // ECDHE-RSA/ECDSA
0xcca9, 0xcca8, // ChaCha20
], ],
alpn: &["h2", "http/1.1"], alpn: &["h2", "http/1.1"],
extension_order: ExtensionOrder::CHROMIUM_131, extension_order: ExtensionOrder::CHROMIUM_131,
// --- НОВЫЕ ПОЛЯ ДЛЯ ГИБКОСТИ ---
is_chromium: true, is_chromium: true,
has_grease: true, has_grease: true,
// Chrome активно использует ALPS для ускорения HTTP/2 настроек
alps_protocols: &["h2"], alps_protocols: &["h2"],
// Chrome старается сделать ClientHello "пухлым",
// чтобы избежать проблем с некоторыми старыми Middleboxes
target_padding_len: 512, target_padding_len: 512,
}; };
pub const FIREFOX_130: Self = Self { pub const FIREFOX_130: Self = Self {
name: "Firefox 130 (Windows)", name: "Firefox 130 (Windows)",
groups: TlsGroups::MODERN, // У FF свой набор предпочтений groups: TlsGroups::MODERN,
signatures: TlsSignatures::BROWSER_STANDARD, signatures: TlsSignatures::BROWSER_STANDARD,
delegated_signatures: TlsSignatures::BROWSER_STANDARD, delegated_signatures: TlsSignatures::BROWSER_STANDARD,
versions: TlsVersions::MODERN, // FF часто шлет 1.3 + 1.2 versions: TlsVersions::MODERN,
// Firefox обычно пишет 0x0303 (TLS 1.2) в Record Layer
record_layer_version: ProtocolVersion::Tls12, record_layer_version: ProtocolVersion::Tls12,
cipher_suites: &[0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0xc030], cipher_suites: &[0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0xc030],
alpn: &["h2", "http/1.1"], alpn: &["h2", "http/1.1"],
extension_order: ExtensionOrder::EDGE_130, // Тут стоит подставить личный порядок FF extension_order: ExtensionOrder::EDGE_130,
is_chromium: false, is_chromium: false,
has_grease: false, // Firefox НЕ использует GREASE has_grease: false,
alps_protocols: &[], // Firefox пока не так активно внедряет ALPS alps_protocols: &[],
target_padding_len: 0, // Firefox обычно не делает принудительный Padding до 512 target_padding_len: 0,
}; };
} }
@@ -75,8 +67,7 @@ pub struct ServerProfile {
pub name: &'static str, pub name: &'static str,
pub versions: TlsVersions, pub versions: TlsVersions,
// ДОБАВЛЯЕМ: pub record_layer_version: ProtocolVersion,
pub record_layer_version: ProtocolVersion, // Что сервер пишет в ответном Record
pub cipher_suites: &'static [u16], pub cipher_suites: &'static [u16],
pub groups: TlsGroups, pub groups: TlsGroups,
@@ -91,7 +82,6 @@ impl ServerProfile {
name: "Modern-Secure", name: "Modern-Secure",
versions: TlsVersions::MODERN, versions: TlsVersions::MODERN,
// Сервера обычно отвечают 0x0303 (TLS 1.2) в Record Layer
record_layer_version: ProtocolVersion::Tls12, record_layer_version: ProtocolVersion::Tls12,
cipher_suites: &[0x1301, 0x1302, 0x1303], cipher_suites: &[0x1301, 0x1302, 0x1303],
+1 -1
View File
@@ -146,7 +146,7 @@ impl TlsExtensions {
if (id & 0x0f0f) != 0x0a0a { if (id & 0x0f0f) != 0x0a0a {
return false; return false;
} }
// Убеждаемся, что оба байта идентичны (0x1A1A, а не 0x1A2A)
(id & 0xff) == (id >> 8) (id & 0xff) == (id >> 8)
} }
} }
-1
View File
@@ -37,7 +37,6 @@ impl Logger {
registry.with(fmt_layer) registry.with(fmt_layer)
}; };
// 4. Инициализируем подписчик
registry.init(); registry.init();
let _ = LOGGER.set(Logger { let _ = LOGGER.set(Logger {