buf sizes changes and socks5 remove to rawcast

This commit is contained in:
2026-03-26 15:50:41 +07:00
parent c51c086fbc
commit b0dc3fc0a4
13 changed files with 641 additions and 945 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
/target /target
things.txt things.txt
host_cache.txt hosts_cache.txt
/gen /gen
+9 -38
View File
@@ -1,3 +1,4 @@
use netrunner_core::net::network::NetworkConfig;
use uniffi; use uniffi;
uniffi::setup_scaffolding!(); uniffi::setup_scaffolding!();
@@ -9,8 +10,6 @@ use crate::{
net::engine::{EngineBuilder, EngineConfig}, net::engine::{EngineBuilder, EngineConfig},
tun::routing::reset_platform_routing, tun::routing::reset_platform_routing,
}; };
use netrunner_core::net::ConnectionRole;
use netrunner_core::net::network::Network;
use netrunner_logger::{error, info}; use netrunner_logger::{error, info};
use std::sync::{Arc, OnceLock}; use std::sync::{Arc, OnceLock};
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
@@ -88,40 +87,9 @@ impl SessionManager {
config = config.with_mtu(1350); config = config.with_mtu(1350);
} }
// --- 1. ЗАПУСК ЛОКАЛЬНОГО ПРОКСИ (NETWORK) --- NetworkConfig::init_global(config.mtu);
let local_proxy_port = 8080;
let local_proxy_host = "127.0.0.1".to_string();
let proxy_token = cancel_token.clone();
let remote_addr_clone = remote_address.clone();
runtime.spawn(async move { // --- ЗАПУСК ENGINE И TUN ---
info!(
"Starting Local Proxy (Network) on {}:{}",
local_proxy_host, local_proxy_port
);
let network = Network::new(
local_proxy_host,
local_proxy_port,
ConnectionRole::Client,
Some(remote_addr_clone),
);
// Оборачиваем в tokio::select! для жесткой отмены
tokio::select! {
_ = network.run(proxy_token.clone()) => {
info!("Local Proxy (Network) task finished normally.");
}
_ = proxy_token.cancelled() => {
info!("Local Proxy (Network) task forcefully stopped via CancellationToken.");
}
}
});
// Даем прокси немного времени на бинд порта
std::thread::sleep(std::time::Duration::from_millis(100));
// --- 2. ЗАПУСК ENGINE И TUN ---
let engine_token = cancel_token.clone(); let engine_token = cancel_token.clone();
runtime.spawn(async move { runtime.spawn(async move {
info!("Starting VPN Engine thread..."); info!("Starting VPN Engine thread...");
@@ -160,10 +128,13 @@ impl SessionManager {
match builder_result { match builder_result {
Ok((mut engine, tun)) => { Ok((mut engine, tun)) => {
info!("Engine async task started"); info!("Engine built successfully, starting loop...");
// tokio::select! позволяет моментально прервать бесконечный цикл
// engine.run() при вызове session.stop()
tokio::select! { tokio::select! {
res = engine.run(tun) => { _ = engine.run(tun) => {
info!("Engine loop finished: {:?}", res); info!("Engine loop finished normally.");
}, },
_ = engine_token.cancelled() => { _ = engine_token.cancelled() => {
info!("Engine task shutting down via token"); info!("Engine task shutting down via token");
+8 -40
View File
@@ -6,9 +6,8 @@ mod tun;
use crate::tun::{routing::reset_platform_routing, tun::Tun}; use crate::tun::{routing::reset_platform_routing, tun::Tun};
use net::engine::{EngineBuilder, EngineConfig}; use net::engine::{EngineBuilder, EngineConfig};
// Импортируем компоненты локального прокси // Импортируем глобальный конфиг сети
use netrunner_core::net::ConnectionRole; use netrunner_core::net::network::NetworkConfig;
use netrunner_core::net::network::Network;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
#[tokio::main] #[tokio::main]
@@ -17,45 +16,18 @@ async fn main() -> anyhow::Result<()> {
info!("Initializing NetRunner Stack..."); info!("Initializing NetRunner Stack...");
let remote_address = "147.45.43.70:443".to_string(); let remote_address = "147.45.43.70:443".to_string();
let local_proxy_host = "127.0.0.1".to_string();
let local_proxy_port = 8080; // Локальный порт прокси
// Токен для управления жизненным циклом фоновых задач
let cancel_token = CancellationToken::new(); let cancel_token = CancellationToken::new();
// ================================================== // ==================================================
// 1. ЗАПУСК ЛОКАЛЬНОГО ПРОКСИ (NETWORK) // 1. ИНИЦИАЛИЗАЦИЯ ДВИЖКА И TUN
// ==================================================
let proxy_token = cancel_token.clone();
let remote_addr_clone = remote_address.clone();
tokio::spawn(async move {
info!(
"Starting Local Proxy (Network) on {}:{}",
local_proxy_host, local_proxy_port
);
let network = Network::new(
local_proxy_host,
local_proxy_port,
ConnectionRole::Client,
Some(remote_addr_clone),
);
// Эта функция заблокирует поток, пока не сработает proxy_token
network.run(proxy_token).await;
info!("Local Proxy (Network) task stopped.");
});
// Даем локальному прокси немного времени на бинд порта и установку соединения
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
// ==================================================
// 2. ИНИЦИАЛИЗАЦИЯ ДВИЖКА И TUN
// ================================================== // ==================================================
let config = EngineConfig::new(&remote_address) let config = EngineConfig::new(&remote_address)
.with_cache_path(".") .with_cache_path(".")
.with_mtu(1350); .with_mtu(1350);
// ВАЖНО: Инициализируем глобальные настройки сети (MTU, размеры буферов Muxer'а)
NetworkConfig::init_global(config.mtu);
let tun_device = Tun::create(|tun_cfg| { let tun_device = Tun::create(|tun_cfg| {
tun_cfg tun_cfg
.tun_name("netr0") .tun_name("netr0")
@@ -75,7 +47,7 @@ async fn main() -> anyhow::Result<()> {
.await; .await;
// ================================================== // ==================================================
// 3. ГЛАВНЫЙ ЦИКЛ ENGINE // 2. ГЛАВНЫЙ ЦИКЛ ENGINE
// ================================================== // ==================================================
match builder_result { match builder_result {
Ok((mut engine, tun)) => { Ok((mut engine, tun)) => {
@@ -87,7 +59,6 @@ async fn main() -> anyhow::Result<()> {
}, },
_ = tokio::signal::ctrl_c() => { _ = tokio::signal::ctrl_c() => {
info!("Ctrl+C received, shutting down..."); info!("Ctrl+C received, shutting down...");
// Отменяем токен, чтобы Network.run завершился
cancel_token.cancel(); cancel_token.cancel();
} }
} }
@@ -99,7 +70,7 @@ async fn main() -> anyhow::Result<()> {
} }
// ================================================== // ==================================================
// 4. ОЧИСТКА РОУТИНГА // 3. ОЧИСТКА РОУТИНГА
// ================================================== // ==================================================
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");
@@ -111,8 +82,5 @@ async fn main() -> anyhow::Result<()> {
info!("System routing restored successfully."); info!("System routing restored successfully.");
} }
// Даем таске Network время на graceful shutdown (чтобы сокеты успели закрыться)
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
Ok(()) Ok(())
} }
+51 -69
View File
@@ -29,9 +29,9 @@ impl ConnectionCore {
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) { pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) {
trace!(%handle, "Creating ConnectionCore channels"); trace!(%handle, "Creating ConnectionCore channels");
let (tx_to_net, rx_from_smol) = let (tx_to_net, rx_from_smol) =
mpsc::channel::<Bytes>(NetworkConfig::global().channel_capacity); mpsc::channel::<Bytes>(NetworkConfig::global().stream_capacity);
let (tx_to_smol, rx_from_net) = let (tx_to_smol, rx_from_net) =
mpsc::channel::<Bytes>(NetworkConfig::global().channel_capacity); mpsc::channel::<Bytes>(NetworkConfig::global().stream_capacity);
let core = Self { let core = Self {
handle, handle,
@@ -42,7 +42,6 @@ impl ConnectionCore {
(core, rx_from_smol, tx_to_smol) (core, rx_from_smol, tx_to_smol)
} }
} }
// ============================================================================ // ============================================================================
// 2. TCP СОЕДИНЕНИЕ (TcpConnection) // 2. TCP СОЕДИНЕНИЕ (TcpConnection)
// ============================================================================ // ============================================================================
@@ -55,14 +54,13 @@ pub enum ConnectionState {
Closed, Closed,
} }
const MAX_PENDING: usize = 64 * 1024;
const TCP_CHUNK_SIZE: usize = 1024 * 16;
pub struct TcpConnection { pub struct TcpConnection {
core: ConnectionCore, core: ConnectionCore,
state: ConnectionState, state: ConnectionState,
pending_data: BytesMut, pending_data: BytesMut,
handshake_rx: Option<oneshot::Receiver<()>>, handshake_rx: Option<oneshot::Receiver<()>>,
chunk_buf: Vec<u8>,
server_eof: bool, // <--- ДОБАВЛЕН ФЛАГ ОКОНЧАНИЯ ПЕРЕДАЧИ
} }
impl TcpConnection { impl TcpConnection {
@@ -83,18 +81,14 @@ impl TcpConnection {
state: ConnectionState::Handshaking, state: ConnectionState::Handshaking,
pending_data: BytesMut::new(), pending_data: BytesMut::new(),
handshake_rx: Some(handshake_rx), handshake_rx: Some(handshake_rx),
chunk_buf: vec![0u8; NetworkConfig::global().tcp_chunk_size],
server_eof: false, // Инициализируем
}; };
(conn, rx_from_smol, tx_to_smol, handshake_tx) (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 tick(&mut self, socket: &mut tcp::Socket) -> bool { pub fn tick(&mut self, socket: &mut tcp::Socket) -> bool {
let state = socket.state();
match self.state { match self.state {
ConnectionState::Handshaking => { ConnectionState::Handshaking => {
if let Some(rx) = &mut self.handshake_rx { if let Some(rx) = &mut self.handshake_rx {
@@ -105,9 +99,8 @@ impl TcpConnection {
self.handshake_rx = None; self.handshake_rx = None;
return true; return true;
} }
Err(oneshot::error::TryRecvError::Empty) => return true, // Ждем Err(oneshot::error::TryRecvError::Empty) => return true,
Err(oneshot::error::TryRecvError::Closed) => { Err(oneshot::error::TryRecvError::Closed) => {
debug!(%self.core.handle, "TCP Handshake channel dropped/aborted, State -> Closed");
self.state = ConnectionState::Closed; self.state = ConnectionState::Closed;
return false; return false;
} }
@@ -120,19 +113,12 @@ impl TcpConnection {
ConnectionState::Active => { ConnectionState::Active => {
self.poll_and_process(socket); self.poll_and_process(socket);
if state == tcp::State::CloseWait { // Если сокет достиг финальных стадий, убиваем нашу сессию
debug!(%self.core.handle, "TCP Socket reached CloseWait state, closing"); if matches!(socket.state(), tcp::State::Closed | tcp::State::TimeWait) {
socket.close(); debug!(%self.core.handle, "TCP Socket is finished, state -> Closed");
self.state = ConnectionState::Closed; self.state = ConnectionState::Closed;
return false; return false;
} }
if self.is_finished(socket) {
debug!(%self.core.handle, "TCP Socket is finished (Closed/TimeWait), state -> Closed");
self.state = ConnectionState::Closed;
socket.close();
return false;
}
} }
ConnectionState::Closed => { ConnectionState::Closed => {
@@ -146,30 +132,29 @@ impl TcpConnection {
} }
fn poll_and_process(&mut self, socket: &mut tcp::Socket) { fn poll_and_process(&mut self, socket: &mut tcp::Socket) {
let max_pending = NetworkConfig::global().tcp_max_pending;
// 1. Вычитываем данные из smoltcp и шлем в Muxer // 1. Вычитываем данные из smoltcp и шлем в Muxer
while socket.can_recv() { while socket.can_recv() {
let mut full = false; let mut full = false;
let mut temp = [0u8; TCP_CHUNK_SIZE];
if let Ok(n) = socket.peek_slice(&mut temp) { if let Ok(n) = socket.peek_slice(&mut self.chunk_buf) {
if n == 0 { if n == 0 {
break; break;
} }
let chunk = Bytes::copy_from_slice(&temp[..n]); let chunk = Bytes::copy_from_slice(&self.chunk_buf[..n]);
match self.core.tx.try_send(chunk) { match self.core.tx.try_send(chunk) {
Ok(_) => { Ok(_) => {
trace!(%self.core.handle, "Forwarded {} bytes from smoltcp to Muxer", n); socket.recv_slice(&mut self.chunk_buf[..n]).unwrap();
socket.recv_slice(&mut temp[..n]).unwrap();
} }
Err(mpsc::error::TrySendError::Full(_)) => { Err(mpsc::error::TrySendError::Full(_)) => {
debug!(%self.core.handle, "Muxer TX channel full, backpressure applied to smoltcp read");
full = true; full = true;
} }
Err(_) => { Err(_) => {
debug!(%self.core.handle, "Muxer TX channel closed unexpectedly, state -> Closed"); // Канал Muxer'а закрыт
self.state = ConnectionState::Closed; self.server_eof = true;
return; break;
} }
} }
} else { } else {
@@ -181,60 +166,57 @@ impl TcpConnection {
} }
} }
// 2. Читаем данные из Muxer'а с учетом Backpressure // 2. Читаем данные из Muxer'а
let current_pending = self.pending_data.len(); if !self.server_eof {
let fill_ratio = (current_pending as f32 / MAX_PENDING as f32) * 100.0; loop {
match self.core.rx.try_recv() {
if current_pending >= MAX_PENDING { Ok(data) => {
warn!(
%self.core.handle,
"Backpressure ACTIVE: Buffer is FULL ({} bytes). Stalling RX channel.",
current_pending
);
} else if fill_ratio > 80.0 {
info!(
%self.core.handle,
"Bufferbloat Warning: Buffer {:.1}% full ({} bytes). Latency increasing.",
fill_ratio, current_pending
);
while let Ok(data) = self.core.rx.try_recv() {
trace!(%self.core.handle, "Received {} bytes from Muxer (high buffer)", data.len());
self.pending_data.extend_from_slice(&data); self.pending_data.extend_from_slice(&data);
if self.pending_data.len() >= MAX_PENDING { if self.pending_data.len() >= max_pending {
break; break;
} }
} }
} else { Err(mpsc::error::TryRecvError::Empty) => {
while let Ok(data) = self.core.rx.try_recv() { break;
trace!(%self.core.handle, "Received {} bytes from Muxer", data.len()); }
self.pending_data.extend_from_slice(&data); Err(mpsc::error::TryRecvError::Disconnected) => {
if self.pending_data.len() >= MAX_PENDING { // ВАЖНО: Удаленный сервер прислал EOF!
debug!(%self.core.handle, "Server sent EOF (channel disconnected).");
self.server_eof = true;
break; break;
} }
} }
} }
}
// 3. Отправляем буферизированные данные в smoltcp // 3. Пишем данные браузеру
if !self.pending_data.is_empty() && socket.can_send() { if !self.pending_data.is_empty() && socket.can_send() {
match socket.send_slice(&self.pending_data) { match socket.send_slice(&self.pending_data) {
Ok(n) => { Ok(n) => {
trace!(%self.core.handle, "Wrote {} bytes from buffer to smoltcp", n);
self.pending_data.advance(n); self.pending_data.advance(n);
if n > 0 && self.pending_data.len() < (MAX_PENDING / 2) && fill_ratio > 90.0 {
info!(
%self.core.handle,
"Backpressure RELIEVED: Buffer drained to {} bytes",
self.pending_data.len()
);
}
} }
Err(e) => { Err(e) => {
debug!(%self.core.handle, "Smoltcp socket send error: {:?}", e); debug!(%self.core.handle, "Smoltcp send error: {:?}", e);
} }
} }
} }
// 4. ГРАЦИОЗНОЕ ЗАКРЫТИЕ (Отправка FIN браузеру)
// Сценарий А: Сервер закрыл соединение, и мы отдали все остатки данных браузеру
if self.server_eof && self.pending_data.is_empty() && socket.may_send() {
debug!(%self.core.handle, "All data flushed after server EOF, sending FIN to browser");
socket.close();
}
// Сценарий Б: Браузер сам инициировал закрытие (CloseWait), но мы дожидаемся опустошения буфера
if socket.state() == tcp::State::CloseWait
&& self.pending_data.is_empty()
&& socket.may_send()
{
debug!(%self.core.handle, "Browser in CloseWait and buffer flushed, sending FIN");
socket.close();
}
} }
} }
+169 -153
View File
@@ -1,14 +1,16 @@
use bytes::Bytes; use bytes::Bytes;
use netrunner_core::nrxp::TargetAddress; use netrunner_core::{
use netrunner_logger::{debug, error, info, trace, warn}; net::network::NetworkConfig,
rawcast::{LocalProtocol, RawCastEvent, RawCastFrame},
};
use netrunner_logger::{debug, error, info, trace};
use smoltcp::{ use smoltcp::{
iface::{SocketHandle, SocketSet}, iface::{SocketHandle, SocketSet},
socket::{AnySocket, icmp, tcp, udp}, socket::{AnySocket, icmp, tcp, udp},
wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket}, wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket},
}; };
use std::{collections::HashMap, time::Instant as StdInstant}; use std::{collections::HashMap, time::Instant as StdInstant};
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::sync::mpsc;
use tokio::net::{TcpStream, UdpSocket};
use crate::net::{ use crate::net::{
connection::{TcpConnection, UdpConnection}, connection::{TcpConnection, UdpConnection},
@@ -19,13 +21,15 @@ use crate::net::{
// ============================================================================ // ============================================================================
// 1. УПРАВЛЕНИЕ СОСТОЯНИЕМ СЕССИЙ (SessionTracker) // 1. УПРАВЛЕНИЕ СОСТОЯНИЕМ СЕССИЙ (SessionTracker)
// ============================================================================ // ============================================================================
struct SessionTracker { struct SessionTracker {
last_activity: HashMap<SocketHandle, StdInstant>, last_activity: HashMap<SocketHandle, StdInstant>,
active_tcp: HashMap<SocketHandle, TcpConnection>, active_tcp: HashMap<SocketHandle, TcpConnection>,
active_udp: HashMap<SocketHandle, UdpConnection>, active_udp: HashMap<SocketHandle, UdpConnection>,
inbound_tx: HashMap<u64, mpsc::Sender<Bytes>>,
failed_until: HashMap<SocketHandle, StdInstant>, failed_until: HashMap<SocketHandle, StdInstant>,
to_remove: Vec<SocketHandle>, to_remove: Vec<SocketHandle>,
next_socket_id: u64,
handle_to_id: HashMap<SocketHandle, u64>,
} }
impl SessionTracker { impl SessionTracker {
@@ -34,11 +38,20 @@ impl SessionTracker {
last_activity: HashMap::new(), last_activity: HashMap::new(),
active_tcp: HashMap::new(), active_tcp: HashMap::new(),
active_udp: HashMap::new(), active_udp: HashMap::new(),
inbound_tx: HashMap::new(),
failed_until: HashMap::new(), failed_until: HashMap::new(),
to_remove: Vec::new(), to_remove: Vec::new(),
next_socket_id: 1,
handle_to_id: HashMap::new(),
} }
} }
fn generate_socket_id(&mut self) -> u64 {
let id = self.next_socket_id;
self.next_socket_id = self.next_socket_id.wrapping_add(1);
id
}
fn queue_removal(&mut self, handle: SocketHandle) { fn queue_removal(&mut self, handle: SocketHandle) {
if !self.to_remove.contains(&handle) { if !self.to_remove.contains(&handle) {
self.to_remove.push(handle); self.to_remove.push(handle);
@@ -53,19 +66,28 @@ impl SessionTracker {
self.failed_until.remove(&handle); self.failed_until.remove(&handle);
self.active_tcp.remove(&handle); self.active_tcp.remove(&handle);
self.active_udp.remove(&handle); self.active_udp.remove(&handle);
// ВОТ ТУТ ГЛАВНОЕ ИСПРАВЛЕНИЕ:
// Достаем НАШ socket_id, который мы выдали этому handle при старте сессии
if let Some(socket_id) = self.handle_to_id.remove(&handle) {
self.inbound_tx.remove(&socket_id);
}
} }
} }
fn has_tcp( fn has_connection_from(
&self, &self,
dst_addr: smoltcp::wire::IpAddress, src_addr: smoltcp::wire::IpAddress,
dst_port: u16, src_port: u16,
socket_set: &SocketSet, socket_set: &SocketSet,
) -> bool { ) -> bool {
socket_set.iter().any(|(_, s)| { socket_set.iter().any(|(_, s)| {
if let Some(tcp) = tcp::Socket::downcast(s) { if let Some(tcp) = tcp::Socket::downcast(s) {
if let Some(ep) = tcp.local_endpoint() { // Если сокет уже привязался к клиенту, проверяем его исходные данные
return ep.addr == dst_addr && ep.port == dst_port; if let Some(remote) = tcp.remote_endpoint() {
if remote.addr == src_addr && remote.port == src_port {
return true;
}
} }
} }
false false
@@ -105,30 +127,6 @@ impl TargetResolver {
} }
} }
fn resolve_tcp(&self, socket: &tcp::Socket) -> TargetAddress {
let ep = match socket.local_endpoint() {
Some(ep) => ep,
None => {
warn!(handle=?socket, "Target resolution failed: no local endpoint");
return TargetAddress::Domain("disconnected".to_string(), 0);
}
};
match ep.addr {
smoltcp::wire::IpAddress::Ipv4(ip) => {
let std_ip = std::net::Ipv4Addr::from(ip);
if let Some(domain) = self.fake_ip_store.lookup_by_ip(&std_ip) {
TargetAddress::Domain(domain, ep.port)
} else {
TargetAddress::Ipv4(std_ip, ep.port)
}
}
smoltcp::wire::IpAddress::Ipv6(ip) => {
TargetAddress::Ipv6(std::net::Ipv6Addr::from(ip), ep.port)
}
}
}
fn process_dns_query(&mut self, data: &[u8]) -> Option<Vec<u8>> { fn process_dns_query(&mut self, data: &[u8]) -> Option<Vec<u8>> {
self.dns_handler.handle_query(data, &mut self.fake_ip_store) self.dns_handler.handle_query(data, &mut self.fake_ip_store)
} }
@@ -137,17 +135,21 @@ impl TargetResolver {
// ============================================================================ // ============================================================================
// 3. ФАБРИКА СОКЕТОВ (SocketFactory) // 3. ФАБРИКА СОКЕТОВ (SocketFactory)
// ============================================================================ // ============================================================================
struct SocketFactory; struct SocketFactory;
impl SocketFactory { impl SocketFactory {
fn create_tcp<'a>(port: u16) -> tcp::Socket<'a> { fn create_tcp<'a>(port: u16) -> tcp::Socket<'a> {
let max_buf = NetworkConfig::global().smoltcp_socket_buf;
// Для Web-трафика используем максимум из конфига (например, 2 МБ)
// Для остальных урезаем в 4 раза, чтобы сэкономить RAM на фоновых соединениях
let buf_size = match port { let buf_size = match port {
443 | 80 => 1024 * 1024 * 2, 443 | 80 | 8080 => max_buf,
22 => 32 * 1024, 22 => 32 * 1024,
53 => 16 * 1024, 53 => 16 * 1024,
_ => 128 * 1024, _ => max_buf / 4,
}; };
let mut socket = tcp::Socket::new( let mut socket = tcp::Socket::new(
tcp::SocketBuffer::new(vec![0; buf_size]), tcp::SocketBuffer::new(vec![0; buf_size]),
tcp::SocketBuffer::new(vec![0; buf_size]), tcp::SocketBuffer::new(vec![0; buf_size]),
@@ -158,11 +160,20 @@ impl SocketFactory {
} }
fn create_udp<'a>(port: u16) -> udp::Socket<'a> { fn create_udp<'a>(port: u16) -> udp::Socket<'a> {
let config = NetworkConfig::global();
let max_buf = config.udp_buffer_size;
let payload_size = config.safe_payload_size.max(1); // Защита от деления на 0
// Вычисляем размер буфера и количество пакетов
let (buf_size, packet_count) = match port { let (buf_size, packet_count) = match port {
443 => (512 * 1024, 390), 443 => (max_buf, max_buf / payload_size), // QUIC/HTTP3 трафик
53 => (64 * 1024, 32), 53 => (64 * 1024, (64 * 1024) / payload_size), // DNS
_ => (128 * 1024, 100), _ => (max_buf / 2, (max_buf / 2) / payload_size),
}; };
// Гарантируем, что метаданных хватит хотя бы на 10 пакетов
let packet_count = packet_count.max(10);
udp::Socket::new( udp::Socket::new(
udp::PacketBuffer::new( udp::PacketBuffer::new(
vec![udp::PacketMetadata::EMPTY; packet_count], vec![udp::PacketMetadata::EMPTY; packet_count],
@@ -186,20 +197,45 @@ impl SocketFactory {
// ============================================================================ // ============================================================================
// 4. ГЛАВНЫЙ КООРДИНАТОР (ConnectionManager) // 4. ГЛАВНЫЙ КООРДИНАТОР (ConnectionManager)
// ============================================================================ // ============================================================================
pub struct ConnectionManager { pub struct ConnectionManager {
tracker: SessionTracker, tracker: SessionTracker,
resolver: TargetResolver, resolver: TargetResolver,
tx_to_tunnel: mpsc::Sender<RawCastFrame>,
} }
impl ConnectionManager { impl ConnectionManager {
pub fn new(dns_handler: DnsHandler) -> Self { pub fn new(dns_handler: DnsHandler, tx_to_tunnel: mpsc::Sender<RawCastFrame>) -> Self {
Self { Self {
tracker: SessionTracker::new(), tracker: SessionTracker::new(),
resolver: TargetResolver::new(dns_handler), resolver: TargetResolver::new(dns_handler),
tx_to_tunnel,
} }
} }
// ВАЖНО: Метод для получения данных из сети (от VPN) и инъекции их в smoltcp
// Пытается внедрить пакет. Если канал переполнен — возвращает пакет обратно!
pub fn try_inject_inbound(&mut self, frame: RawCastFrame) -> Result<(), RawCastFrame> {
if frame.event != RawCastEvent::Data {
if frame.event == RawCastEvent::Close {
self.tracker.inbound_tx.remove(&frame.socket_id);
}
return Ok(());
}
if let Some(tx) = self.tracker.inbound_tx.get(&frame.socket_id) {
match tx.try_send(frame.payload.clone()) {
Ok(_) => Ok(()),
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
Err(frame) // Возвращаем кадр, чтобы Engine затормозил чтение туннеля
}
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
Ok(()) // Сокет уже закрыт браузером, дропаем пакет
}
}
} else {
Ok(())
}
}
pub fn setup_sockets(n_icmp: usize) -> SocketSet<'static> { pub fn setup_sockets(n_icmp: usize) -> SocketSet<'static> {
let mut sockets = SocketSet::new(Vec::with_capacity(48)); let mut sockets = SocketSet::new(Vec::with_capacity(48));
for _ in 0..n_icmp { for _ in 0..n_icmp {
@@ -238,13 +274,23 @@ impl ConnectionManager {
match ip_packet.next_header() { match ip_packet.next_header() {
IpProtocol::Tcp => { IpProtocol::Tcp => {
if let Ok(tcp_packet) = TcpPacket::new_checked(ip_packet.payload()) { let src_addr = ip_packet.src_addr();
if tcp_packet.syn() && !tcp_packet.ack() { let dst_addr = ip_packet.dst_addr();
let dst_port = tcp_packet.dst_port();
trace!(%dst_addr, dst_port, "Received TCP SYN");
if !self.tracker.has_tcp(dst_addr.into(), dst_port, socket_set) { if let Ok(tcp_packet) = TcpPacket::new_checked(ip_packet.payload()) {
debug!(%dst_addr, dst_port, "No active TCP socket found, allocating new one"); // 2. Проверяем, что это пакет инициализации соединения (SYN)
if tcp_packet.syn() && !tcp_packet.ack() {
let src_port = tcp_packet.src_port();
let dst_port = tcp_packet.dst_port();
trace!(%dst_addr, dst_port, src_port, "Received TCP SYN");
// 3. Теперь src_addr доступен в этой области видимости
if !self
.tracker
.has_connection_from(src_addr.into(), src_port, socket_set)
{
debug!(%dst_addr, dst_port, src_port, "Allocating new TCP socket");
let mut socket = SocketFactory::create_tcp(dst_port); let mut socket = SocketFactory::create_tcp(dst_port);
let endpoint = IpListenEndpoint { let endpoint = IpListenEndpoint {
@@ -254,19 +300,16 @@ impl ConnectionManager {
match socket.listen(endpoint) { match socket.listen(endpoint) {
Ok(_) => { Ok(_) => {
debug!(%dst_addr, dst_port, "TCP socket successfully listening");
socket_set.add(socket); socket_set.add(socket);
} }
Err(e) => { Err(e) => {
error!(%dst_addr, dst_port, "Failed to listen on TCP socket: {:?}", e); error!(%dst_addr, dst_port, "Failed to listen: {:?}", e);
} }
} }
} else { } else {
trace!(%dst_addr, dst_port, "TCP socket already exists, ignoring SYN"); trace!(%dst_addr, dst_port, src_port, "Socket already exists, ignoring SYN");
} }
} }
} else {
trace!("try_create_socket: Failed to parse TCP payload");
} }
} }
IpProtocol::Udp => { IpProtocol::Udp => {
@@ -324,7 +367,6 @@ impl ConnectionManager {
} }
} }
} }
fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) { fn handle_tcp(&mut self, handle: SocketHandle, socket: &mut tcp::Socket) {
if socket.state() == tcp::State::Closed { if socket.state() == tcp::State::Closed {
self.tracker.active_tcp.remove(&handle); self.tracker.active_tcp.remove(&handle);
@@ -335,67 +377,63 @@ impl ConnectionManager {
if socket.state() == tcp::State::Established if socket.state() == tcp::State::Established
&& !self.tracker.active_tcp.contains_key(&handle) && !self.tracker.active_tcp.contains_key(&handle)
{ {
let target = self.resolver.resolve_tcp(socket); let (dst_ip, dst_port) = match socket.local_endpoint() {
if let TargetAddress::Domain(ref d, _) = target { Some(ep) => match ep.addr {
if d == "disconnected" { smoltcp::wire::IpAddress::Ipv4(ip) => (std::net::Ipv4Addr::from(ip), ep.port),
socket.abort(); _ => return,
return; },
} None => return,
} };
info!(%handle, target = %target, "New TCP session established"); info!(%handle, ip = %dst_ip, port = dst_port, "New TCP session intercepted");
let socket_id = self.tracker.generate_socket_id();
self.tracker.handle_to_id.insert(handle, socket_id);
let (conn, mut rx_from_smol, tx_to_smol, handshake_tx) = TcpConnection::new(handle); let (conn, mut rx_from_smol, tx_to_smol, handshake_tx) = TcpConnection::new(handle);
self.tracker.active_tcp.insert(handle, conn);
// Конвертируем TargetAddress в строку, понятную для tokio::net self.tracker.active_tcp.insert(handle, conn);
let target_str = match target { self.tracker.inbound_tx.insert(socket_id, tx_to_smol);
TargetAddress::Domain(d, p) => format!("{}:{}", d, p),
TargetAddress::Ipv4(ip, p) => format!("{}:{}", ip, p), // ИСПРАВЛЕНИЕ ЗДЕСЬ: Переводим IP обратно в домен
TargetAddress::Ipv6(ip, p) => format!("{}:{}", ip, p), let target_str = if let Some(domain) = self.resolver.fake_ip_store.lookup_by_ip(&dst_ip)
{
format!("{}:{}", domain, dst_port)
} else {
format!("{}:{}", dst_ip, dst_port)
}; };
let tx_tunnel = self.tx_to_tunnel.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut upstream = match TcpStream::connect(&target_str).await { // Создаем кадр коннекта
Ok(s) => s, let mut connect_frame =
Err(e) => { RawCastFrame::connect(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
error!("Failed to connect to upstream TCP {}: {}", target_str, e); // Кладем доменное имя в payload, чтобы ClientHandler его прочитал
connect_frame.payload = bytes::Bytes::from(target_str);
if tx_tunnel.send(connect_frame).await.is_err() {
return; return;
} }
};
// Сообщаем соединению smoltcp, что мы готовы (рукопожатие выполнено)
let _ = handshake_tx.send(()); let _ = handshake_tx.send(());
let (mut r, mut w) = upstream.into_split();
// Читаем из tun (smoltcp) и пишем во внешнюю сеть
let to_upstream = async {
while let Some(data) = rx_from_smol.recv().await { while let Some(data) = rx_from_smol.recv().await {
if w.write_all(&data).await.is_err() { let data_frame = RawCastFrame::data(
LocalProtocol::Tcp,
socket_id,
dst_ip,
dst_port,
data.to_vec(),
);
if tx_tunnel.send(data_frame).await.is_err() {
break; break;
} }
} }
};
// Читаем из внешней сети и пишем в tun (smoltcp) let close_frame =
let from_upstream = async { RawCastFrame::close(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
let mut buf = vec![0u8; 8192]; // Читаем чанками let _ = tx_tunnel.send(close_frame).await;
while let Ok(n) = r.read(&mut buf).await {
if n == 0 {
break;
} // EOF
if tx_to_smol
.send(Bytes::copy_from_slice(&buf[..n]))
.await
.is_err()
{
break;
}
}
};
tokio::select! { _ = to_upstream => {}, _ = from_upstream => {} }
}); });
} }
@@ -404,12 +442,7 @@ impl ConnectionManager {
socket.abort(); socket.abort();
} }
} }
if socket.state() == tcp::State::CloseWait {
socket.close();
} }
}
fn handle_udp(&mut self, handle: SocketHandle, socket: &mut udp::Socket) { fn handle_udp(&mut self, handle: SocketHandle, socket: &mut udp::Socket) {
self.tracker.last_activity.insert(handle, StdInstant::now()); self.tracker.last_activity.insert(handle, StdInstant::now());
@@ -427,71 +460,54 @@ impl ConnectionManager {
} }
if socket.is_open() && !self.tracker.active_udp.contains_key(&handle) { if socket.is_open() && !self.tracker.active_udp.contains_key(&handle) {
let ep = socket.endpoint(); let (dst_ip, dst_port) = match socket.endpoint().addr {
let target = match ep.addr {
Some(smoltcp::wire::IpAddress::Ipv4(ip)) => { Some(smoltcp::wire::IpAddress::Ipv4(ip)) => {
TargetAddress::Ipv4(std::net::Ipv4Addr::from(ip), ep.port) (std::net::Ipv4Addr::from(ip), socket.endpoint().port)
} }
Some(smoltcp::wire::IpAddress::Ipv6(ip)) => { _ => return,
TargetAddress::Ipv6(std::net::Ipv6Addr::from(ip), ep.port)
}
None => return,
}; };
// 1. Генерируем уникальный ID
let socket_id = self.tracker.generate_socket_id();
// 2. Сохраняем привязку для будущего cleanup
self.tracker.handle_to_id.insert(handle, socket_id);
let (conn, mut rx_from_smol, tx_to_smol) = UdpConnection::new(handle); let (conn, mut rx_from_smol, tx_to_smol) = UdpConnection::new(handle);
self.tracker.active_udp.insert(handle, conn);
// Конвертируем для tokio::net::UdpSocket self.tracker.active_udp.insert(handle, conn);
let target_str = match target { self.tracker.inbound_tx.insert(socket_id, tx_to_smol);
TargetAddress::Domain(d, p) => format!("{}:{}", d, p), let target_str = if let Some(domain) = self.resolver.fake_ip_store.lookup_by_ip(&dst_ip)
TargetAddress::Ipv4(ip, p) => format!("{}:{}", ip, p), {
TargetAddress::Ipv6(ip, p) => format!("{}:{}", ip, p), format!("{}:{}", domain, dst_port)
} else {
format!("{}:{}", dst_ip, dst_port)
}; };
let tx_tunnel = self.tx_to_tunnel.clone();
tokio::spawn(async move { tokio::spawn(async move {
// Создаем локальный UDP сокет со случайным портом let mut connect_frame =
let upstream = match UdpSocket::bind("0.0.0.0:0").await { RawCastFrame::connect(LocalProtocol::Udp, socket_id, dst_ip, dst_port);
Ok(s) => s, connect_frame.payload = bytes::Bytes::from(target_str); // Кладем домен
Err(e) => { let _ = tx_tunnel.send(connect_frame).await;
error!("Failed to bind local UDP socket: {}", e);
return;
}
};
// "Подключаем" UDP сокет к цели (включает фильтр пакетов и позволяет использовать обычные send/recv)
if let Err(e) = upstream.connect(&target_str).await {
error!("Failed to connect UDP to {}: {}", target_str, e);
return;
}
let upstream = std::sync::Arc::new(upstream);
let upstream_rx = upstream.clone();
let upstream_tx = upstream;
// Из smoltcp наружу
let to_upstream = async {
while let Some(data) = rx_from_smol.recv().await { while let Some(data) = rx_from_smol.recv().await {
if upstream_tx.send(&data).await.is_err() { let data_frame = RawCastFrame::data(
LocalProtocol::Udp,
socket_id,
dst_ip,
dst_port,
data.to_vec(),
);
if tx_tunnel.send(data_frame).await.is_err() {
break; break;
} }
} }
};
// Извне в smoltcp let close_frame =
let from_upstream = async { RawCastFrame::close(LocalProtocol::Udp, socket_id, dst_ip, dst_port);
let mut buf = vec![0u8; 65536]; // Максимальный размер UDP датаграммы let _ = tx_tunnel.send(close_frame).await;
while let Ok(n) = upstream_rx.recv(&mut buf).await {
if tx_to_smol
.send(Bytes::copy_from_slice(&buf[..n]))
.await
.is_err()
{
break;
}
}
};
tokio::select! { _ = to_upstream => {}, _ = from_upstream => {} }
}); });
} }
+94 -24
View File
@@ -1,4 +1,6 @@
use netrunner_core::net::ClientHandler; use netrunner_core::net::ClientHandler;
use netrunner_core::net::network::NetworkConfig;
use netrunner_core::rawcast::RawCastFrame;
use smoltcp::iface::PollResult; use smoltcp::iface::PollResult;
use smoltcp::time::Instant; use smoltcp::time::Instant;
use smoltcp::wire::{IpAddress, IpCidr}; use smoltcp::wire::{IpAddress, IpCidr};
@@ -35,17 +37,25 @@ pub struct Engine {
to_smoltcp_tx: UnboundedSender<TokenBuffer>, to_smoltcp_tx: UnboundedSender<TokenBuffer>,
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>, from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
avail: Arc<AtomicBool>, avail: Arc<AtomicBool>,
rx_from_tunnel: mpsc::Receiver<RawCastFrame>,
} }
impl Engine { impl Engine {
pub fn new(config: Config, caps: DeviceCapabilities, dns_handler: DnsHandler) -> Self { pub fn new(
config: Config,
caps: DeviceCapabilities,
dns_handler: DnsHandler,
tx_to_tunnel: mpsc::Sender<RawCastFrame>, // Куда менеджер будет слать пакеты
rx_from_tunnel: mpsc::Receiver<RawCastFrame>, // Откуда движок будет читать пакеты
) -> 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);
let socket_set = ConnectionManager::setup_sockets(2); let socket_set = ConnectionManager::setup_sockets(2);
let manager = ConnectionManager::new(dns_handler);
// Передаем TX-канал в ConnectionManager
let manager = ConnectionManager::new(dns_handler, tx_to_tunnel);
Self { Self {
interface, interface,
@@ -55,6 +65,7 @@ impl Engine {
from_smoltcp_rx: Some(from_smoltcp_rx), from_smoltcp_rx: Some(from_smoltcp_rx),
avail, avail,
manager, manager,
rx_from_tunnel,
} }
} }
@@ -69,17 +80,36 @@ impl Engine {
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();
let mut stuck_frame: Option<RawCastFrame> = None;
loop { loop {
// 1. Быстро читаем из TUN
while let Ok(token) = tun_to_engine_rx.try_recv() { while let Ok(token) = tun_to_engine_rx.try_recv() {
self.manager self.manager
.try_create_socket_from_packet(&token, &mut self.socket_set); .try_create_socket_from_packet(&token, &mut self.socket_set);
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();
} }
} }
// 2. Пытаемся протолкнуть застрявший кадр
if let Some(frame) = stuck_frame.take() {
if let Err(returned_frame) = self.manager.try_inject_inbound(frame) {
stuck_frame = Some(returned_frame);
}
}
// 3. Если затора нет, читаем новые кадры из VPN-туннеля
if stuck_frame.is_none() {
while let Ok(frame) = self.rx_from_tunnel.try_recv() {
if let Err(returned_frame) = self.manager.try_inject_inbound(frame) {
stuck_frame = Some(returned_frame);
break; // СТОП! Локальный сокет переполнен, давим на тормоза!
}
}
}
// 4. Двигаем стейт-машину smoltcp
let result = self.poll(); let result = self.poll();
self.manager.process_sockets(&mut self.socket_set); self.manager.process_sockets(&mut self.socket_set);
@@ -89,16 +119,48 @@ impl Engine {
} }
if matches!(result, PollResult::SocketStateChanged) { if matches!(result, PollResult::SocketStateChanged) {
continue; self.manager.cleanup(&mut self.socket_set);
}
if self.avail.swap(false, Ordering::Acquire) {
tokio::task::yield_now().await;
continue; continue;
} }
self.manager.cleanup(&mut self.socket_set); self.manager.cleanup(&mut self.socket_set);
self.poll_delay().await;
let delay = self
.interface
.poll_delay(Self::current_time(), &self.socket_set);
let sleep_duration = match delay {
Some(d) => Duration::from_micros(d.micros()),
None => Duration::from_millis(10),
};
// 5. Умный select!
if stuck_frame.is_none() {
tokio::select! {
_ = sleep(sleep_duration) => {}
Some(token) = tun_to_engine_rx.recv() => {
self.manager.try_create_socket_from_packet(&token, &mut self.socket_set);
if self.to_smoltcp_tx.send(token).is_ok() {
self.device.mark_rx_available();
}
}
Some(frame) = self.rx_from_tunnel.recv() => {
if let Err(returned_frame) = self.manager.try_inject_inbound(frame) {
stuck_frame = Some(returned_frame);
}
}
}
} else {
// Если буфер переполнен, ждем только таймер или данные от браузера
tokio::select! {
_ = sleep(sleep_duration) => {}
Some(token) = tun_to_engine_rx.recv() => {
self.manager.try_create_socket_from_packet(&token, &mut self.socket_set);
if self.to_smoltcp_tx.send(token).is_ok() {
self.device.mark_rx_available();
}
}
}
}
} }
} }
@@ -108,16 +170,6 @@ impl Engine {
.poll(now, &mut self.device, &mut self.socket_set) .poll(now, &mut self.device, &mut self.socket_set)
} }
async fn poll_delay(&mut self) {
let timestamp = Self::current_time();
let delay = self.interface.poll_delay(timestamp, &self.socket_set);
let sleep_duration = match delay {
Some(d) => Duration::from_micros(d.micros()),
None => Duration::from_millis(10),
};
sleep(sleep_duration).await;
}
fn spawn_tun_reader( fn spawn_tun_reader(
mut reader: DeviceReader, mut reader: DeviceReader,
to_engine: UnboundedSender<TokenBuffer>, to_engine: UnboundedSender<TokenBuffer>,
@@ -305,18 +357,36 @@ impl EngineBuilder {
// 3. Конфигурация интерфейса smoltcp // 3. Конфигурация интерфейса smoltcp
let smol_config = Config::new(smoltcp::wire::HardwareAddress::Ip); let smol_config = Config::new(smoltcp::wire::HardwareAddress::Ip);
let mut caps = DeviceCapabilities::default(); let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = self.config.mtu; // Берем из конфига caps.max_transmission_unit = self.config.mtu;
caps.medium = smoltcp::phy::Medium::Ip; caps.medium = smoltcp::phy::Medium::Ip;
// --- СОЗДАЕМ КАНАЛЫ СВЯЗИ ДВИЖОК <-> ТУННЕЛЬ ---
// tx_to_tunnel: Движок пишет, ClientHandler читает (исходящий трафик)
let (tx_to_tunnel, rx_for_client_handler) =
mpsc::channel(NetworkConfig::global().muxer_capacity);
// tx_to_engine: ClientHandler пишет, Движок читает (входящий трафик)
let (tx_for_client_handler, rx_from_tunnel) =
mpsc::channel(NetworkConfig::global().muxer_capacity);
// 4. Подключение к серверу // 4. Подключение к серверу
info!("Establishing secure tunnel to proxy server..."); info!("Establishing secure tunnel to proxy server...");
ClientHandler::connect(&self.config.remote_address)
// ВАЖНО: Тебе нужно добавить эти аргументы в `ClientHandler::connect`,
// чтобы он знал, откуда забирать `RawCastFrame` для отправки на сервер,
// и куда отдавать `RawCastFrame`, прилетевшие от сервера.
ClientHandler::connect(
&self.config.remote_address,
rx_for_client_handler,
tx_for_client_handler,
)
.await .await
.map_err(|e| format!("Failed to establish secure tunnel: {}", e))?; .map_err(|e| format!("Failed to establish secure tunnel: {}", e))?;
info!("Secure tunnel established, Muxer is ready."); info!("Secure tunnel established, Muxer is ready.");
// 5. Инициализация и настройка Engine // 5. Инициализация и настройка Engine (передаем каналы)
let mut engine = Engine::new(smol_config, caps, dns_handler); let mut engine = Engine::new(smol_config, caps, dns_handler, tx_to_tunnel, rx_from_tunnel);
engine.set_any_ip(self.config.any_ip); engine.set_any_ip(self.config.any_ip);
+134 -135
View File
@@ -1,18 +1,14 @@
use crate::{ use crate::{
net::{ net::{
connection::{ connection::{engine::TunnelEngine, handler::StreamHandler, muxer::Muxer},
bridge::run_tcp_bridge, engine::TunnelEngine, handler::StreamHandler, muxer::Muxer,
},
network::NetworkConfig, network::NetworkConfig,
}, },
nrxp::{ nrxp::{Codec, ErrorAction, FrameType},
Codec, ErrorAction, FrameType, {SocksReply, SocksRequest}, rawcast::{LocalProtocol, RawCastEvent, RawCastFrame},
},
parser::Parser,
tlseng::BrowserProfile, tlseng::BrowserProfile,
}; };
use bytes::BytesMut; use bytes::{Bytes, BytesMut};
use netrunner_logger::{info, warn}; use netrunner_logger::{error, info, warn};
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
net::{ net::{
@@ -21,7 +17,6 @@ use tokio::{
}, },
sync::mpsc, sync::mpsc,
}; };
use tokio_util::sync::CancellationToken;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConnectionRole { pub enum ConnectionRole {
@@ -60,53 +55,34 @@ impl Connection {
codec: Codec::new(false), codec: Codec::new(false),
} }
} }
pub async fn read_socks_request(&mut self) -> Result<SocksRequest, String> {
loop {
match SocksRequest::parse(&mut self.read_buf) {
Ok(Some(req)) => return Ok(req),
Ok(None) => {}
Err(e) => return Err(format!("Socks parse error: {}", e)),
}
let n = self
.inbound
.read_buf(&mut self.read_buf)
.await
.map_err(|e| e.to_string())?;
if n == 0 {
return Err("Client closed connection".into());
}
}
} }
pub async fn send_socks_reply(&mut self, reply: SocksReply) -> Result<(), String> { pub struct ClientHandler;
let mut buf = BytesMut::with_capacity(24);
reply.write_to(&mut buf);
self.outbound
.write_all(&buf)
.await
.map_err(|e| e.to_string())
}
}
pub struct ClientHandler {
pub(crate) conn: Connection,
pub(crate) muxer: Muxer,
}
impl ClientHandler { impl ClientHandler {
pub async fn connect(remote_proxy_addr: &str) -> Result<Muxer, String> { /// Вспомогательная функция: устанавливает одно физическое TLS-соединение
/// и возвращает готовый Muxer для работы с ним.
async fn establish_leg(remote_proxy_addr: &str, leg_name: &str) -> Result<Muxer, String> {
info!(
"Establishing dedicated {} tunnel to {}...",
leg_name, remote_proxy_addr
);
let stream = TcpStream::connect(remote_proxy_addr) let stream = TcpStream::connect(remote_proxy_addr)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| format!("Failed to connect: {}", e))?;
let (inbound, outbound) = stream.into_split();
if let Err(e) = stream.set_nodelay(true) {
warn!("Failed to set TCP_NODELAY on {} leg: {}", leg_name, e);
}
let (inbound, outbound) = stream.into_split();
let mut conn = Connection::new_raw(inbound, outbound); let mut conn = Connection::new_raw(inbound, outbound);
let ch = conn let ch = conn
.codec .codec
.make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com") .make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
.map_err(|e| format!("{:?}", e))?; .map_err(|e| format!("Handshake generation failed: {:?}", e))?;
conn.outbound conn.outbound
.write_all(&ch) .write_all(&ch)
.await .await
@@ -122,18 +98,19 @@ impl ClientHandler {
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
if n == 0 { if n == 0 {
return Err("EOF during handshake".into()); return Err(format!("EOF during handshake on {} leg", leg_name));
} }
} }
Err(e) => return Err(format!("TLS error: {:?}", e)), Err(e) => return Err(format!("TLS error on {} leg: {:?}", leg_name, e)),
} }
} }
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().channel_capacity); info!("{} TLS Handshake complete. Starting Engine.", leg_name);
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().channel_capacity);
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
let muxer = Muxer::new(control_tx, data_tx, true); let muxer = Muxer::new(control_tx, data_tx, true);
let handler = let handler =
std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Client)); std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Client));
@@ -152,102 +129,125 @@ impl ClientHandler {
Ok(muxer) Ok(muxer)
} }
async fn handle_udp_associate(&mut self) -> Result<(), String> { /// Главная функция запуска клиента
let reply = SocksReply::ConnectResult { pub async fn connect(
reply_code: 0x00, remote_proxy_addr: &str,
atyp: 0x01, mut rx_from_engine: mpsc::Receiver<RawCastFrame>,
addr: [0, 0, 0, 0], tx_to_engine: mpsc::Sender<RawCastFrame>,
port: 0, ) -> Result<(), String> {
}; // 1. Устанавливаем ДВА независимых соединения
self.conn.send_socks_reply(reply).await?; // Мы можем сделать это параллельно через tokio::try_join! для скорости
let (tcp_muxer, udp_muxer) = tokio::try_join!(
Self::establish_leg(remote_proxy_addr, "TCP"),
Self::establish_leg(remote_proxy_addr, "UDP"),
)?;
let mut buf = [0u8; 1024]; info!("Dual-tunnel architecture established successfully!");
loop {
if self // 2. ЗАПУСКАЕМ МОСТ-РОУТЕР
.conn tokio::spawn(async move {
.inbound while let Some(raw_frame) = rx_from_engine.recv().await {
.read(&mut buf) let stream_id = raw_frame.socket_id as u32;
let is_udp = raw_frame.protocol == LocalProtocol::Udp;
// РОУТИНГ: Выбираем нужный физический канал
let muxer = if is_udp {
udp_muxer.clone()
} else {
tcp_muxer.clone()
};
match raw_frame.event {
RawCastEvent::Connect => {
let (v_tx, mut v_rx) =
mpsc::channel(NetworkConfig::global().tcp_buffer_size);
muxer.register_stream(stream_id, v_tx);
// Читаем домен, который мы прокинули в предыдущем шаге
let target = if !raw_frame.payload.is_empty() {
String::from_utf8_lossy(&raw_frame.payload).to_string()
} else {
format!("{}:{}", raw_frame.dst_ip, raw_frame.dst_port)
};
let frame_type = if is_udp {
FrameType::UdpConnect
} else {
FrameType::Connect
};
if let Err(e) = muxer
.send_control(stream_id, frame_type, Bytes::from(target))
.await .await
.map_err(|e| e.to_string())?
== 0
{ {
error!("Failed to send connect control frame: {}", e);
continue;
}
let tx_engine_clone = tx_to_engine.clone();
let mut muxer_clone = muxer.clone();
let dst_ip = raw_frame.dst_ip;
let dst_port = raw_frame.dst_port;
let protocol = raw_frame.protocol;
let socket_id = raw_frame.socket_id;
tokio::spawn(async move {
while let Some(payload) = v_rx.recv().await {
if payload.is_empty() {
let close_frame =
RawCastFrame::close(protocol, socket_id, dst_ip, dst_port);
let _ = tx_engine_clone.send(close_frame).await;
break;
} else {
let data_frame = RawCastFrame::data(
protocol,
socket_id,
dst_ip,
dst_port,
payload.to_vec(),
);
if tx_engine_clone.send(data_frame).await.is_err() {
break; break;
} }
} }
Ok(())
} }
} muxer_clone.remove_stream(stream_id);
#[async_trait::async_trait]
impl TunnelHandler for ClientHandler {
async fn run(mut self) -> Result<(), String> {
info!("Starting SOCKS multiplexed handling");
self.conn.read_socks_request().await?;
self.conn
.send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 })
.await?;
let req = self.conn.read_socks_request().await?;
match req {
SocksRequest::Connect {
command: 0x01,
target,
} => {
let stream_id = self.muxer.next_id();
let (v_tx, mut v_rx) =
mpsc::channel::<bytes::Bytes>(NetworkConfig::global().tcp_buffer_size);
self.muxer.register_stream(stream_id, v_tx);
self.muxer
.send_control(
stream_id,
FrameType::Connect,
bytes::Bytes::from(target.to_string()),
)
.await?;
let first_payload =
tokio::time::timeout(std::time::Duration::from_secs(10), v_rx.recv())
.await
.map_err(|_| "Timeout waiting for proxy response")?
.ok_or("No data from proxy")?;
if first_payload.len() >= 2 && first_payload[1] == 0x00 {
self.conn
.outbound
.write_all(&first_payload)
.await
.map_err(|e| e.to_string())?;
} else {
self.conn.outbound.write_all(&first_payload).await.ok();
return Err("Proxy rejected connection".into());
}
let browser_in = self.conn.inbound;
let browser_out = self.conn.outbound;
let muxer = self.muxer;
tokio::spawn(async move {
run_tcp_bridge(stream_id, browser_in, browser_out, muxer, v_rx).await;
}); });
}
RawCastEvent::Data => {
let frame_type = if is_udp {
FrameType::UdpData
} else {
FrameType::Data
};
if let Err(e) = muxer
.send_control(stream_id, frame_type, raw_frame.payload)
.await
{
error!("Failed to send data frame: {}", e);
}
}
RawCastEvent::Close => {
let _ = muxer
.send_control(stream_id, FrameType::Close, Bytes::new())
.await;
muxer.remove_stream(stream_id);
}
}
}
info!("ClientHandler bridge task terminated.");
});
Ok(()) Ok(())
} }
SocksRequest::Connect { command: 0x03, .. } => {
info!("Handling UDP Associate request");
self.handle_udp_associate().await
} }
_ => Err("Unsupported SOCKS command".into()),
}
}
}
pub struct ServerHandler { pub struct ServerHandler {
pub(crate) conn: Connection, pub(crate) conn: Connection,
} }
impl ServerHandler { impl ServerHandler {
async fn handle_stealth_fallback( async fn handle_stealth_fallback(
mut client_inbound: OwnedReadHalf, mut client_inbound: OwnedReadHalf,
@@ -295,14 +295,13 @@ impl ServerHandler {
} }
} }
} }
#[async_trait::async_trait] #[async_trait::async_trait]
impl TunnelHandler for ServerHandler { impl TunnelHandler for ServerHandler {
async fn run(mut self) -> Result<(), String> { async fn run(mut self) -> Result<(), String> {
info!("Acting as TLS Server with Stealth Fallback"); info!("Acting as TLS Server with Stealth Fallback");
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().channel_capacity); let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().channel_capacity); let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
let muxer = Muxer::new(control_tx, data_tx, false); let muxer = Muxer::new(control_tx, data_tx, false);
let handshake_timeout = std::time::Duration::from_secs(1); let handshake_timeout = std::time::Duration::from_secs(1);
+14 -49
View File
@@ -1,4 +1,4 @@
use bytes::{Bytes, BytesMut}; use bytes::Bytes;
use netrunner_logger::{debug, error, info}; use netrunner_logger::{debug, error, info};
use crate::{ use crate::{
@@ -10,9 +10,7 @@ use crate::{
}, },
network::NetworkConfig, network::NetworkConfig,
}, },
nrxp::{ nrxp::{Frame, FrameType},
SocksReply, {Frame, FrameType},
},
}; };
pub(crate) struct StreamHandler { pub(crate) struct StreamHandler {
@@ -43,7 +41,7 @@ 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();
let (v_tx, v_rx) = tokio::sync::mpsc::channel(NetworkConfig::global().channel_capacity); let (v_tx, v_rx) = tokio::sync::mpsc::channel(NetworkConfig::global().stream_capacity);
muxer.register_stream(stream_id, v_tx); muxer.register_stream(stream_id, v_tx);
tokio::spawn(async move { tokio::spawn(async move {
@@ -61,29 +59,17 @@ impl StreamHandler {
let elapsed = start.elapsed(); let elapsed = start.elapsed();
info!(stream_id, target = %target_str, latency_ms = elapsed.as_millis(), "Remote TCP connection established"); info!(stream_id, target = %target_str, latency_ms = elapsed.as_millis(), "Remote TCP connection established");
let mut reply_buf = BytesMut::with_capacity(10); // Больше никаких ответов (Reply), просто прокидываем байты!
let reply = SocksReply::ConnectResult {
reply_code: 0x00,
atyp: 0x01,
addr: [0, 0, 0, 0],
port: 0,
};
reply.write_to(&mut reply_buf);
let _ = muxer
.send_control(stream_id, FrameType::Connect, reply_buf.freeze())
.await;
let (r, w) = stream.into_split(); let (r, w) = stream.into_split();
run_tcp_bridge(stream_id, r, w, muxer, v_rx).await; run_tcp_bridge(stream_id, r, w, muxer, v_rx).await;
} }
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, FrameType::Connect).await; Self::close_stream(&muxer, stream_id).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, FrameType::Connect).await; Self::close_stream(&muxer, stream_id).await;
} }
} }
}); });
@@ -97,7 +83,7 @@ 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();
let (v_tx, v_rx) = tokio::sync::mpsc::channel(NetworkConfig::global().channel_capacity); let (v_tx, v_rx) = tokio::sync::mpsc::channel(NetworkConfig::global().stream_capacity);
muxer.register_stream(stream_id, v_tx); muxer.register_stream(stream_id, v_tx);
tokio::spawn(async move { tokio::spawn(async move {
@@ -107,30 +93,16 @@ impl StreamHandler {
Ok(socket) => { Ok(socket) => {
if let Err(e) = socket.connect(&target_str).await { if let Err(e) = socket.connect(&target_str).await {
error!(stream_id, target = %target_str, error = %e, "UDP connect failed"); error!(stream_id, target = %target_str, error = %e, "UDP connect failed");
Self::send_error_reply(&muxer, stream_id, 0x01, FrameType::UdpConnect) Self::close_stream(&muxer, stream_id).await;
.await;
return; return;
} }
let mut reply_buf = BytesMut::with_capacity(10); // Успех - просто начинаем слушать UDP и слать в туннель
let reply = SocksReply::ConnectResult {
reply_code: 0x00,
atyp: 0x01,
addr: [0, 0, 0, 0],
port: 0,
};
reply.write_to(&mut reply_buf);
let _ = muxer
.send_control(stream_id, FrameType::UdpConnect, reply_buf.freeze())
.await;
run_udp_bridge(stream_id, socket, muxer, v_rx).await; run_udp_bridge(stream_id, socket, muxer, v_rx).await;
} }
Err(e) => { Err(e) => {
error!(stream_id, target = %target_str, error = %e, "UDP bind failed"); error!(stream_id, target = %target_str, error = %e, "UDP bind failed");
Self::send_error_reply(&muxer, stream_id, 0x01, FrameType::UdpConnect) Self::close_stream(&muxer, stream_id).await;
.await;
} }
} }
}); });
@@ -143,19 +115,12 @@ impl StreamHandler {
self.muxer.dispatch_to_local(stream_id, payload).await; self.muxer.dispatch_to_local(stream_id, payload).await;
} }
async fn send_error_reply(muxer: &Muxer, stream_id: u32, code: u8, frame_type: FrameType) { // Вспомогательная функция вместо send_error_reply
muxer.remove_stream(stream_id); async fn close_stream(muxer: &Muxer, stream_id: u32) {
let mut reply_buf = BytesMut::with_capacity(10);
let reply = SocksReply::ConnectResult {
reply_code: code,
atyp: 0x01,
addr: [0, 0, 0, 0],
port: 0,
};
reply.write_to(&mut reply_buf);
let _ = muxer let _ = muxer
.send_control(stream_id, frame_type, reply_buf.freeze()) .send_control(stream_id, FrameType::Close, Bytes::new())
.await; .await;
muxer.remove_stream(stream_id);
} }
async fn on_data(&self, stream_id: u32, payload: Bytes) { async fn on_data(&self, stream_id: u32, payload: Bytes) {
+45 -60
View File
@@ -1,10 +1,10 @@
use std::sync::OnceLock; use std::sync::OnceLock;
use crate::{ use crate::{
net::connection::{ClientHandler, Connection, ConnectionRole, ServerHandler, TunnelHandler}, net::connection::{Connection, ConnectionRole, ServerHandler, TunnelHandler},
nrxp::{FRAME_HEADER_SIZE, MAX_PADDING_SIZE}, nrxp::{FRAME_HEADER_SIZE, MAX_PADDING_SIZE},
}; };
use netrunner_logger::{error, info}; use netrunner_logger::{error, info, warn};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
@@ -32,50 +32,36 @@ impl Network {
pub async fn run(&self, token: CancellationToken) { pub async fn run(&self, token: CancellationToken) {
let addr = format!("{}:{}", self.host, self.port); let addr = format!("{}:{}", self.host, self.port);
// Инициализируем глобальный конфиг сети (MTU, размеры буферов)
NetworkConfig::init_global(1350); NetworkConfig::init_global(1350);
match self.role { match self.role {
ConnectionRole::Client => { ConnectionRole::Client => {
info!("Starting Client mode"); // В новой архитектуре клиент запускается через EngineBuilder + TUN.
let server_addr = self // Структура Network теперь используется только для запуска Сервера.
.remote_proxy_addr error!("Client mode cannot be run via Network::run anymore.");
.as_ref() error!("Please use EngineBuilder to initialize the TUN client.");
.ok_or("No proxy addr") panic!("Legacy SOCKS5 client mode has been removed.");
.unwrap();
let muxer = match ClientHandler::connect(server_addr).await {
Ok(m) => m,
Err(e) => {
error!(error = %e, "Global tunnel failed.");
return;
}
};
let listener = TcpListener::bind(&addr).await.expect("SOCKS bind failed");
loop {
tokio::select! {
_ = token.cancelled() => break,
res = listener.accept() => {
if let Ok((stream, _client_addr)) = res {
let conn = Connection::new(stream, false);
let handler = ClientHandler{ conn, muxer: muxer.clone() };
tokio::spawn(async move {
if let Err(e) = handler.run().await {
error!(error = %e, "Client handler error");
}
});
}
}
}
}
} }
ConnectionRole::Server => { ConnectionRole::Server => {
info!("Starting Server mode on {}", addr);
let listener = TcpListener::bind(&addr).await.expect("Server bind failed"); let listener = TcpListener::bind(&addr).await.expect("Server bind failed");
loop { loop {
tokio::select! { tokio::select! {
_ = token.cancelled() => break, _ = token.cancelled() => {
info!("Shutdown signal received, stopping server.");
break;
}
res = listener.accept() => { res = listener.accept() => {
if let Ok((stream, client_addr)) = res { if let Ok((stream, client_addr)) = res {
info!("New connection from {}", client_addr);
// Создаем соединение (init = true для сервера)
let conn = Connection::new(stream, true); let conn = Connection::new(stream, true);
let handler = ServerHandler { conn }; let handler = ServerHandler { conn };
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = handler.run().await { if let Err(e) = handler.run().await {
error!(client = %client_addr, error = %e, "Server handler error"); error!(client = %client_addr, error = %e, "Server handler error");
@@ -96,52 +82,51 @@ pub struct NetworkConfig {
pub mtu: usize, pub mtu: usize,
pub max_wire_frame_size: usize, pub max_wire_frame_size: usize,
pub safe_payload_size: usize, pub safe_payload_size: usize,
pub tcp_buffer_size: usize,
// --- ИЗМЕНЕНИЯ ЗДЕСЬ ---
pub tcp_buffer_size: usize, // Размер буфера для системного tokio::TcpStream
pub udp_buffer_size: usize, pub udp_buffer_size: usize,
pub channel_capacity: usize, pub muxer_capacity: usize, // Глобальные каналы (Muxer <-> Engine)
pub stream_capacity: usize, // Локальные каналы 1 сокета (Engine <-> Muxer)
pub smoltcp_socket_buf: usize,
pub tcp_max_pending: usize,
pub tcp_chunk_size: usize,
} }
impl NetworkConfig { impl NetworkConfig {
pub fn new(system_mtu: usize) -> Self { pub fn new(system_mtu: usize) -> Self {
let transport_overhead = 28; // IPv4 + UDP let transport_overhead = 28;
let max_wire_frame = system_mtu.saturating_sub(transport_overhead); let max_wire_frame = system_mtu.saturating_sub(transport_overhead);
let safe_payload = max_wire_frame.saturating_sub(10).saturating_sub(255);
let safe_payload = max_wire_frame let muxer_capacity = 64;
.saturating_sub(FRAME_HEADER_SIZE as usize) let stream_capacity = 4;
.saturating_sub((MAX_PADDING_SIZE - 1) as usize);
let tcp_chunks_count = 65536 / safe_payload;
let tcp_buffer = safe_payload * tcp_chunks_count;
let udp_chunks_count = 16384 / safe_payload;
let udp_buffer = safe_payload * udp_chunks_count;
let channel_cap = 1024;
netrunner_logger::info!(
mtu = system_mtu,
payload = safe_payload,
tcp_buf = tcp_buffer,
"Network Optimizer: Calculations complete for current MTU"
);
Self { Self {
mtu: system_mtu, mtu: system_mtu,
max_wire_frame_size: max_wire_frame, max_wire_frame_size: max_wire_frame,
safe_payload_size: safe_payload, safe_payload_size: safe_payload,
tcp_buffer_size: tcp_buffer,
udp_buffer_size: udp_buffer, // Заменяем громоздкие вычисления на стандартные 64KB чанки для системных сокетов
channel_capacity: channel_cap, tcp_buffer_size: 64 * 1024,
udp_buffer_size: 64 * 1024,
muxer_capacity,
stream_capacity,
smoltcp_socket_buf: 64 * 1024,
tcp_max_pending: 64 * 1024,
tcp_chunk_size: 16 * 1024,
} }
} }
pub fn init_global(system_mtu: usize) { pub fn init_global(system_mtu: usize) {
let config = Self::new(system_mtu); let config = Self::new(system_mtu);
if GLOBAL_NET_CONFIG.set(config).is_err() { if GLOBAL_NET_CONFIG.set(config).is_err() {
netrunner_logger::warn!("Global network config was already initialized!"); warn!("Global network config was already initialized!");
} }
} }
pub fn global() -> &'static Self { pub fn global() -> &'static Self {
GLOBAL_NET_CONFIG GLOBAL_NET_CONFIG
.get() .get()
-4
View File
@@ -3,11 +3,7 @@ mod bridge;
mod codec; mod codec;
mod errors; mod errors;
mod frame; mod frame;
mod socks;
// Экспортируем для остального ядра только необходимые типы // Экспортируем для остального ядра только необходимые типы
pub(crate) use codec::Codec; pub(crate) use codec::Codec;
pub(crate) use errors::{ErrorAction, ErrorStage, TlsError}; pub(crate) use errors::{ErrorAction, ErrorStage, TlsError};
pub(crate) use frame::{Frame, FrameType, FRAME_HEADER_SIZE, MAX_PADDING_SIZE}; pub(crate) use frame::{Frame, FrameType, FRAME_HEADER_SIZE, MAX_PADDING_SIZE};
pub use socks::TargetAddress;
pub(crate) use socks::{SocksReply, SocksRequest};
-345
View File
@@ -1,345 +0,0 @@
use std::fmt;
use bytes::{Buf, BufMut, BytesMut};
use crate::parser::Parser;
const SOCKS5_VERSION: u8 = 0x05;
const REPLY_SUCCESS: u8 = 0x00;
const REPLY_AUTH_FAILURE: u8 = 0xFF;
const SOCKS5_MIN_HEADER: usize = 4;
const ATYP_IPV4: u8 = 0x01;
const ATYP_DOMAIN: u8 = 0x03;
const ATYP_IPV6: u8 = 0x04;
const IPV4_SIZE: usize = 4;
const IPV6_SIZE: usize = 16;
const PORT_SIZE: usize = 2;
#[derive(Debug)]
pub(crate) enum SocksRequest {
Handshake { methods: Vec<u8> },
Connect { command: u8, target: SocksTarget },
Unknown,
}
impl SocksRequest {
pub async fn handle_handshake<S>(
stream: &mut S,
buf: &mut BytesMut,
) -> Result<SocksTarget, String>
where
S: tokio::io::AsyncReadExt + tokio::io::AsyncWriteExt + Unpin,
{
loop {
if let Some(req) = Self::parse(buf)? {
if let SocksRequest::Handshake { .. } = req {
let mut reply = BytesMut::with_capacity(2);
SocksReply::HandshakeSelect { method: 0x00 }.write_to(&mut reply);
stream.write_all(&reply).await.map_err(|e| e.to_string())?;
break;
}
return Err("Expected Handshake, got something else".into());
}
if stream.read_buf(buf).await.map_err(|e| e.to_string())? == 0 {
return Err("Client closed during greeting".into());
}
}
loop {
if let Some(req) = Self::parse(buf)? {
if let SocksRequest::Connect { command, target } = req {
if command != 0x01 {
return Err(format!("Unsupported SOCKS command: 0x{:02X}", command));
}
return Ok(target);
}
}
if stream.read_buf(buf).await.map_err(|e| e.to_string())? == 0 {
return Err("Client closed during connect request".into());
}
}
}
pub async fn perform_client_handshake<S>(
stream: &mut S,
target_addr: &TargetAddress,
) -> Result<(), String>
where
S: tokio::io::AsyncReadExt + tokio::io::AsyncWriteExt + Unpin,
{
let greeting = [SOCKS5_VERSION, 0x01, 0x00];
stream
.write_all(&greeting)
.await
.map_err(|e| e.to_string())?;
let mut method_selection = [0u8; 2];
stream
.read_exact(&mut method_selection)
.await
.map_err(|e| e.to_string())?;
if method_selection[0] != SOCKS5_VERSION || method_selection[1] != 0x00 {
return Err(format!(
"Proxy rejected auth method or version: {:02X?}",
method_selection
));
}
let mut connect_req = BytesMut::with_capacity(32);
connect_req.put_u8(SOCKS5_VERSION);
connect_req.put_u8(0x01);
connect_req.put_u8(0x00);
match target_addr {
TargetAddress::Ipv4(ip, port) => {
connect_req.put_u8(ATYP_IPV4);
connect_req.put_slice(&ip.octets());
connect_req.put_u16(*port);
}
TargetAddress::Ipv6(ip, port) => {
connect_req.put_u8(ATYP_IPV6);
connect_req.put_slice(&ip.octets());
connect_req.put_u16(*port);
}
TargetAddress::Domain(host, port) => {
connect_req.put_u8(ATYP_DOMAIN);
let host_bytes = host.as_bytes();
connect_req.put_u8(host_bytes.len() as u8);
connect_req.put_slice(host_bytes);
connect_req.put_u16(*port);
}
}
stream
.write_all(&connect_req)
.await
.map_err(|e| e.to_string())?;
let mut reply_header = [0u8; 4];
stream
.read_exact(&mut reply_header)
.await
.map_err(|e| e.to_string())?;
if reply_header[1] != REPLY_SUCCESS {
return Err(format!(
"Proxy failed to connect, code: {:02X}",
reply_header[1]
));
}
let atyp = reply_header[3];
let remain_len = match atyp {
ATYP_IPV4 => IPV4_SIZE + PORT_SIZE,
ATYP_IPV6 => IPV6_SIZE + PORT_SIZE,
ATYP_DOMAIN => {
let len = stream.read_u8().await.map_err(|e| e.to_string())?;
len as usize + PORT_SIZE
}
_ => return Err("Unknown ATYP in proxy response".into()),
};
let mut discard = vec![0u8; remain_len];
stream
.read_exact(&mut discard)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
}
#[derive(Debug)]
pub(crate) enum SocksReply {
HandshakeSelect {
method: u8,
},
ConnectResult {
reply_code: u8,
atyp: u8,
addr: [u8; 4],
port: u16,
},
}
#[derive(Debug, Clone)]
pub enum TargetAddress {
Ipv4(std::net::Ipv4Addr, u16),
Domain(String, u16),
Ipv6(std::net::Ipv6Addr, u16),
}
impl fmt::Display for TargetAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TargetAddress::Ipv4(addr, port) => write!(f, "{}:{}", addr, port),
TargetAddress::Domain(domain, port) => write!(f, "{}:{}", domain, port),
TargetAddress::Ipv6(addr, port) => write!(f, "[{}]:{}", addr, port),
}
}
}
#[derive(Debug)]
pub(crate) struct SocksTarget {
pub addr: TargetAddress,
}
impl SocksReply {
pub fn write_to(self, buf: &mut BytesMut) {
match self {
SocksReply::HandshakeSelect { method } => {
buf.put_u8(SOCKS5_VERSION);
buf.put_u8(method);
}
SocksReply::ConnectResult {
reply_code,
atyp,
addr,
port,
} => {
buf.put_u8(SOCKS5_VERSION);
buf.put_u8(reply_code);
buf.put_u8(0x00);
buf.put_u8(atyp);
buf.put_slice(&addr);
buf.put_u16(port);
}
}
}
}
impl SocksTarget {
pub fn to_string(&self) -> String {
match &self.addr {
TargetAddress::Ipv4(ip, port) => {
format!("{}:{}", ip, port)
}
TargetAddress::Ipv6(ip, port) => {
format!("[{}]:{}", ip, port)
}
TargetAddress::Domain(domain, port) => {
let clean_domain = domain.replace('\0', "");
format!("{}:{}", clean_domain, port)
}
}
}
}
impl Parser for SocksTarget {
type Error = String;
fn can_parse(bytes: &BytesMut) -> bool {
if bytes.len() < 4 {
return false;
}
let atyp = bytes[3];
match atyp {
ATYP_IPV4 => bytes.len() >= 4 + 4 + 2,
ATYP_IPV6 => bytes.len() >= 4 + 16 + 2,
ATYP_DOMAIN => {
if bytes.len() < 5 {
return false;
}
let domain_len = bytes[4] as usize;
bytes.len() >= 4 + 1 + domain_len + 2
}
_ => false,
}
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if !Self::can_parse(bytes) {
return Ok(None);
}
let atyp = bytes[3];
let total_len = match atyp {
ATYP_IPV4 => SOCKS5_MIN_HEADER + IPV4_SIZE + PORT_SIZE,
ATYP_DOMAIN => SOCKS5_MIN_HEADER + 1 + (bytes[4] as usize) + PORT_SIZE,
ATYP_IPV6 => SOCKS5_MIN_HEADER + IPV6_SIZE + PORT_SIZE,
_ => return Err("Unsupported address type".to_string()),
};
let mut packet = bytes.split_to(total_len);
packet.advance(4);
let addr = match atyp {
ATYP_IPV4 => {
let octets: [u8; 4] = packet.split_to(4)[..].try_into().unwrap();
let port = packet.get_u16();
TargetAddress::Ipv4(octets.into(), port)
}
ATYP_DOMAIN => {
let len = packet.get_u8() as usize;
let domain_bytes = packet.split_to(len);
let domain = String::from_utf8(domain_bytes.to_vec())
.map_err(|_| "Invalid UTF-8 domain".to_string())?;
let port = packet.get_u16();
TargetAddress::Domain(domain, port)
}
ATYP_IPV6 => {
let octets: [u8; 16] = packet.split_to(16)[..].try_into().unwrap();
let port = packet.get_u16();
TargetAddress::Ipv6(octets.into(), port)
}
_ => unreachable!(),
};
Ok(Some(SocksTarget { addr }))
}
}
impl Parser for SocksRequest {
type Error = String;
fn can_parse(bytes: &BytesMut) -> bool {
if bytes.len() < 2 || bytes[0] != SOCKS5_VERSION {
return false;
}
let nmethods = bytes[1] as usize;
if bytes.len() >= 2 + nmethods {
if bytes.len() >= SOCKS5_MIN_HEADER && SocksTarget::can_parse(bytes) {
return true;
}
return true;
}
false
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if bytes.len() < 2 || bytes[0] != SOCKS5_VERSION {
return Ok(None);
}
if bytes.len() >= SOCKS5_MIN_HEADER && SocksTarget::can_parse(bytes) {
let command = bytes[1];
if let Some(target) = SocksTarget::parse(bytes)? {
return Ok(Some(SocksRequest::Connect { command, target }));
}
}
let nmethods = bytes[1] as usize;
let total_handshake = 2 + nmethods;
if bytes.len() >= total_handshake {
let mut packet = bytes.split_to(total_handshake);
packet.advance(2);
let mut methods = vec![0u8; nmethods];
packet.copy_to_slice(&mut methods);
return Ok(Some(SocksRequest::Handshake { methods }));
}
Ok(None)
}
}
+86
View File
@@ -0,0 +1,86 @@
use bytes::Bytes;
use std::net::Ipv4Addr;
use crate::{
nrxp::{Frame, FrameType},
rawcast::frame::{LocalProtocol, RawCastEvent, RawCastFrame},
};
pub struct RawCastAdapter;
impl RawCastAdapter {
/// Перевод из RawCast (от локального TUN/smoltcp) в NRXP (в ядро/сеть)
pub fn to_nrxp(raw: RawCastFrame) -> Result<Frame, String> {
// Кастим ID сокета
let stream_id = raw.socket_id as u32;
let (frame_type, payload) = match (raw.protocol, raw.event) {
// Открытие TCP соединения
(LocalProtocol::Tcp, RawCastEvent::Connect) => {
let target = format!("{}:{}", raw.dst_ip, raw.dst_port);
(FrameType::Connect, Bytes::from(target))
}
// Открытие UDP сессии
(LocalProtocol::Udp, RawCastEvent::Connect) => {
let target = format!("{}:{}", raw.dst_ip, raw.dst_port);
(FrameType::UdpConnect, Bytes::from(target))
}
// Передача данных (TCP)
(LocalProtocol::Tcp, RawCastEvent::Data) => (FrameType::Data, raw.payload),
// Передача данных (UDP)
(LocalProtocol::Udp, RawCastEvent::Data) => (FrameType::UdpData, raw.payload),
// Закрытие соединения (одинаково для TCP и UDP)
(_, RawCastEvent::Close) => (FrameType::Close, Bytes::new()),
// ICMP пока не поддерживается в NRXP, отбрасываем
(LocalProtocol::Icmp, _) => {
return Err("ICMP protocol is not supported by NRXP core".into());
}
};
// Используем твой новый удобный конструктор Frame!
Ok(Frame::new(stream_id, frame_type, payload))
}
/// Перевод из NRXP (от сервера) обратно в RawCast (в smoltcp)
///
/// ВАЖНО: Так как NRXP Frame не содержит IP и Port (только stream_id),
/// локальный клиент должен помнить, какому stream_id какой IP/Port принадлежит.
pub fn from_nrxp(
nrxp_frame: Frame,
dst_ip: Ipv4Addr,
dst_port: u16,
is_udp: bool,
) -> Result<RawCastFrame, String> {
let socket_id = nrxp_frame.header.stream_id as u64;
let protocol = if is_udp {
LocalProtocol::Udp
} else {
LocalProtocol::Tcp
};
let event = match nrxp_frame.header.frame_type {
// Сервер прислал успешный коннект
FrameType::Connect | FrameType::UdpConnect => RawCastEvent::Connect,
// Сервер прислал данные
FrameType::Data | FrameType::UdpData => RawCastEvent::Data,
// Сервер закрыл соединение
FrameType::Close => RawCastEvent::Close,
FrameType::Heartbeat => return Err("Heartbeat should be handled by muxer".into()),
};
Ok(RawCastFrame {
protocol,
event,
socket_id,
dst_ip,
dst_port,
payload: nrxp_frame.payload,
})
}
}
+3
View File
@@ -1 +1,4 @@
mod adapter;
mod frame; mod frame;
pub use frame::{LocalProtocol, RawCastEvent, RawCastFrame};