sessions and muxer legs
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
@@ -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,71 +223,63 @@ 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);
|
||||||
// БРОАДКАСТ: Отправляем ответ на ВСЕ порты, которые мы запомнили
|
self.last_activity = std::time::Instant::now();
|
||||||
for endpoint in &self.client_endpoints {
|
|
||||||
let _ = socket.send_slice(&data, *endpoint);
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
// Блокируем порты локального вещания и NetBIOS
|
|
||||||
if dst_port == 0 || dst_port == 137 || dst_port == 138 {
|
|
||||||
trace!(%dst_addr, dst_port, "Ignored blocked UDP port");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.tracker.has_udp(dst_addr.into(), dst_port, socket_set) {
|
|
||||||
debug!(%dst_addr, dst_port, "No active UDP socket found, allocating new one");
|
|
||||||
|
|
||||||
let mut socket = SocketFactory::create_udp(dst_port);
|
|
||||||
let endpoint = IpListenEndpoint {
|
|
||||||
addr: Some(dst_addr.into()),
|
|
||||||
port: dst_port,
|
|
||||||
};
|
|
||||||
|
|
||||||
match socket.bind(endpoint) {
|
|
||||||
Ok(_) => {
|
|
||||||
debug!(%dst_addr, dst_port, "UDP socket successfully bound");
|
|
||||||
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 => {
|
p => trace!("Skip IPv4 protocol {:?}", p),
|
||||||
trace!(
|
|
||||||
"try_create_socket: Ignored unsupported IP protocol: {:?}",
|
|
||||||
protocol
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_ipv6(&mut self, packet: &[u8], socket_set: &mut SocketSet) {
|
||||||
|
let Ok(ip) = Ipv6Packet::new_checked(packet) else {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst_p == 53 {
|
||||||
|
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 socket_id = self.tracker.generate_socket_id();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"🚀 [UDP MASTER] ID:{} | Intercepted: {}:{} -> Target: {}",
|
||||||
|
socket_id, src, src_p, target
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
} else {
|
if let Some(domain) = self.resolver.fake_ip_store.lookup_by_ip(&std_ip) {
|
||||||
format!("{}:{}", dst_ip, dst_port)
|
info!(
|
||||||
};
|
"🔍 [TCP {}] Reverse lookup MATCH: {} -> {}",
|
||||||
|
socket_id, std_ip, domain
|
||||||
let tx_tunnel = self.tx_to_tunnel.clone();
|
);
|
||||||
|
(std_ip, format!("{}:{}", domain, ep.port))
|
||||||
tokio::spawn(async move {
|
} else {
|
||||||
// Создаем кадр коннекта
|
info!(
|
||||||
let mut connect_frame =
|
"🔍 [TCP {}] Reverse lookup MISS: using raw IP {}",
|
||||||
RawCastFrame::connect(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
|
socket_id, std_ip
|
||||||
// Кладем доменное имя в payload, чтобы ClientHandler его прочитал
|
);
|
||||||
connect_frame.payload = bytes::Bytes::from(target_str);
|
(std_ip, format!("{}:{}", std_ip, ep.port))
|
||||||
|
|
||||||
if tx_tunnel.send(connect_frame).await.is_err() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = handshake_tx.send(());
|
|
||||||
|
|
||||||
while let Some(data) = rx_from_smol.recv().await {
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
smoltcp::wire::IpAddress::Ipv6(ip) => (
|
||||||
|
std::net::Ipv4Addr::new(0, 0, 0, 0),
|
||||||
|
format!("[{}]:{}", ip, ep.port),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
let close_frame =
|
info!("🔗 [TCP {}] established -> {}", socket_id, target);
|
||||||
RawCastFrame::close(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
|
|
||||||
let _ = tx_tunnel.send(close_frame).await;
|
let tx_tunnel = self.tx_to_tunnel.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut frame =
|
||||||
|
RawCastFrame::connect(LocalProtocol::Tcp, socket_id, dst_ip, ep.port);
|
||||||
|
frame.payload = bytes::Bytes::from(target.clone());
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"📦 [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(());
|
||||||
|
while let Some(data) = rx_smol.recv().await {
|
||||||
|
let _ = tx_tunnel
|
||||||
|
.send(RawCastFrame::data(
|
||||||
|
LocalProtocol::Tcp,
|
||||||
|
socket_id,
|
||||||
|
dst_ip,
|
||||||
|
ep.port,
|
||||||
|
data.to_vec(),
|
||||||
|
))
|
||||||
|
.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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
loop {
|
||||||
|
if let Err(e) = Self::establish_leg(&addr, id, m.clone(), &sid).await {
|
||||||
|
error!("Leg {} failed: {}. Reconnecting...", id, e);
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("🚀 Netrunner Dual-Leg Tunnel Active.");
|
||||||
|
|
||||||
|
let muxer_inner = muxer.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Some(raw_frame) = rx_from_engine.recv().await {
|
while let Some(raw_frame) = rx_from_engine.recv().await {
|
||||||
let stream_id = raw_frame.socket_id as u32;
|
// 1. Сохраняем метаданные для локального реестра (до того, как raw_frame исчезнет)
|
||||||
let is_udp = raw_frame.protocol == LocalProtocol::Udp;
|
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 muxer = if is_udp {
|
let nrxp_frame = match RawCastAdapter::to_nrxp(raw_frame) {
|
||||||
udp_muxer.clone()
|
Ok(frame) => frame,
|
||||||
} else {
|
Err(e) => {
|
||||||
tcp_muxer.clone()
|
error!("⚠️ [Adapter] Failed to cast frame: {}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match raw_frame.event {
|
let stream_id = nrxp_frame.header.stream_id;
|
||||||
RawCastEvent::Connect => {
|
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) =
|
let (v_tx, mut v_rx) =
|
||||||
mpsc::channel(NetworkConfig::global().tcp_buffer_size);
|
mpsc::channel(NetworkConfig::global().tcp_buffer_size);
|
||||||
muxer.register_stream(stream_id, v_tx);
|
muxer_inner.register_stream(stream_id, v_tx);
|
||||||
|
|
||||||
// Читаем домен, который мы прокинули в предыдущем шаге
|
let tx_to_tun = tx_to_engine.clone();
|
||||||
let target = if !raw_frame.payload.is_empty() {
|
let reg = registry.clone();
|
||||||
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 {
|
tokio::spawn(async move {
|
||||||
while let Some(payload) = v_rx.recv().await {
|
while let Some(back_payload) = v_rx.recv().await {
|
||||||
let data_frame = RawCastFrame::data(
|
if let Some(ctx) = reg.get(&stream_id) {
|
||||||
protocol,
|
let (ip, port, proto) = *ctx;
|
||||||
socket_id,
|
let out_f_type = if proto == LocalProtocol::Udp {
|
||||||
dst_ip,
|
FrameType::UdpData
|
||||||
dst_port,
|
} else {
|
||||||
payload.to_vec(),
|
FrameType::Data
|
||||||
);
|
};
|
||||||
if tx_engine_clone.send(data_frame).await.is_err() {
|
let mock_nrxp = Frame::new(stream_id, out_f_type, back_payload);
|
||||||
break;
|
|
||||||
|
// Здесь from_nrxp уже используется у тебя правильно!
|
||||||
|
if let Ok(raw) = RawCastAdapter::from_nrxp(
|
||||||
|
mock_nrxp,
|
||||||
|
ip,
|
||||||
|
port,
|
||||||
|
proto == LocalProtocol::Udp,
|
||||||
|
) {
|
||||||
|
let _ = tx_to_tun.send(raw).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
muxer_clone.remove_stream(stream_id);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Отправляем управляющий фрейм с адресом
|
||||||
|
let _ = muxer_inner.send_control(stream_id, f_type, payload).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
RawCastEvent::Data => {
|
FrameType::Data | FrameType::UdpData => {
|
||||||
let frame_type = if is_udp {
|
let _ = muxer_inner.send_data_safe(stream_id, payload, is_udp).await;
|
||||||
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 => {
|
FrameType::Close => {
|
||||||
let _ = muxer
|
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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
let target_str = String::from_utf8_lossy(&payload).to_string();
|
||||||
|
|
||||||
if self.role == ConnectionRole::Server {
|
if self.role == ConnectionRole::Server {
|
||||||
let target_str = String::from_utf8_lossy(&payload).to_string();
|
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) {
|
||||||
|
let target_str = String::from_utf8_lossy(&payload).to_string();
|
||||||
|
|
||||||
if self.role == ConnectionRole::Server {
|
if self.role == ConnectionRole::Server {
|
||||||
let target_str = String::from_utf8_lossy(&payload).to_string();
|
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_udp_data(&self, stream_id: u32, payload: Bytes) {
|
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;
|
self.muxer.dispatch_to_local(stream_id, payload).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Вспомогательная функция вместо send_error_reply
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
+13
-1
@@ -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];
|
||||||
received_tag.copy_from_slice(&base.session_id[16..32]);
|
if base.session_id.len() >= 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");
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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,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
@@ -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");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user