sessions and muxer legs

This commit is contained in:
2026-03-27 15:57:04 +07:00
parent fffc3794b9
commit f4c6f2efdd
13 changed files with 974 additions and 540 deletions
+2
View File
@@ -73,6 +73,8 @@ impl SessionManager {
tun_fd: Option<i32>, tun_fd: Option<i32>,
cache_dir: String, cache_dir: String,
) -> Arc<Session> { ) -> Arc<Session> {
netrunner_logger::Logger::init();
netrunner_logger::Logger::global().set_level("info");
let runtime = get_runtime(); let runtime = get_runtime();
let cancel_token = CancellationToken::new(); let cancel_token = CancellationToken::new();
let session_token = cancel_token.clone(); let session_token = cancel_token.clone();
+45 -54
View File
@@ -3,13 +3,12 @@ use netrunner_core::net::network::NetworkConfig;
use smoltcp::{ use smoltcp::{
iface::SocketHandle, iface::SocketHandle,
socket::{tcp, udp}, socket::{tcp, udp},
time::Instant,
wire::IpEndpoint, wire::IpEndpoint,
}; };
use std::{collections::HashSet, time::Duration}; use std::time::Duration;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
// Добавили trace для частых логов (попакетно) и debug для состояний // Добавили trace для частых логов (попакетно) и debug для состояний
use netrunner_logger::{debug, error, info, trace, warn}; use netrunner_logger::debug;
// ============================================================================ // ============================================================================
// 1. БАЗОВАЯ СТРУКТУРА (ConnectionCore) // 1. БАЗОВАЯ СТРУКТУРА (ConnectionCore)
@@ -17,19 +16,19 @@ use netrunner_logger::{debug, error, info, trace, warn};
/// Фундамент для любого соединения. /// Фундамент для любого соединения.
/// Инициализирует и хранит каналы связи между smoltcp и Muxer'ом. /// Инициализирует и хранит каналы связи между smoltcp и Muxer'ом.
pub struct ConnectionCore { pub struct ConnectionCore<T> {
pub handle: SocketHandle, pub handle: SocketHandle,
pub tx: mpsc::Sender<Bytes>, pub tx: mpsc::Sender<T>,
pub rx: mpsc::Receiver<Bytes>, pub rx: mpsc::Receiver<Bytes>, // Входящие из туннеля всегда байты
} }
impl ConnectionCore { impl<T> ConnectionCore<T> {
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) { pub fn new(
trace!(%handle, "Creating ConnectionCore channels"); handle: SocketHandle,
let (tx_to_net, rx_from_smol) = capacity: usize,
mpsc::channel::<Bytes>(NetworkConfig::global().tcp_stream_capacity); ) -> (Self, mpsc::Receiver<T>, mpsc::Sender<Bytes>) {
let (tx_to_smol, rx_from_net) = let (tx_to_net, rx_from_smol) = mpsc::channel::<T>(capacity);
mpsc::channel::<Bytes>(NetworkConfig::global().tcp_stream_capacity); let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(capacity);
let core = Self { let core = Self {
handle, handle,
@@ -53,7 +52,7 @@ pub enum ConnectionState {
} }
pub struct TcpConnection { pub struct TcpConnection {
core: ConnectionCore, core: ConnectionCore<Bytes>,
state: ConnectionState, state: ConnectionState,
pending_data: BytesMut, pending_data: BytesMut,
handshake_rx: Option<oneshot::Receiver<()>>, handshake_rx: Option<oneshot::Receiver<()>>,
@@ -70,8 +69,8 @@ impl TcpConnection {
mpsc::Sender<Bytes>, mpsc::Sender<Bytes>,
oneshot::Sender<()>, oneshot::Sender<()>,
) { ) {
debug!(%handle, "Initializing new TCP Connection (State -> Handshaking)"); let capacity = NetworkConfig::global().tcp_stream_capacity;
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle); let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle, capacity);
let (handshake_tx, handshake_rx) = oneshot::channel(); let (handshake_tx, handshake_rx) = oneshot::channel();
let conn = Self { let conn = Self {
@@ -80,7 +79,7 @@ impl TcpConnection {
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], chunk_buf: vec![0u8; NetworkConfig::global().tcp_chunk_size],
server_eof: false, // Инициализируем server_eof: false,
}; };
(conn, rx_from_smol, tx_to_smol, handshake_tx) (conn, rx_from_smol, tx_to_smol, handshake_tx)
@@ -224,72 +223,64 @@ impl TcpConnection {
const UDP_TIMEOUT: Duration = Duration::from_secs(60); const UDP_TIMEOUT: Duration = Duration::from_secs(60);
pub type UdpPacketTarget = (Bytes, std::net::Ipv4Addr, u16);
pub struct UdpConnection { pub struct UdpConnection {
core: ConnectionCore, core: ConnectionCore<UdpPacketTarget>, // Используем кортеж
client_endpoints: HashSet<IpEndpoint>, last_client_endpoint: Option<IpEndpoint>,
last_activity: std::time::Instant, // Системное время для таймаутов last_activity: std::time::Instant,
} }
impl UdpConnection { impl UdpConnection {
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) { pub fn new(
debug!(%handle, "Initializing new UDP Connection"); handle: SocketHandle,
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle); client_addr: smoltcp::wire::IpAddress,
client_port: u16,
) -> (Self, mpsc::Receiver<UdpPacketTarget>, mpsc::Sender<Bytes>) {
// Для UDP используем фиксированный буфер 512
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle, 512);
let conn = Self { let conn = Self {
core, core,
client_endpoints: HashSet::new(), // Инициализируем пустое множество last_client_endpoint: Some(IpEndpoint::new(client_addr, client_port)),
last_activity: std::time::Instant::now(), last_activity: std::time::Instant::now(),
}; };
(conn, rx_from_smol, tx_to_smol) (conn, rx_from_smol, tx_to_smol)
} }
// Вспомогательный метод для Tracker'а
pub fn has_client(&self, port: u16) -> bool {
self.last_client_endpoint
.map_or(false, |ep| ep.port == port)
}
pub fn tick(&mut self, socket: &mut udp::Socket) -> bool { pub fn tick(&mut self, socket: &mut udp::Socket) -> bool {
// Проверка таймаутов (остается твоя рабочая)
if self.last_activity.elapsed() > UDP_TIMEOUT { if self.last_activity.elapsed() > UDP_TIMEOUT {
debug!(%self.core.handle, "UDP Session closed due to timeout");
socket.close(); socket.close();
return false; return false;
} }
// ЧИТАЕМ ИЗ SMOLTCP (от клиента) И ШЛЕМ В ТУННЕЛЬ
if socket.can_recv() { if socket.can_recv() {
while let Ok((data, metadata)) = socket.recv() { while let Ok((data, metadata)) = socket.recv() {
let source_endpoint = metadata.endpoint; if let smoltcp::wire::IpAddress::Ipv4(ip) = metadata.endpoint.addr {
self.last_client_endpoint = Some(metadata.endpoint);
// ЗАПОМИНАЕМ ВСЕ ПОРТЫ КЛИЕНТА, КОТОРЫЕ СЮДА СТУЧАТСЯ let target_ip = std::net::Ipv4Addr::from(ip);
if self.client_endpoints.insert(source_endpoint) { let target_port = metadata.endpoint.port;
info!( let payload = (Bytes::copy_from_slice(data), target_ip, target_port);
%self.core.handle,
source = %source_endpoint,
"Registered new client port for UDP session"
);
}
if self.core.tx.try_send(Bytes::copy_from_slice(data)).is_ok() { if self.core.tx.try_send(payload).is_ok() {
self.last_activity = std::time::Instant::now(); self.last_activity = std::time::Instant::now();
} }
} }
} }
}
// ЧИТАЕМ ИЗ ТУННЕЛЯ И ШЛЕМ В SMOLTCP (клиенту) // Исходящие из туннеля (от Telegram) — всё еще просто Bytes
if socket.can_send() && !self.client_endpoints.is_empty() { if socket.can_send() {
loop { if let Some(client_endpoint) = self.last_client_endpoint {
match self.core.rx.try_recv() { while let Ok(data) = self.core.rx.try_recv() {
Ok(data) => { let _ = socket.send_slice(&data, client_endpoint);
// БРОАДКАСТ: Отправляем ответ на ВСЕ порты, которые мы запомнили
for endpoint in &self.client_endpoints {
let _ = socket.send_slice(&data, *endpoint);
}
self.last_activity = std::time::Instant::now(); self.last_activity = std::time::Instant::now();
} }
Err(mpsc::error::TryRecvError::Empty) => break,
Err(mpsc::error::TryRecvError::Disconnected) => {
debug!(%self.core.handle, "Muxer channel disconnected");
socket.close();
return false;
}
}
} }
} }
true true
+369 -201
View File
@@ -3,11 +3,13 @@ use netrunner_core::{
net::network::NetworkConfig, net::network::NetworkConfig,
rawcast::{LocalProtocol, RawCastEvent, RawCastFrame}, rawcast::{LocalProtocol, RawCastEvent, RawCastFrame},
}; };
use netrunner_logger::{debug, error, info, trace}; use netrunner_logger::{debug, error, info, trace, warn};
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, Ipv6Address, Ipv6Packet, TcpPacket, UdpPacket,
},
}; };
use std::{collections::HashMap, time::Instant as StdInstant}; use std::{collections::HashMap, time::Instant as StdInstant};
use tokio::sync::mpsc; use tokio::sync::mpsc;
@@ -94,18 +96,13 @@ impl SessionTracker {
}) })
} }
fn has_udp( fn get_id_by_port(&self, port: u16) -> Option<u64> {
&self, self.active_udp.iter().find_map(|(handle, conn)| {
dst_addr: smoltcp::wire::IpAddress, if conn.has_client(port) {
dst_port: u16, self.handle_to_id.get(handle).copied()
socket_set: &SocketSet, } else {
) -> bool { None
socket_set.iter().any(|(_, s)| {
if let Some(udp) = udp::Socket::downcast(s) {
let ep = udp.endpoint();
return ep.addr == Some(dst_addr) && ep.port == dst_port;
} }
false
}) })
} }
} }
@@ -215,6 +212,7 @@ impl ConnectionManager {
pub fn try_inject_inbound(&mut self, frame: RawCastFrame) -> Result<(), RawCastFrame> { pub fn try_inject_inbound(&mut self, frame: RawCastFrame) -> Result<(), RawCastFrame> {
if frame.event != RawCastEvent::Data { if frame.event != RawCastEvent::Data {
if frame.event == RawCastEvent::Close { if frame.event == RawCastEvent::Close {
info!("💀 [Stream {}] Received CLOSE from tunnel", frame.socket_id);
self.tracker.inbound_tx.remove(&frame.socket_id); self.tracker.inbound_tx.remove(&frame.socket_id);
} }
return Ok(()); return Ok(());
@@ -222,30 +220,36 @@ impl ConnectionManager {
if let Some(tx) = self.tracker.inbound_tx.get(&frame.socket_id) { if let Some(tx) = self.tracker.inbound_tx.get(&frame.socket_id) {
match tx.try_send(frame.payload.clone()) { match tx.try_send(frame.payload.clone()) {
Ok(_) => Ok(()), Ok(_) => {
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => { // Логируем входящие данные (trace чтобы не спамить, но можно временно info)
error!( trace!(
"🟡 WARNING: Inbound channel FULL for stream {}", "📥 [Stream {}] Inbound data: {} bytes",
frame.socket_id frame.socket_id,
frame.payload.len()
); );
Ok(())
}
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
warn!("🟡 [Stream {}] Inbound channel FULL", frame.socket_id);
Err(frame) Err(frame)
} }
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => { Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
error!( error!("🔴 [Stream {}] Inbound channel CLOSED", frame.socket_id);
"🔴 WARNING: Inbound channel CLOSED for stream {}", self.tracker.inbound_tx.remove(&frame.socket_id);
frame.socket_id
);
Ok(()) Ok(())
} }
} }
} else { } else {
// Если это происходит во время Connecting - значит сервер шлет ответы,
// а клиент уже забыл про этот ID или еще не создал его.
error!( error!(
"👻 ORPHAN PACKET: No local socket for stream {}", "👻 [Stream {}] ORPHAN packet from tunnel. ID mismatch?",
frame.socket_id frame.socket_id
); );
Ok(()) 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 {
@@ -275,111 +279,228 @@ impl ConnectionManager {
} }
pub fn try_create_socket_from_packet(&mut self, packet: &[u8], socket_set: &mut SocketSet) { pub fn try_create_socket_from_packet(&mut self, packet: &[u8], socket_set: &mut SocketSet) {
let Ok(ip_packet) = Ipv4Packet::new_checked(packet) else { if packet.is_empty() {
trace!("try_create_socket: Failed to parse IPv4 packet, ignoring"); return;
}
match packet[0] >> 4 {
4 => self.process_ipv4(packet, socket_set),
6 => self.process_ipv6(packet, socket_set),
_ => {}
}
}
fn process_ipv4(&mut self, packet: &[u8], socket_set: &mut SocketSet) {
let Ok(ip) = Ipv4Packet::new_checked(packet) else {
return; return;
}; };
let (src, dst) = (ip.src_addr().into(), ip.dst_addr().into());
let dst_addr = ip_packet.dst_addr(); match ip.next_header() {
match ip_packet.next_header() {
IpProtocol::Tcp => { IpProtocol::Tcp => {
let src_addr = ip_packet.src_addr(); if let Ok(p) = TcpPacket::new_checked(ip.payload()) {
let dst_addr = ip_packet.dst_addr(); self.intercept_tcp(src, dst, p.src_port(), p.dst_port(), socket_set);
if let Ok(tcp_packet) = TcpPacket::new_checked(ip_packet.payload()) {
// 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 endpoint = IpListenEndpoint {
addr: Some(dst_addr.into()),
port: dst_port,
};
match socket.listen(endpoint) {
Ok(_) => {
socket_set.add(socket);
}
Err(e) => {
error!(%dst_addr, dst_port, "Failed to listen: {:?}", e);
}
}
} else {
trace!(%dst_addr, dst_port, src_port, "Socket already exists, ignoring SYN");
}
}
} }
} }
IpProtocol::Udp => { IpProtocol::Udp => {
if let Ok(udp_packet) = UdpPacket::new_checked(ip_packet.payload()) { if let Ok(p) = UdpPacket::new_checked(ip.payload()) {
let dst_port = udp_packet.dst_port(); self.intercept_udp(src, dst, p.src_port(), p.dst_port(), socket_set);
}
}
p => trace!("Skip IPv4 protocol {:?}", p),
}
}
// Блокируем порты локального вещания и NetBIOS fn process_ipv6(&mut self, packet: &[u8], socket_set: &mut SocketSet) {
if dst_port == 0 || dst_port == 137 || dst_port == 138 { let Ok(ip) = Ipv6Packet::new_checked(packet) else {
trace!(%dst_addr, dst_port, "Ignored blocked UDP port"); return;
};
let (src, dst) = (ip.src_addr().into(), ip.dst_addr().into());
match ip.next_header() {
IpProtocol::Tcp => {
if let Ok(p) = TcpPacket::new_checked(ip.payload()) {
self.intercept_tcp(src, dst, p.src_port(), p.dst_port(), socket_set);
}
}
IpProtocol::Udp => {
if let Ok(p) = UdpPacket::new_checked(ip.payload()) {
self.intercept_udp(src, dst, p.src_port(), p.dst_port(), socket_set);
}
}
_ => {}
}
}
// В TCP мы просто открываем "слушающий" сокет на конкретный IP:Port,
// а саму сессию туннеля запустим позже в handle_tcp, когда handshake завершится.
fn intercept_tcp(
&mut self,
src: smoltcp::wire::IpAddress,
dst: smoltcp::wire::IpAddress,
src_p: u16,
dst_p: u16,
socket_set: &mut SocketSet,
) {
if !self.tracker.has_connection_from(src, src_p, socket_set) {
info!(
"🆕 [TCP] New session detected: {}:{} -> {}:{}",
src, src_p, dst, dst_p
);
let mut socket = SocketFactory::create_tcp(dst_p);
let _ = socket.listen(IpListenEndpoint {
addr: Some(dst),
port: dst_p,
});
socket_set.add(socket);
}
}
fn intercept_udp(
&mut self,
src: smoltcp::wire::IpAddress,
dst: smoltcp::wire::IpAddress,
src_p: u16,
dst_p: u16,
socket_set: &mut SocketSet,
) {
if dst_p == 0
|| dst_p == 137
|| dst_p == 138
|| self.tracker.get_id_by_port(src_p).is_some()
{
return; return;
} }
if !self.tracker.has_udp(dst_addr.into(), dst_port, socket_set) { if dst_p == 53 {
debug!(%dst_addr, dst_port, "No active UDP socket found, allocating new one"); debug!("🎯 [DNS] Intercepting local query: {}:{} -> 53", src, src_p);
let mut socket = SocketFactory::create_udp(53);
// Биндимся на адрес назначения, который ожидает клиент (обычно 10.0.0.2 или 8.8.8.8)
if socket
.bind(IpListenEndpoint {
addr: Some(dst),
port: 53,
})
.is_ok()
{
let handle = socket_set.add(socket);
let (conn, _rx_smol, tx_smol) = UdpConnection::new(handle, src, src_p);
let mut socket = SocketFactory::create_udp(dst_port); // Регистрируем, но НЕ спавним задачу туннеля!
let endpoint = IpListenEndpoint { let socket_id = self.tracker.generate_socket_id();
addr: Some(dst_addr.into()), self.tracker.handle_to_id.insert(handle, socket_id);
port: dst_port, self.tracker.active_udp.insert(handle, conn);
self.tracker.inbound_tx.insert(socket_id, tx_smol);
}
return;
}
let socket_id = self.tracker.generate_socket_id();
// ВОТ ТУТ МЫ ИЗВЛЕКАЕМ IP И ДЕЛАЕМ LOOKUP С ЛОГИРОВАНИЕМ
let (dst_ip, target) = match dst {
smoltcp::wire::IpAddress::Ipv4(ip) => {
let std_ip = std::net::Ipv4Addr::from(ip);
if let Some(domain) = self.resolver.fake_ip_store.lookup_by_ip(&std_ip) {
info!(
"🔍 [UDP {}] Reverse lookup MATCH: {} -> {}",
socket_id, std_ip, domain
);
(std_ip, format!("{}:{}", domain, dst_p))
} else {
info!(
"🔍 [UDP {}] Reverse lookup MISS: using raw IP {}",
socket_id, std_ip
);
(std_ip, format!("{}:{}", std_ip, dst_p))
}
}
smoltcp::wire::IpAddress::Ipv6(ip) => {
let std_ip = std::net::Ipv4Addr::new(0, 0, 0, 0);
(std_ip, format!("[{}]:{}", ip, dst_p))
}
}; };
match socket.bind(endpoint) { info!(
Ok(_) => { "🚀 [UDP MASTER] ID:{} | Intercepted: {}:{} -> Target: {}",
debug!(%dst_addr, dst_port, "UDP socket successfully bound"); socket_id, src, src_p, target
socket_set.add(socket);
}
Err(e) => {
error!(%dst_addr, dst_port, "Failed to bind UDP socket: {:?}", e);
}
}
} else {
trace!(%dst_addr, dst_port, "UDP socket already exists, ignoring creation");
}
} else {
trace!("try_create_socket: Failed to parse UDP payload");
}
}
protocol => {
trace!(
"try_create_socket: Ignored unsupported IP protocol: {:?}",
protocol
); );
let mut socket = SocketFactory::create_udp(dst_p);
if socket
.bind(IpListenEndpoint {
addr: Some(dst),
port: dst_p,
})
.is_ok()
{
let handle = socket_set.add(socket);
let (conn, mut rx_smol, tx_smol) = UdpConnection::new(handle, src, src_p);
self.tracker.handle_to_id.insert(handle, socket_id);
self.tracker.active_udp.insert(handle, conn);
self.tracker.inbound_tx.insert(socket_id, tx_smol);
let tx_tunnel = self.tx_to_tunnel.clone();
tokio::spawn(async move {
info!("📡 [UDP {}] Task started for {}", socket_id, target);
// ВОТ ТУТ ИСПРАВЛЕН ХАРДКОД! Передаем dst_ip вместо нулей
let mut frame = RawCastFrame::connect(LocalProtocol::Udp, socket_id, dst_ip, dst_p);
frame.payload = bytes::Bytes::from(target.clone());
info!(
"📦 [UDP {}] Packed Connect frame: dst_ip={}, dst_port={}, payload={}",
socket_id, dst_ip, dst_p, target
);
if tx_tunnel.send(frame).await.is_err() {
error!("❌ [UDP {}] Failed to send CONNECT to tunnel", socket_id);
return;
}
let mut pkt_count = 0;
while let Some((data, ip, port)) = rx_smol.recv().await {
pkt_count += 1;
if pkt_count == 1 {
info!("📤 [UDP {}] First data packet sent to tunnel", socket_id);
}
let df =
RawCastFrame::data(LocalProtocol::Udp, socket_id, ip, port, data.to_vec());
if tx_tunnel.send(df).await.is_err() {
break;
} }
} }
info!(
"🛑 [UDP {}] Task stopped. Sent {} packets",
socket_id, pkt_count
);
});
} }
}
// --- ЦИКЛ ОБРАБОТКИ СОКЕТОВ (Pumping) ---
pub fn process_sockets(&mut self, socket_set: &mut SocketSet) { pub fn process_sockets(&mut self, socket_set: &mut SocketSet) {
for (handle, socket) in socket_set.iter_mut() { for (handle, socket) in socket_set.iter_mut() {
if let Some(tcp) = tcp::Socket::downcast_mut(socket) { if let Some(s) = tcp::Socket::downcast_mut(socket) {
self.handle_tcp(handle, tcp); self.handle_tcp(handle, s);
} else if let Some(udp) = udp::Socket::downcast_mut(socket) { } else if let Some(s) = udp::Socket::downcast_mut(socket) {
self.handle_udp(handle, udp); self.handle_udp(handle, s);
} else if let Some(icmp) = icmp::Socket::downcast_mut(socket) { } else if let Some(s) = icmp::Socket::downcast_mut(socket) {
self.handle_icmp(handle, icmp); // smoltcp использует один тип icmp::Socket для v4 и v6
// разделяем логику по содержимому или конфигурации
self.handle_icmp(handle, s);
self.handle_icmpv6(handle, s);
} }
} }
} }
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); if let Some(id) = self.tracker.handle_to_id.get(&handle) {
info!("🏁 [TCP {}] Connection closed", id);
}
self.tracker.queue_removal(handle); self.tracker.queue_removal(handle);
return; return;
} }
@@ -387,151 +508,198 @@ 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 (dst_ip, dst_port) = match socket.local_endpoint() { let Some(ep) = socket.local_endpoint() else {
Some(ep) => match ep.addr { return;
smoltcp::wire::IpAddress::Ipv4(ip) => (std::net::Ipv4Addr::from(ip), ep.port),
_ => return,
},
None => return,
}; };
info!(%handle, ip = %dst_ip, port = dst_port, "New TCP session intercepted");
let socket_id = self.tracker.generate_socket_id(); let socket_id = self.tracker.generate_socket_id();
let (conn, mut rx_smol, tx_smol, handshake_tx) = TcpConnection::new(handle);
self.tracker.handle_to_id.insert(handle, 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);
self.tracker.active_tcp.insert(handle, conn); self.tracker.active_tcp.insert(handle, conn);
self.tracker.inbound_tx.insert(socket_id, tx_to_smol); self.tracker.inbound_tx.insert(socket_id, tx_smol);
// ИСПРАВЛЕНИЕ ЗДЕСЬ: Переводим IP обратно в домен // ДОБАВЛЕНО ЛОГИРОВАНИЕ ДЛЯ TCP
let target_str = if let Some(domain) = self.resolver.fake_ip_store.lookup_by_ip(&dst_ip) let (dst_ip, target) = match ep.addr {
{ smoltcp::wire::IpAddress::Ipv4(ip) => {
format!("{}:{}", domain, dst_port) let std_ip = std::net::Ipv4Addr::from(ip);
if let Some(domain) = self.resolver.fake_ip_store.lookup_by_ip(&std_ip) {
info!(
"🔍 [TCP {}] Reverse lookup MATCH: {} -> {}",
socket_id, std_ip, domain
);
(std_ip, format!("{}:{}", domain, ep.port))
} else { } else {
format!("{}:{}", dst_ip, dst_port) info!(
"🔍 [TCP {}] Reverse lookup MISS: using raw IP {}",
socket_id, std_ip
);
(std_ip, format!("{}:{}", std_ip, ep.port))
}
}
smoltcp::wire::IpAddress::Ipv6(ip) => (
std::net::Ipv4Addr::new(0, 0, 0, 0),
format!("[{}]:{}", ip, ep.port),
),
}; };
info!("🔗 [TCP {}] established -> {}", socket_id, target);
let tx_tunnel = self.tx_to_tunnel.clone(); let tx_tunnel = self.tx_to_tunnel.clone();
tokio::spawn(async move { tokio::spawn(async move {
// Создаем кадр коннекта let mut frame =
let mut connect_frame = RawCastFrame::connect(LocalProtocol::Tcp, socket_id, dst_ip, ep.port);
RawCastFrame::connect(LocalProtocol::Tcp, socket_id, dst_ip, dst_port); frame.payload = bytes::Bytes::from(target.clone());
// Кладем доменное имя в payload, чтобы ClientHandler его прочитал
connect_frame.payload = bytes::Bytes::from(target_str);
if tx_tunnel.send(connect_frame).await.is_err() { info!(
return; "📦 [TCP {}] Packed Connect frame: dst_ip={}, dst_port={}, payload={}",
} socket_id, dst_ip, ep.port, target
);
if tx_tunnel.send(frame).await.is_ok() {
let _ = handshake_tx.send(()); let _ = handshake_tx.send(());
while let Some(data) = rx_smol.recv().await {
while let Some(data) = rx_from_smol.recv().await { let _ = tx_tunnel
let data_frame = RawCastFrame::data( .send(RawCastFrame::data(
LocalProtocol::Tcp, LocalProtocol::Tcp,
socket_id, socket_id,
dst_ip, dst_ip,
dst_port, ep.port,
data.to_vec(), data.to_vec(),
); ))
if tx_tunnel.send(data_frame).await.is_err() { .await;
break;
} }
} }
let close_frame =
RawCastFrame::close(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
let _ = tx_tunnel.send(close_frame).await;
}); });
} }
if let Some(conn) = self.tracker.active_tcp.get_mut(&handle) { if let Some(conn) = self.tracker.active_tcp.get_mut(&handle) {
if !conn.tick(socket) { if !conn.tick(socket) {
info!("⚠️ [TCP] Tick failed, aborting handle {:?}", handle);
socket.abort(); socket.abort();
} }
} }
} }
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());
// 1. DNS перехват с логированием
if socket.endpoint().port == 53 { if socket.endpoint().port == 53 {
while socket.can_recv() { while let Ok((data, meta)) = socket.recv() {
if let Ok((data, meta)) = socket.recv() { if let Some(res) = self.resolver.process_dns_query(data) {
if let Some(response) = self.resolver.process_dns_query(data) { // Используем debug, чтобы не заливать консоль, но видеть активность
let _ = socket.send_slice(&response, meta); debug!("🔍 [DNS] Resolved query for client {}", meta.endpoint);
if let Err(e) = socket.send_slice(&res, meta) {
warn!("❌ [DNS] Failed to send response: {:?}", e);
} }
} else {
break;
} }
} }
return; return;
} }
if socket.is_open() && !self.tracker.active_udp.contains_key(&handle) { // 2. Жизненный цикл обычных UDP сессий
let (dst_ip, dst_port) = match socket.endpoint().addr {
Some(smoltcp::wire::IpAddress::Ipv4(ip)) => {
(std::net::Ipv4Addr::from(ip), socket.endpoint().port)
}
_ => 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);
self.tracker.active_udp.insert(handle, conn);
self.tracker.inbound_tx.insert(socket_id, tx_to_smol);
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 {
let mut connect_frame =
RawCastFrame::connect(LocalProtocol::Udp, socket_id, dst_ip, dst_port);
connect_frame.payload = bytes::Bytes::from(target_str); // Кладем домен
let _ = tx_tunnel.send(connect_frame).await;
while let Some(data) = rx_from_smol.recv().await {
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;
}
}
let close_frame =
RawCastFrame::close(LocalProtocol::Udp, socket_id, dst_ip, dst_port);
let _ = tx_tunnel.send(close_frame).await;
});
}
if let Some(conn) = self.tracker.active_udp.get_mut(&handle) { if let Some(conn) = self.tracker.active_udp.get_mut(&handle) {
if !conn.tick(socket) { if !conn.tick(socket) {
if let Some(socket_id) = self.tracker.handle_to_id.get(&handle) {
info!("🛑 [UDP {}] Session expired or closed by tick", socket_id);
}
self.tracker.queue_removal(handle); self.tracker.queue_removal(handle);
self.tracker.active_udp.remove(&handle);
} }
} }
} }
fn handle_icmp(&mut self, _handle: SocketHandle, socket: &mut icmp::Socket) { fn handle_icmp(&mut self, _handle: SocketHandle, socket: &mut icmp::Socket) {
if socket.can_recv() { if !socket.can_recv() {
let _ = socket.recv(); return;
}
match socket.recv() {
Ok((data, src_addr)) => {
let Ok(pkt) = smoltcp::wire::Icmpv4Packet::new_checked(data) else {
return;
};
match pkt.msg_type() {
smoltcp::wire::Icmpv4Message::EchoRequest => {
info!("🏓 [ICMPv4] Ping Request from {}", src_addr);
// Формируем ответ (Echo Reply)
let mut reply_data = data.to_vec();
let mut reply_pkt =
smoltcp::wire::Icmpv4Packet::new_unchecked(&mut reply_data);
reply_pkt.set_msg_type(smoltcp::wire::Icmpv4Message::EchoReply);
reply_pkt.fill_checksum();
if let Err(e) = socket.send_slice(&reply_data, src_addr) {
warn!("❌ [ICMPv4] Reply failed to {}: {:?}", src_addr, e);
} else {
info!("✅ [ICMPv4] Echo Reply sent to {}", src_addr);
}
}
smoltcp::wire::Icmpv4Message::DstUnreachable => {
warn!(
"🚫 [ICMPv4] Destination Unreachable from {}. Check MTU!",
src_addr
);
}
_ => debug!("📡 [ICMPv4] Message {:?} from {}", pkt.msg_type(), src_addr),
}
}
Err(e) => trace!("ICMPv4 recv error: {:?}", e),
}
}
fn handle_icmpv6(&mut self, _handle: SocketHandle, socket: &mut icmp::Socket) {
if !socket.can_recv() {
return;
}
match socket.recv() {
Ok((data, src_addr)) => {
// 1. Извлекаем конкретно Ipv6Address из перечисления IpAddress
let smoltcp::wire::IpAddress::Ipv6(ipv6_src) = src_addr else {
return; // Если пришел не IPv6, выходим
};
let Ok(pkt) = smoltcp::wire::Icmpv6Packet::new_checked(data) else {
return;
};
match pkt.msg_type() {
smoltcp::wire::Icmpv6Message::EchoRequest => {
info!("🏓 [ICMPv6] Ping Request from {}", ipv6_src);
let mut reply_data = data.to_vec();
let mut reply_pkt =
smoltcp::wire::Icmpv6Packet::new_unchecked(&mut reply_data);
reply_pkt.set_msg_type(smoltcp::wire::Icmpv6Message::EchoReply);
// 2. РАСЧЕТ ЧЕКСУММЫ (Критический момент)
let my_v6_gateway =
smoltcp::wire::Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
reply_pkt.fill_checksum(&my_v6_gateway, &ipv6_src);
if let Err(e) = socket.send_slice(&reply_data, src_addr) {
warn!("❌ [ICMPv6] Failed to send reply: {:?}", e);
} else {
info!("✅ [ICMPv6] Echo Reply sent to {}", src_addr);
}
}
smoltcp::wire::Icmpv6Message::DstUnreachable => {
warn!("🚫 [ICMPv6] Destination Unreachable from {}", ipv6_src);
}
smoltcp::wire::Icmpv6Message::PktTooBig => {
warn!(
"📏 [ICMPv6] Packet Too Big! MTU issue detected at {}",
ipv6_src
);
}
_ => {}
}
}
Err(e) => trace!("ICMPv6 recv error: {:?}", e),
} }
} }
+28 -29
View File
@@ -1,8 +1,10 @@
use std::sync::Arc;
use crate::net::connection::muxer::{MuxMessage, Muxer}; use crate::net::connection::muxer::{MuxMessage, Muxer};
use crate::net::network::NetworkConfig; use crate::net::network::NetworkConfig;
use crate::nrxp::FrameType; use crate::nrxp::FrameType;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use netrunner_logger::{debug, error}; use netrunner_logger::{debug, error, info};
use tokio::net::UdpSocket; use tokio::net::UdpSocket;
use tokio::sync::mpsc; use tokio::sync::mpsc;
@@ -10,7 +12,7 @@ pub(crate) async fn run_tcp_bridge<R, W>(
stream_id: u32, stream_id: u32,
mut reader: R, mut reader: R,
mut writer: W, mut writer: W,
muxer: Muxer, muxer: Arc<Muxer>,
mut v_rx: mpsc::Receiver<Bytes>, mut v_rx: mpsc::Receiver<Bytes>,
) where ) where
R: tokio::io::AsyncReadExt + Unpin, R: tokio::io::AsyncReadExt + Unpin,
@@ -71,61 +73,58 @@ pub(crate) async fn run_tcp_bridge<R, W>(
muxer.remove_stream(stream_id); muxer.remove_stream(stream_id);
} }
pub(crate) async fn run_udp_bridge( pub(crate) async fn run_udp_bridge(
stream_id: u32, stream_id: u32,
socket: UdpSocket, socket: UdpSocket,
muxer: Muxer, muxer: Arc<Muxer>,
mut v_rx: mpsc::Receiver<Bytes>, mut v_rx: mpsc::Receiver<Bytes>,
) { ) {
let mut buf = BytesMut::with_capacity(NetworkConfig::global().udp_buffer_size); let config = NetworkConfig::global();
let mut buf = vec![0u8; config.udp_buffer_size];
info!(
"🌉 [UDP {}] Bridge active. Media traffic leg optimization enabled.",
stream_id
);
loop { loop {
tokio::select! { tokio::select! {
// Данные ИЗ ИНТЕРНЕТА -> В ТУННЕЛЬ
res = socket.recv(&mut buf) => { res = socket.recv(&mut buf) => {
match res { match res {
Ok(0) => break, Ok(n) if n > 0 => {
Ok(n) => { let data = Bytes::copy_from_slice(&buf[..n]);
let msg = MuxMessage {
stream_id, // КРИТИЧЕСКИЙ МОМЕНТ: передаем true (is_udp),
frame_type: FrameType::UdpData, // чтобы Muxer выбрал правильную ногу для реалтайм трафика
data: Bytes::copy_from_slice(&buf[..n]), if let Err(e) = muxer.send_data_safe(stream_id, data, true).await {
}; error!("❌ [UDP {}] Failed to send to tunnel: {}", stream_id, e);
if muxer.send_to_network(msg).await.is_err() { break; } break;
} }
}
Ok(_) => break,
Err(e) => { Err(e) => {
error!(stream_id, error = %e, "UDP socket read error"); error!("❌ [UDP {}] Internet read error: {}", stream_id, e);
break; break;
} }
} }
} }
// Данные ИЗ ТУННЕЛЯ -> В ИНТЕРНЕТ
maybe_data = v_rx.recv() => { maybe_data = v_rx.recv() => {
match maybe_data { match maybe_data {
Some(data) => { Some(data) => {
if let Err(e) = socket.send(&data).await { if let Err(e) = socket.send(&data).await {
error!(stream_id, error = %e, "UDP socket write error"); error!("❌ [UDP {}] Internet write error: {}", stream_id, e);
break; break;
} }
} }
None => { None => break,
debug!(stream_id, "Virtual channel closed (UDP)");
break;
}
} }
} }
} }
} }
let _ = muxer info!("🔌 [UDP {}] Bridge closed", stream_id);
.send_to_network(MuxMessage {
stream_id,
frame_type: FrameType::Close,
data: Bytes::new(),
})
.await;
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
muxer.remove_stream(stream_id); muxer.remove_stream(stream_id);
} }
+243 -120
View File
@@ -1,14 +1,18 @@
use std::{net::Ipv4Addr, sync::Arc};
use crate::{ use crate::{
net::{ net::{
connection::{engine::TunnelEngine, handler::StreamHandler, muxer::Muxer}, connection::{engine::TunnelEngine, handler::StreamHandler, muxer::Muxer},
network::NetworkConfig, network::NetworkConfig,
}, },
nrxp::{Codec, ErrorAction, FrameType}, nrxp::{Codec, ErrorAction, Frame, FrameType},
rawcast::{LocalProtocol, RawCastEvent, RawCastFrame}, rawcast::{LocalProtocol, RawCastAdapter, RawCastFrame},
tlseng::BrowserProfile, tlseng::BrowserProfile,
}; };
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use dashmap::DashMap;
use netrunner_logger::{error, info, warn}; use netrunner_logger::{error, info, warn};
use rand::Rng;
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
net::{ net::{
@@ -18,6 +22,38 @@ use tokio::{
sync::mpsc, sync::mpsc,
}; };
pub struct SessionManager {
sessions: DashMap<String, Arc<Muxer>>,
}
impl SessionManager {
pub fn new() -> Self {
Self {
sessions: DashMap::new(),
}
}
// Генерация переехала сюда
pub fn generate_id() -> String {
let mut rng = rand::rng();
format!("{:016x}{:016x}", rng.next_u64(), rng.next_u64())
}
pub fn get_or_create(&self, session_id: &str) -> Arc<Muxer> {
self.sessions
.entry(session_id.to_string())
.or_insert_with(|| Arc::new(Muxer::new(false)))
.clone()
}
// Удаление мертвых сессий, чтобы не текла память
pub fn remove(&self, session_id: &str) {
if self.sessions.remove(session_id).is_some() {
netrunner_logger::info!("🧹 Session {} completely closed and cleaned up", session_id);
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConnectionRole { pub enum ConnectionRole {
Client, Client,
@@ -57,14 +93,19 @@ impl Connection {
} }
} }
type StreamContext = (Ipv4Addr, u16, LocalProtocol);
pub struct ClientHandler; pub struct ClientHandler;
impl ClientHandler { impl ClientHandler {
/// Вспомогательная функция: устанавливает одно физическое TLS-соединение async fn establish_leg(
/// и возвращает готовый Muxer для работы с ним. remote_proxy_addr: &str,
async fn establish_leg(remote_proxy_addr: &str, leg_name: &str) -> Result<Muxer, String> { leg_id: u32,
muxer: Arc<Muxer>,
session_id: &str,
) -> Result<(), String> {
let leg_name = if leg_id == 0 { "TCP-Leg" } else { "UDP-Leg" };
info!( info!(
"Establishing dedicated {} tunnel to {}...", "Establishing dedicated {} to {}...",
leg_name, remote_proxy_addr leg_name, remote_proxy_addr
); );
@@ -72,12 +113,11 @@ impl ClientHandler {
.await .await
.map_err(|e| format!("Failed to connect: {}", e))?; .map_err(|e| format!("Failed to connect: {}", e))?;
if let Err(e) = stream.set_nodelay(true) { let _ = stream.set_nodelay(true);
warn!("Failed to set TCP_NODELAY on {} leg: {}", leg_name, e);
}
let (inbound, outbound) = stream.into_split(); let (inbound, outbound) = stream.into_split();
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, "ubuntu.com") .make_client_handshake(&BrowserProfile::CHROME_131, "ubuntu.com")
@@ -98,19 +138,39 @@ impl ClientHandler {
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
if n == 0 { if n == 0 {
return Err(format!("EOF during handshake on {} leg", leg_name)); return Err(format!("EOF on {}", leg_name));
} }
} }
Err(e) => return Err(format!("TLS error on {} leg: {:?}", leg_name, e)), Err(e) => return Err(format!("TLS error on {}: {:?}", leg_name, e)),
} }
} }
info!("{} TLS Handshake complete. Starting Engine.", leg_name); info!("{} TLS Handshake complete.", leg_name);
let handshake_payload = Bytes::from(format!("{}:{}", session_id, leg_id));
// 2. Шифруем через Codec! (Внутри сгенерится auth_tag)
let encrypted_handshake = conn
.codec
.encrypt_data(0, FrameType::Handshake, handshake_payload)
.map_err(|e| format!("Failed to encrypt Handshake: {:?}", e))?;
// 3. Отправляем в сокет
conn.outbound
.write_all(&encrypted_handshake)
.await
.map_err(|e| format!("Failed to send Handshake: {}", e))?;
info!(
"{} Sent Encrypted Handshake (Leg: {}). Starting engine...",
leg_name, leg_id
);
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity); let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
let (data_tx, data_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); // ОБНОВЛЕННЫЙ ВЫЗОВ: передаем leg_id
muxer.add_leg(leg_id, control_tx, data_tx);
let handler = let handler =
std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Client)); std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Client));
@@ -124,114 +184,123 @@ impl ClientHandler {
handler, handler,
}; };
tokio::spawn(async move { engine.run().await }); engine.run().await;
Err(format!("{} Engine stopped", leg_name))
Ok(muxer)
} }
/// Главная функция запуска клиента
pub async fn connect( pub async fn connect(
remote_proxy_addr: &str, remote_proxy_addr: &str,
mut rx_from_engine: mpsc::Receiver<RawCastFrame>, mut rx_from_engine: mpsc::Receiver<RawCastFrame>,
tx_to_engine: mpsc::Sender<RawCastFrame>, tx_to_engine: mpsc::Sender<RawCastFrame>,
) -> Result<(), String> { ) -> Result<(), String> {
// 1. Устанавливаем ДВА независимых соединения let muxer = Arc::new(Muxer::new(true));
// Мы можем сделать это параллельно через tokio::try_join! для скорости let registry: Arc<DashMap<u32, StreamContext>> = Arc::new(DashMap::new());
let (tcp_muxer, udp_muxer) = tokio::try_join!(
Self::establish_leg(remote_proxy_addr, "TCP"),
Self::establish_leg(remote_proxy_addr, "UDP"),
)?;
info!("Dual-tunnel architecture established successfully!"); let mut rng = rand::rng();
let session_id = format!("{:016x}{:016x}", rng.next_u64(), rng.next_u64());
info!("🔑 Generated Master Session ID: {}", session_id);
// 2. ЗАПУСКАЕМ МОСТ-РОУТЕР for id in 0..2 {
let addr = remote_proxy_addr.to_string();
let m = muxer.clone();
let sid = session_id.clone();
tokio::spawn(async move { tokio::spawn(async move {
while let Some(raw_frame) = rx_from_engine.recv().await { loop {
let stream_id = raw_frame.socket_id as u32; if let Err(e) = Self::establish_leg(&addr, id, m.clone(), &sid).await {
let is_udp = raw_frame.protocol == LocalProtocol::Udp; error!("Leg {} failed: {}. Reconnecting...", id, e);
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
// РОУТИНГ: Выбираем нужный физический канал
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
{
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 {
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;
} }
} }
muxer_clone.remove_stream(stream_id);
}); });
} }
RawCastEvent::Data => { info!("🚀 Netrunner Dual-Leg Tunnel Active.");
let frame_type = if is_udp {
let muxer_inner = muxer.clone();
tokio::spawn(async move {
while let Some(raw_frame) = rx_from_engine.recv().await {
// 1. Сохраняем метаданные для локального реестра (до того, как raw_frame исчезнет)
let dst_ip = raw_frame.dst_ip;
let dst_port = raw_frame.dst_port;
let protocol = raw_frame.protocol;
let is_udp = protocol == LocalProtocol::Udp;
// 2. Вся магия здесь: адаптер сам решит, это Connect или Data, и склеит IP:Port
let nrxp_frame = match RawCastAdapter::to_nrxp(raw_frame) {
Ok(frame) => frame,
Err(e) => {
error!("⚠️ [Adapter] Failed to cast frame: {}", e);
continue;
}
};
let stream_id = nrxp_frame.header.stream_id;
let f_type = nrxp_frame.header.frame_type;
let payload = nrxp_frame.payload;
// 3. Маршрутизация на основе ГОТОВОГО FrameType
match f_type {
FrameType::Connect | FrameType::UdpConnect => {
info!(
"🔗 [Muxer] Forwarding {} request to {} (stream {})",
if is_udp { "UDP" } else { "TCP" },
String::from_utf8_lossy(&payload), // Адаптер уже положил сюда "IP:Port"
stream_id
);
registry.insert(stream_id, (dst_ip, dst_port, protocol));
let (v_tx, mut v_rx) =
mpsc::channel(NetworkConfig::global().tcp_buffer_size);
muxer_inner.register_stream(stream_id, v_tx);
let tx_to_tun = tx_to_engine.clone();
let reg = registry.clone();
tokio::spawn(async move {
while let Some(back_payload) = v_rx.recv().await {
if let Some(ctx) = reg.get(&stream_id) {
let (ip, port, proto) = *ctx;
let out_f_type = if proto == LocalProtocol::Udp {
FrameType::UdpData FrameType::UdpData
} else { } else {
FrameType::Data FrameType::Data
}; };
let mock_nrxp = Frame::new(stream_id, out_f_type, back_payload);
if let Err(e) = muxer // Здесь from_nrxp уже используется у тебя правильно!
.send_control(stream_id, frame_type, raw_frame.payload) if let Ok(raw) = RawCastAdapter::from_nrxp(
.await mock_nrxp,
{ ip,
error!("Failed to send data frame: {}", e); port,
proto == LocalProtocol::Udp,
) {
let _ = tx_to_tun.send(raw).await;
} }
} }
}
});
RawCastEvent::Close => { // Отправляем управляющий фрейм с адресом
let _ = muxer let _ = muxer_inner.send_control(stream_id, f_type, payload).await;
}
FrameType::Data | FrameType::UdpData => {
let _ = muxer_inner.send_data_safe(stream_id, payload, is_udp).await;
}
FrameType::Close => {
let _ = muxer_inner
.send_control(stream_id, FrameType::Close, Bytes::new()) .send_control(stream_id, FrameType::Close, Bytes::new())
.await; .await;
muxer.remove_stream(stream_id); muxer_inner.remove_stream(stream_id);
}
_ => {
warn!("Unhandled FrameType from adapter: {:?}", f_type);
} }
} }
} }
info!("ClientHandler bridge task terminated.");
}); });
Ok(()) Ok(())
@@ -239,9 +308,17 @@ impl ClientHandler {
} }
pub struct ServerHandler { pub struct ServerHandler {
pub(crate) conn: Connection, pub(crate) conn: Connection,
pub(crate) session_manager: Arc<SessionManager>,
} }
impl ServerHandler { impl ServerHandler {
pub fn new(connection: Connection) -> Self {
Self {
conn: connection,
session_manager: Arc::new(SessionManager::new()),
}
}
async fn handle_stealth_fallback( async fn handle_stealth_fallback(
mut client_inbound: OwnedReadHalf, mut client_inbound: OwnedReadHalf,
mut client_outbound: OwnedWriteHalf, mut client_outbound: OwnedWriteHalf,
@@ -288,20 +365,17 @@ impl ServerHandler {
} }
} }
} }
use crate::parser::Parser; // Не забудь импортировать трейт Parser!
#[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().muxer_capacity); // 1. TLS Хендшейк (без изменений)
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
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);
let hello = loop { let hello = loop {
let buf_snapshot = self.conn.read_buf.clone().freeze(); let buf_snapshot = self.conn.read_buf.clone().freeze();
match self match self
.conn .conn
.codec .codec
@@ -309,19 +383,15 @@ impl TunnelHandler for ServerHandler {
{ {
Ok(b) => break b, Ok(b) => break b,
Err(e) if e.action == ErrorAction::Wait => { Err(e) if e.action == ErrorAction::Wait => {
let read_res = tokio::time::timeout( let res = tokio::time::timeout(
handshake_timeout, handshake_timeout,
self.conn.inbound.read_buf(&mut self.conn.read_buf), self.conn.inbound.read_buf(&mut self.conn.read_buf),
) )
.await; .await;
match res {
match read_res { Ok(Ok(0)) => return Err("Client closed".into()),
Ok(Ok(n)) if n == 0 => return Err("Client closed".into()),
Ok(Ok(_)) => continue, Ok(Ok(_)) => continue,
Ok(Err(e)) => return Err(e.to_string()), _ => {
Err(_) => {
warn!("Handshake timeout. Going stealth.");
ServerHandler::handle_stealth_fallback( ServerHandler::handle_stealth_fallback(
self.conn.inbound, self.conn.inbound,
self.conn.outbound, self.conn.outbound,
@@ -332,14 +402,7 @@ impl TunnelHandler for ServerHandler {
} }
} }
} }
Err(e) => { Err(_) => {
warn!("Auth/Format failed: {:?}. Going stealth.", e);
info!(
"DEBUG: Restoring {} bytes from snapshot for fallback",
buf_snapshot.len()
);
ServerHandler::handle_stealth_fallback( ServerHandler::handle_stealth_fallback(
self.conn.inbound, self.conn.inbound,
self.conn.outbound, self.conn.outbound,
@@ -357,9 +420,61 @@ impl TunnelHandler for ServerHandler {
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let handler = std::sync::Arc::new(StreamHandler::new(muxer, ConnectionRole::Server)); // 1. ЧИТАЕМ ЗАШИФРОВАННЫЙ HANDSHAKE ЧЕРЕЗ CODEC
let (session_id, leg_id) = loop {
let n = tokio::time::timeout(
std::time::Duration::from_secs(3),
self.conn.inbound.read_buf(&mut self.conn.read_buf),
)
.await
.map_err(|_| "Timeout waiting for Handshake".to_string())?
.map_err(|e| e.to_string())?;
TunnelEngine { if n == 0 {
return Err("Client closed connection before Handshake".into());
}
// Кодек сам расшифрует и проверит auth_tag
match self.conn.codec.inbound(&mut self.conn.read_buf) {
Ok(Some(frame)) => {
if frame.header.frame_type == FrameType::Handshake {
let payload_str = String::from_utf8_lossy(&frame.payload).to_string();
let parts: Vec<&str> = payload_str.split(':').collect();
if parts.len() == 2 {
let sid = parts[0].to_string();
let lid: u32 = parts[1].parse().unwrap_or(0);
info!(
"🤝 Secure Handshake verified! Session: {}, Leg: {}",
sid, lid
);
break (sid, lid);
} else {
return Err("Invalid Handshake format".into());
}
} else {
return Err("Invalid first frame (Expected Handshake)".into());
}
}
Ok(None) => continue, // Ждем еще байт
Err(e) => return Err(format!("Decryption/Auth failed during Handshake: {:?}", e)),
}
};
// 2. РЕГИСТРИРУЕМ НОГУ
let muxer = self.session_manager.get_or_create(&session_id);
let (control_tx, control_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
let (data_tx, data_rx) = mpsc::channel(NetworkConfig::global().muxer_capacity);
// ОБНОВЛЕННЫЙ ВЫЗОВ: регистрируем конкретный ID ноги
muxer.add_leg(leg_id, control_tx, data_tx);
let handler =
std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Server));
// 3. ЗАПУСКАЕМ ДВИЖОК
let engine = TunnelEngine {
inbound: self.conn.inbound, inbound: self.conn.inbound,
outbound: self.conn.outbound, outbound: self.conn.outbound,
codec: self.conn.codec, codec: self.conn.codec,
@@ -367,8 +482,16 @@ impl TunnelHandler for ServerHandler {
control_rx, control_rx,
data_rx, data_rx,
handler, handler,
};
let _ = engine.run().await;
// 4. ОЧИСТКА ПОСЛЕ ОТКЛЮЧЕНИЯ
muxer.remove_leg(leg_id);
if muxer.active_legs_count() == 0 {
self.session_manager.remove(&session_id);
} }
.run()
.await Ok(())
} }
} }
+74 -33
View File
@@ -1,5 +1,7 @@
use std::sync::Arc;
use bytes::Bytes; use bytes::Bytes;
use netrunner_logger::{debug, error, info}; use netrunner_logger::{debug, error, info, warn};
use crate::{ use crate::{
net::{ net::{
@@ -14,18 +16,23 @@ use crate::{
}; };
pub(crate) struct StreamHandler { pub(crate) struct StreamHandler {
muxer: Muxer, muxer: Arc<Muxer>,
role: ConnectionRole, role: ConnectionRole,
} }
impl StreamHandler { impl StreamHandler {
pub(crate) fn new(muxer: Muxer, role: ConnectionRole) -> Self { pub(crate) fn new(muxer: Arc<Muxer>, role: ConnectionRole) -> Self {
Self { muxer, role } Self { muxer, role }
} }
pub(crate) async fn handle(&self, frame: Frame) { pub(crate) async fn handle(&self, frame: Frame) {
let stream_id = frame.header.stream_id; let stream_id = frame.header.stream_id;
info!(
stream_id,
"📥 [Tunnel] Received frame: {:?}", frame.header.frame_type
);
match frame.header.frame_type { match frame.header.frame_type {
FrameType::Connect => self.on_connect(stream_id, frame.payload).await, FrameType::Connect => self.on_connect(stream_id, frame.payload).await,
FrameType::UdpConnect => self.on_udp_connect(stream_id, frame.payload).await, FrameType::UdpConnect => self.on_udp_connect(stream_id, frame.payload).await,
@@ -35,10 +42,11 @@ impl StreamHandler {
_ => debug!(stream_id, "Unhandled frame type"), _ => debug!(stream_id, "Unhandled frame type"),
} }
} }
async fn on_connect(&self, stream_id: u32, payload: Bytes) { async fn on_connect(&self, stream_id: u32, payload: Bytes) {
if self.role == ConnectionRole::Server {
let target_str = String::from_utf8_lossy(&payload).to_string(); let target_str = String::from_utf8_lossy(&payload).to_string();
if self.role == ConnectionRole::Server {
info!(stream_id, target = %target_str, "🌐 [TCP] Request to establish remote connection");
let muxer = self.muxer.clone(); let muxer = self.muxer.clone();
let (v_tx, v_rx) = let (v_tx, v_rx) =
@@ -47,41 +55,46 @@ impl StreamHandler {
tokio::spawn(async move { tokio::spawn(async move {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
info!(stream_id, target = %target_str, "Attempting remote TCP connection"); info!(
stream_id,
"⏳ [TCP Worker] Attempting connection to {}", target_str
);
let connect_timeout = tokio::time::timeout( let connect_res = tokio::time::timeout(
std::time::Duration::from_secs(5), std::time::Duration::from_secs(7),
tokio::net::TcpStream::connect(&target_str), tokio::net::TcpStream::connect(&target_str),
) )
.await; .await;
match connect_timeout { match connect_res {
Ok(Ok(stream)) => { Ok(Ok(stream)) => {
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, "✅ [TCP] Established in {:?}. Starting bridge.", elapsed);
// Больше никаких ответов (Reply), просто прокидываем байты!
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;
info!(stream_id, "🔚 [TCP Worker] Bridge task finished");
} }
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::close_stream(&muxer, stream_id).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, "⏰ [TCP] Connection timeout (DNS or IP unreachable)");
Self::close_stream(&muxer, stream_id).await; Self::close_stream(&muxer, stream_id).await;
} }
} }
}); });
} else { } else {
info!(stream_id, "📲 [TCP] Dispatching payload to local stack");
self.muxer.dispatch_to_local(stream_id, payload).await; self.muxer.dispatch_to_local(stream_id, payload).await;
} }
} }
async fn on_udp_connect(&self, stream_id: u32, payload: Bytes) { async fn on_udp_connect(&self, stream_id: u32, payload: Bytes) {
if self.role == ConnectionRole::Server {
let target_str = String::from_utf8_lossy(&payload).to_string(); let target_str = String::from_utf8_lossy(&payload).to_string();
if self.role == ConnectionRole::Server {
info!(stream_id, target = %target_str, "🚀 [UDP] Request to establish remote bridge");
let muxer = self.muxer.clone(); let muxer = self.muxer.clone();
let (v_tx, v_rx) = let (v_tx, v_rx) =
@@ -89,47 +102,75 @@ impl StreamHandler {
muxer.register_stream(stream_id, v_tx); muxer.register_stream(stream_id, v_tx);
tokio::spawn(async move { tokio::spawn(async move {
info!(stream_id, target = %target_str, "Attempting remote UDP connection"); info!(
stream_id,
"⏳ [UDP Worker] Binding socket for {}", target_str
);
match tokio::net::UdpSocket::bind("0.0.0.0:0").await { let socket_res = tokio::net::UdpSocket::bind("[::]:0").await;
Ok(socket) => { let socket = match socket_res {
if let Err(e) = socket.connect(&target_str).await { Ok(s) => s,
error!(stream_id, target = %target_str, error = %e, "UDP connect failed"); Err(e) => {
Self::close_stream(&muxer, stream_id).await; warn!(
return; stream_id,
"⚠️ [UDP] IPv6 bind failed, falling back to IPv4: {}", e
);
tokio::net::UdpSocket::bind("0.0.0.0:0")
.await
.expect("UDP bind fail")
} }
};
// Успех - просто начинаем слушать UDP и слать в туннель match socket.connect(&target_str).await {
Ok(_) => {
let local = socket.local_addr().unwrap();
let remote = socket.peer_addr().unwrap();
info!(
"✅ [UDP {}] Socket ready. Local: {}, Remote: {}. Starting bridge.",
stream_id, local, remote
);
run_udp_bridge(stream_id, socket, muxer, v_rx).await; run_udp_bridge(stream_id, socket, muxer, v_rx).await;
info!(stream_id, "🔚 [UDP Worker] Bridge task finished");
} }
Err(e) => { Err(e) => {
error!(stream_id, target = %target_str, error = %e, "UDP bind failed"); error!(stream_id, target = %target_str, error = %e, "❌ [UDP] Target connect failed");
Self::close_stream(&muxer, stream_id).await; Self::close_stream(&muxer, stream_id).await;
} }
} }
}); });
} else { } else {
info!(
stream_id,
"📲 [UDP] Dispatching connection payload to local stack"
);
self.muxer.dispatch_to_local(stream_id, payload).await; self.muxer.dispatch_to_local(stream_id, payload).await;
} }
} }
async fn on_data(&self, stream_id: u32, payload: Bytes) {
// Здесь info может быть избыточным при большой нагрузке, но для отладки полезно
debug!(stream_id, "📦 [TCP Data] Size: {} bytes", payload.len());
self.muxer.dispatch_to_local(stream_id, payload).await;
}
async fn on_udp_data(&self, stream_id: u32, payload: Bytes) { async fn on_udp_data(&self, stream_id: u32, payload: Bytes) {
debug!(stream_id, "📦 [UDP Data] Size: {} bytes", payload.len());
self.muxer.dispatch_to_local(stream_id, payload).await; self.muxer.dispatch_to_local(stream_id, payload).await;
} }
// Вспомогательная функция вместо send_error_reply async fn on_close(&self, stream_id: u32) {
info!(stream_id, "🏁 [Close] Received close signal for stream");
self.muxer.remove_stream(stream_id);
}
async fn close_stream(muxer: &Muxer, stream_id: u32) { async fn close_stream(muxer: &Muxer, stream_id: u32) {
info!(
stream_id,
"📡 [Control] Sending CLOSE signal to remote peer"
);
let _ = muxer let _ = muxer
.send_control(stream_id, FrameType::Close, Bytes::new()) .send_control(stream_id, FrameType::Close, Bytes::new())
.await; .await;
muxer.remove_stream(stream_id); muxer.remove_stream(stream_id);
} }
async fn on_data(&self, stream_id: u32, payload: Bytes) {
self.muxer.dispatch_to_local(stream_id, payload).await;
}
async fn on_close(&self, stream_id: u32) {
self.muxer.remove_stream(stream_id);
}
} }
+107 -51
View File
@@ -2,10 +2,18 @@ use bytes::Bytes;
use dashmap::DashMap; use dashmap::DashMap;
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc::{error::SendError, Sender}; use tokio::sync::mpsc::Sender;
use crate::net::network::NetworkConfig;
use crate::nrxp::FrameType; use crate::nrxp::FrameType;
// Вспомогательная структура для отдельного TLS-соединения
#[derive(Clone)]
struct MuxLeg {
control_tx: Sender<MuxMessage>,
data_tx: Sender<MuxMessage>,
}
struct IdGenerator { struct IdGenerator {
counter: AtomicU32, counter: AtomicU32,
} }
@@ -31,67 +39,129 @@ pub struct MuxMessage {
#[derive(Clone)] #[derive(Clone)]
pub struct Muxer { pub struct Muxer {
control_tx: Sender<MuxMessage>, legs: Arc<DashMap<u32, MuxLeg>>,
data_tx: Sender<MuxMessage>,
streams: Arc<DashMap<u32, Sender<Bytes>>>, streams: Arc<DashMap<u32, Sender<Bytes>>>,
id_gen: Arc<IdGenerator>, id_gen: Arc<IdGenerator>,
leg_selector: Arc<AtomicU32>,
} }
impl Muxer { impl Muxer {
pub fn new( pub fn new(is_client: bool) -> Self {
control_tx: Sender<MuxMessage>,
data_tx: Sender<MuxMessage>,
is_client: bool,
) -> Self {
Self { Self {
control_tx, legs: Arc::new(DashMap::new()),
data_tx,
streams: Arc::new(DashMap::new()), streams: Arc::new(DashMap::new()),
id_gen: Arc::new(IdGenerator::new(is_client)), id_gen: Arc::new(IdGenerator::new(is_client)),
leg_selector: Arc::new(AtomicU32::new(0)),
// leg_id_gen удален!
} }
} }
pub fn next_id(&self) -> u32 { // ТЕПЕРЬ leg_id ПЕРЕДАЕТСЯ СНАРУЖИ
self.id_gen.next() pub fn add_leg(
&self,
leg_id: u32,
control_tx: Sender<MuxMessage>,
data_tx: Sender<MuxMessage>,
) {
self.legs.insert(
leg_id,
MuxLeg {
control_tx,
data_tx,
},
);
netrunner_logger::info!(leg_id, "MUXER: Leg registered");
} }
pub async fn send_to_network(&self, message: MuxMessage) -> Result<(), SendError<MuxMessage>> { pub fn remove_leg(&self, leg_id: u32) {
self.data_tx.send(message).await self.legs.remove(&leg_id);
netrunner_logger::info!(leg_id, "MUXER: Leg removed");
} }
pub async fn send_data_safe(&self, stream_id: u32, mut data: Bytes) -> Result<(), String> { pub fn active_legs_count(&self) -> usize {
// Лимит полезной нагрузки, чтобы вместе с заголовком и паддингом self.legs.len()
// пакет оставался в пределах ~1400-1450 байт. }
// Возвращаем кортеж (leg_id, MuxLeg), чтобы знать, кого удалять в случае ошибки
fn select_leg(&self, frame_type: &FrameType) -> Option<(u32, MuxLeg)> {
if self.legs.is_empty() {
return None;
}
match frame_type {
FrameType::UdpData | FrameType::UdpConnect => {
// Пытаемся взять UDP ногу (1), если её нет — берем TCP (0) как фоллбэк
if let Some(leg) = self.legs.get(&1) {
Some((1, leg.clone()))
} else if let Some(leg) = self.legs.get(&0) {
Some((0, leg.clone()))
} else {
None
}
}
_ => {
// Пытаемся взять TCP ногу (0), фоллбэк на UDP (1)
if let Some(leg) = self.legs.get(&0) {
Some((0, leg.clone()))
} else if let Some(leg) = self.legs.get(&1) {
Some((1, leg.clone()))
} else {
None
}
}
}
}
pub async fn send_to_network(&self, message: MuxMessage) -> Result<(), String> {
let (leg_id, leg) = self
.select_leg(&message.frame_type)
.ok_or_else(|| "MUXER: No active legs available".to_string())?;
let target_tx = match message.frame_type {
FrameType::Connect
| FrameType::Close
| FrameType::UdpConnect
| FrameType::Handshake => &leg.control_tx,
_ => &leg.data_tx,
};
// Если канал мертв (TunnelEngine упал), удаляем ногу из роутинга!
if let Err(e) = target_tx.send(message).await {
self.remove_leg(leg_id);
return Err(format!("MUXER: Leg {} died during send: {}", leg_id, e));
}
Ok(())
}
pub async fn send_data_safe(
&self,
stream_id: u32,
mut data: Bytes,
is_udp: bool,
) -> Result<(), String> {
const MAX_PAYLOAD_CHUNK: usize = 1300; const MAX_PAYLOAD_CHUNK: usize = 1300;
let frame_type = if is_udp {
FrameType::UdpData
} else {
FrameType::Data
};
if data.len() <= MAX_PAYLOAD_CHUNK {
return self
.send_to_network(MuxMessage {
stream_id,
frame_type: FrameType::Data,
data,
})
.await
.map_err(|e| e.to_string());
}
// Если данных много (например, те самые 4096 байт), режем их на куски
while !data.is_empty() { while !data.is_empty() {
let chunk_size = std::cmp::min(data.len(), MAX_PAYLOAD_CHUNK); let chunk_size = std::cmp::min(data.len(), MAX_PAYLOAD_CHUNK);
let chunk = data.split_to(chunk_size); let chunk = data.split_to(chunk_size);
self.send_to_network(MuxMessage { self.send_to_network(MuxMessage {
stream_id, stream_id,
frame_type: FrameType::Data, frame_type,
data: chunk, data: chunk,
}) })
.await .await?;
.map_err(|e| e.to_string())?;
// Небольшая уступка планировщику, чтобы не забить канал мгновенно if !data.is_empty() {
tokio::task::yield_now().await; tokio::task::yield_now().await;
} }
}
Ok(()) Ok(())
} }
@@ -101,14 +171,12 @@ impl Muxer {
f_type: FrameType, f_type: FrameType,
data: Bytes, data: Bytes,
) -> Result<(), String> { ) -> Result<(), String> {
self.control_tx self.send_to_network(MuxMessage {
.send(MuxMessage {
stream_id, stream_id,
frame_type: f_type, frame_type: f_type,
data, data,
}) })
.await .await
.map_err(|e| e.to_string())
} }
pub fn register_stream(&self, stream_id: u32, tx: Sender<Bytes>) { pub fn register_stream(&self, stream_id: u32, tx: Sender<Bytes>) {
@@ -120,22 +188,10 @@ impl Muxer {
} }
pub async fn dispatch_to_local(&self, stream_id: u32, data: Bytes) { pub async fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
let tx = self.streams.get(&stream_id).map(|r| r.value().clone()); if let Some(tx) = self.streams.get(&stream_id) {
if let Err(_) = tx.send(data).await {
if let Some(tx) = tx {
if let Err(_e) = tx.send(data).await {
netrunner_logger::warn!(
stream_id,
"MUXER: [INBOUND_ERR] Local channel closed, dropping packet"
);
self.remove_stream(stream_id); self.remove_stream(stream_id);
} }
} else {
netrunner_logger::trace!(
stream_id,
len = data.len(),
"MUXER: [IGNORE] Received data for already closed stream (draining pipe)"
);
} }
} }
} }
+4 -15
View File
@@ -12,22 +12,11 @@ pub struct Network {
host: String, host: String,
port: u16, port: u16,
role: ConnectionRole, role: ConnectionRole,
remote_proxy_addr: Option<String>,
} }
impl Network { impl Network {
pub fn new( pub fn new(host: String, port: u16, role: ConnectionRole) -> Self {
host: String, Self { host, port, role }
port: u16,
role: ConnectionRole,
remote_proxy_addr: Option<String>,
) -> Self {
Self {
host,
port,
role,
remote_proxy_addr,
}
} }
pub async fn run(&self, token: CancellationToken) { pub async fn run(&self, token: CancellationToken) {
@@ -60,7 +49,7 @@ impl Network {
// Создаем соединение (init = true для сервера) // Создаем соединение (init = true для сервера)
let conn = Connection::new(stream, true); let conn = Connection::new(stream, true);
let handler = ServerHandler { conn }; let handler = ServerHandler::new(conn);
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = handler.run().await { if let Err(e) = handler.run().await {
@@ -108,7 +97,7 @@ impl NetworkConfig {
// 2. Каналы Muxer (Баланс между скоростью и задержкой) // 2. Каналы Muxer (Баланс между скоростью и задержкой)
let muxer_capacity = 512; // Глобальная очередь (выдержит много вкладок) let muxer_capacity = 512; // Глобальная очередь (выдержит много вкладок)
let tcp_stream_capacity = 16; // Хватит для скорости, но не даст пингу взлететь let tcp_stream_capacity = 16; // Хватит для скорости, но не даст пингу взлететь
let udp_stream_capacity = 64; // Простор для голосового трафика и игр let udp_stream_capacity = 32; // Простор для голосового трафика и игр
Self { Self {
mtu: system_mtu, mtu: system_mtu,
+12
View File
@@ -144,6 +144,10 @@ impl TlsBridge {
) -> Result<Bytes, TlsError> { ) -> Result<Bytes, TlsError> {
if let HandshakeMessage::Client { base, extensions } = client_msg { if let HandshakeMessage::Client { base, extensions } = client_msg {
if base.session_id.len() != 32 { if base.session_id.len() != 32 {
netrunner_logger::warn!(
"❌ Auth failed: Expected 32 bytes SessionID, got {}. Client IP: ...",
base.session_id.len()
);
return Err(TlsError::new( return Err(TlsError::new(
ErrorStage::Handshake("Invalid SessionID len"), ErrorStage::Handshake("Invalid SessionID len"),
ErrorAction::Drop, ErrorAction::Drop,
@@ -152,7 +156,15 @@ impl TlsBridge {
} }
let mut received_tag = [0u8; 16]; let mut received_tag = [0u8; 16];
if base.session_id.len() >= 32 {
received_tag.copy_from_slice(&base.session_id[16..32]); received_tag.copy_from_slice(&base.session_id[16..32]);
} else {
return Err(TlsError::new(
ErrorStage::Handshake("Short SessionID"),
ErrorAction::Drop,
Bytes::new(),
));
}
if !keys.verify_auth_tag(&received_tag) { if !keys.verify_auth_tag(&received_tag) {
netrunner_logger::warn!("Unauthorized ClientHello: Auth Tag mismatch"); netrunner_logger::warn!("Unauthorized ClientHello: Auth Tag mismatch");
+5 -1
View File
@@ -25,7 +25,7 @@ impl Padding {
} }
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) enum FrameType { pub(crate) enum FrameType {
Connect = 0x00, Connect = 0x00,
Data = 0x01, Data = 0x01,
@@ -33,6 +33,7 @@ pub(crate) enum FrameType {
Heartbeat = 0x03, Heartbeat = 0x03,
UdpConnect = 0x04, UdpConnect = 0x04,
UdpData = 0x05, UdpData = 0x05,
Handshake = 0x06,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub(crate) struct FrameHeader { pub(crate) struct FrameHeader {
@@ -122,6 +123,9 @@ impl Parser for FrameHeader {
0x01 => FrameType::Data, 0x01 => FrameType::Data,
0x02 => FrameType::Close, 0x02 => FrameType::Close,
0x03 => FrameType::Heartbeat, 0x03 => FrameType::Heartbeat,
0x04 => FrameType::UdpConnect,
0x05 => FrameType::UdpData,
0x06 => FrameType::Handshake,
_ => FrameType::Close, _ => FrameType::Close,
}; };
+57 -9
View File
@@ -1,5 +1,7 @@
use bytes::Bytes; use bytes::Bytes;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
// Добавили импорт логгеров для детального трейсинга
use netrunner_logger::{debug, trace};
use crate::{ use crate::{
nrxp::{Frame, FrameType}, nrxp::{Frame, FrameType},
@@ -10,20 +12,45 @@ pub struct RawCastAdapter;
impl RawCastAdapter { impl RawCastAdapter {
/// Перевод из RawCast (от локального TUN/smoltcp) в NRXP (в ядро/сеть) /// Перевод из RawCast (от локального TUN/smoltcp) в NRXP (в ядро/сеть)
pub fn to_nrxp(raw: RawCastFrame) -> Result<Frame, String> { pub(crate) fn to_nrxp(raw: RawCastFrame) -> Result<Frame, String> {
// Кастим ID сокета // Кастим ID сокета
let stream_id = raw.socket_id as u32; let stream_id = raw.socket_id as u32;
let (frame_type, payload) = match (raw.protocol, raw.event) { let (frame_type, payload) = match (raw.protocol, raw.event) {
// Открытие TCP соединения // Открытие TCP соединения
(LocalProtocol::Tcp, RawCastEvent::Connect) => { (LocalProtocol::Tcp, RawCastEvent::Connect) => {
let target = format!("{}:{}", raw.dst_ip, raw.dst_port); // Если payload не пустой (ConnectionManager положил туда домен) - используем его.
(FrameType::Connect, Bytes::from(target)) // Иначе фоллбэк: собираем из сырого IP и порта.
let final_payload = if !raw.payload.is_empty() {
raw.payload
} else {
Bytes::from(format!("{}:{}", raw.dst_ip, raw.dst_port))
};
debug!(
"🧩 [Adapter TCP] Stream {}: Packing Connect target: {}",
stream_id,
String::from_utf8_lossy(&final_payload)
);
(FrameType::Connect, final_payload)
} }
// Открытие UDP сессии // Открытие UDP сессии
(LocalProtocol::Udp, RawCastEvent::Connect) => { (LocalProtocol::Udp, RawCastEvent::Connect) => {
let target = format!("{}:{}", raw.dst_ip, raw.dst_port); // Аналогичная логика защиты переданного таргета для UDP
(FrameType::UdpConnect, Bytes::from(target)) let final_payload = if !raw.payload.is_empty() {
raw.payload
} else {
Bytes::from(format!("{}:{}", raw.dst_ip, raw.dst_port))
};
debug!(
"🧩 [Adapter UDP] Stream {}: Packing UdpConnect target: {}",
stream_id,
String::from_utf8_lossy(&final_payload)
);
(FrameType::UdpConnect, final_payload)
} }
// Передача данных (TCP) // Передача данных (TCP)
@@ -32,7 +59,10 @@ impl RawCastAdapter {
(LocalProtocol::Udp, RawCastEvent::Data) => (FrameType::UdpData, raw.payload), (LocalProtocol::Udp, RawCastEvent::Data) => (FrameType::UdpData, raw.payload),
// Закрытие соединения (одинаково для TCP и UDP) // Закрытие соединения (одинаково для TCP и UDP)
(_, RawCastEvent::Close) => (FrameType::Close, Bytes::new()), (_, RawCastEvent::Close) => {
trace!("🧩 [Adapter] Stream {}: Packing Close frame", stream_id);
(FrameType::Close, Bytes::new())
}
// ICMP пока не поддерживается в NRXP, отбрасываем // ICMP пока не поддерживается в NRXP, отбрасываем
(LocalProtocol::Icmp, _) => { (LocalProtocol::Icmp, _) => {
@@ -48,7 +78,7 @@ impl RawCastAdapter {
/// ///
/// ВАЖНО: Так как NRXP Frame не содержит IP и Port (только stream_id), /// ВАЖНО: Так как NRXP Frame не содержит IP и Port (только stream_id),
/// локальный клиент должен помнить, какому stream_id какой IP/Port принадлежит. /// локальный клиент должен помнить, какому stream_id какой IP/Port принадлежит.
pub fn from_nrxp( pub(crate) fn from_nrxp(
nrxp_frame: Frame, nrxp_frame: Frame,
dst_ip: Ipv4Addr, dst_ip: Ipv4Addr,
dst_port: u16, dst_port: u16,
@@ -63,13 +93,31 @@ impl RawCastAdapter {
let event = match nrxp_frame.header.frame_type { let event = match nrxp_frame.header.frame_type {
// Сервер прислал успешный коннект // Сервер прислал успешный коннект
FrameType::Connect | FrameType::UdpConnect => RawCastEvent::Connect, FrameType::Connect | FrameType::UdpConnect => {
trace!(
"🧩 [Adapter] Stream {}: Unpacked Connect Ack from server",
socket_id
);
RawCastEvent::Connect
}
// Сервер прислал данные // Сервер прислал данные
FrameType::Data | FrameType::UdpData => RawCastEvent::Data, FrameType::Data | FrameType::UdpData => RawCastEvent::Data,
// Сервер закрыл соединение // Сервер закрыл соединение
FrameType::Close => RawCastEvent::Close, FrameType::Close => {
trace!(
"🧩 [Adapter] Stream {}: Unpacked Close signal from server",
socket_id
);
RawCastEvent::Close
}
FrameType::Handshake => {
return Err(
"Handshake frame detected. Muxer should consume it, not the Adapter.".into(),
);
}
FrameType::Heartbeat => return Err("Heartbeat should be handled by muxer".into()), FrameType::Heartbeat => return Err("Heartbeat should be handled by muxer".into()),
}; };
+1
View File
@@ -1,4 +1,5 @@
mod adapter; mod adapter;
mod frame; mod frame;
pub use adapter::RawCastAdapter;
pub use frame::{LocalProtocol, RawCastEvent, RawCastFrame}; pub use frame::{LocalProtocol, RawCastEvent, RawCastFrame};
+1 -1
View File
@@ -16,7 +16,7 @@ fn main() {
Logger::init(); Logger::init();
Logger::global().set_level("info"); //set error in prod Logger::global().set_level("info"); //set error in prod
let args = Args::parse(); let args = Args::parse();
let net = Network::new(args.host, args.port, ConnectionRole::Server, None); let net = Network::new(args.host, args.port, ConnectionRole::Server);
let rt = tokio::runtime::Runtime::new().expect("Failed to create Tokio runtime"); let rt = tokio::runtime::Runtime::new().expect("Failed to create Tokio runtime");