sessions and muxer legs
This commit is contained in:
@@ -73,6 +73,8 @@ impl SessionManager {
|
||||
tun_fd: Option<i32>,
|
||||
cache_dir: String,
|
||||
) -> Arc<Session> {
|
||||
netrunner_logger::Logger::init();
|
||||
netrunner_logger::Logger::global().set_level("info");
|
||||
let runtime = get_runtime();
|
||||
let cancel_token = CancellationToken::new();
|
||||
let session_token = cancel_token.clone();
|
||||
|
||||
@@ -3,13 +3,12 @@ use netrunner_core::net::network::NetworkConfig;
|
||||
use smoltcp::{
|
||||
iface::SocketHandle,
|
||||
socket::{tcp, udp},
|
||||
time::Instant,
|
||||
wire::IpEndpoint,
|
||||
};
|
||||
use std::{collections::HashSet, time::Duration};
|
||||
use std::time::Duration;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
// Добавили trace для частых логов (попакетно) и debug для состояний
|
||||
use netrunner_logger::{debug, error, info, trace, warn};
|
||||
use netrunner_logger::debug;
|
||||
|
||||
// ============================================================================
|
||||
// 1. БАЗОВАЯ СТРУКТУРА (ConnectionCore)
|
||||
@@ -17,19 +16,19 @@ use netrunner_logger::{debug, error, info, trace, warn};
|
||||
|
||||
/// Фундамент для любого соединения.
|
||||
/// Инициализирует и хранит каналы связи между smoltcp и Muxer'ом.
|
||||
pub struct ConnectionCore {
|
||||
pub struct ConnectionCore<T> {
|
||||
pub handle: SocketHandle,
|
||||
pub tx: mpsc::Sender<Bytes>,
|
||||
pub rx: mpsc::Receiver<Bytes>,
|
||||
pub tx: mpsc::Sender<T>,
|
||||
pub rx: mpsc::Receiver<Bytes>, // Входящие из туннеля всегда байты
|
||||
}
|
||||
|
||||
impl ConnectionCore {
|
||||
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) {
|
||||
trace!(%handle, "Creating ConnectionCore channels");
|
||||
let (tx_to_net, rx_from_smol) =
|
||||
mpsc::channel::<Bytes>(NetworkConfig::global().tcp_stream_capacity);
|
||||
let (tx_to_smol, rx_from_net) =
|
||||
mpsc::channel::<Bytes>(NetworkConfig::global().tcp_stream_capacity);
|
||||
impl<T> ConnectionCore<T> {
|
||||
pub fn new(
|
||||
handle: SocketHandle,
|
||||
capacity: usize,
|
||||
) -> (Self, mpsc::Receiver<T>, mpsc::Sender<Bytes>) {
|
||||
let (tx_to_net, rx_from_smol) = mpsc::channel::<T>(capacity);
|
||||
let (tx_to_smol, rx_from_net) = mpsc::channel::<Bytes>(capacity);
|
||||
|
||||
let core = Self {
|
||||
handle,
|
||||
@@ -53,7 +52,7 @@ pub enum ConnectionState {
|
||||
}
|
||||
|
||||
pub struct TcpConnection {
|
||||
core: ConnectionCore,
|
||||
core: ConnectionCore<Bytes>,
|
||||
state: ConnectionState,
|
||||
pending_data: BytesMut,
|
||||
handshake_rx: Option<oneshot::Receiver<()>>,
|
||||
@@ -70,8 +69,8 @@ impl TcpConnection {
|
||||
mpsc::Sender<Bytes>,
|
||||
oneshot::Sender<()>,
|
||||
) {
|
||||
debug!(%handle, "Initializing new TCP Connection (State -> Handshaking)");
|
||||
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle);
|
||||
let capacity = NetworkConfig::global().tcp_stream_capacity;
|
||||
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle, capacity);
|
||||
let (handshake_tx, handshake_rx) = oneshot::channel();
|
||||
|
||||
let conn = Self {
|
||||
@@ -80,7 +79,7 @@ impl TcpConnection {
|
||||
pending_data: BytesMut::new(),
|
||||
handshake_rx: Some(handshake_rx),
|
||||
chunk_buf: vec![0u8; NetworkConfig::global().tcp_chunk_size],
|
||||
server_eof: false, // Инициализируем
|
||||
server_eof: false,
|
||||
};
|
||||
|
||||
(conn, rx_from_smol, tx_to_smol, handshake_tx)
|
||||
@@ -224,71 +223,63 @@ impl TcpConnection {
|
||||
|
||||
const UDP_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
|
||||
pub type UdpPacketTarget = (Bytes, std::net::Ipv4Addr, u16);
|
||||
pub struct UdpConnection {
|
||||
core: ConnectionCore,
|
||||
client_endpoints: HashSet<IpEndpoint>,
|
||||
last_activity: std::time::Instant, // Системное время для таймаутов
|
||||
core: ConnectionCore<UdpPacketTarget>, // Используем кортеж
|
||||
last_client_endpoint: Option<IpEndpoint>,
|
||||
last_activity: std::time::Instant,
|
||||
}
|
||||
|
||||
impl UdpConnection {
|
||||
pub fn new(handle: SocketHandle) -> (Self, mpsc::Receiver<Bytes>, mpsc::Sender<Bytes>) {
|
||||
debug!(%handle, "Initializing new UDP Connection");
|
||||
let (core, rx_from_smol, tx_to_smol) = ConnectionCore::new(handle);
|
||||
pub fn new(
|
||||
handle: SocketHandle,
|
||||
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 {
|
||||
core,
|
||||
client_endpoints: HashSet::new(), // Инициализируем пустое множество
|
||||
last_client_endpoint: Some(IpEndpoint::new(client_addr, client_port)),
|
||||
last_activity: std::time::Instant::now(),
|
||||
};
|
||||
|
||||
(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 {
|
||||
// Проверка таймаутов (остается твоя рабочая)
|
||||
if self.last_activity.elapsed() > UDP_TIMEOUT {
|
||||
debug!(%self.core.handle, "UDP Session closed due to timeout");
|
||||
socket.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// ЧИТАЕМ ИЗ SMOLTCP (от клиента) И ШЛЕМ В ТУННЕЛЬ
|
||||
if socket.can_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);
|
||||
|
||||
// ЗАПОМИНАЕМ ВСЕ ПОРТЫ КЛИЕНТА, КОТОРЫЕ СЮДА СТУЧАТСЯ
|
||||
if self.client_endpoints.insert(source_endpoint) {
|
||||
info!(
|
||||
%self.core.handle,
|
||||
source = %source_endpoint,
|
||||
"Registered new client port for UDP session"
|
||||
);
|
||||
}
|
||||
let target_ip = std::net::Ipv4Addr::from(ip);
|
||||
let target_port = metadata.endpoint.port;
|
||||
let payload = (Bytes::copy_from_slice(data), target_ip, target_port);
|
||||
|
||||
if self.core.tx.try_send(Bytes::copy_from_slice(data)).is_ok() {
|
||||
self.last_activity = std::time::Instant::now();
|
||||
if self.core.tx.try_send(payload).is_ok() {
|
||||
self.last_activity = std::time::Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ЧИТАЕМ ИЗ ТУННЕЛЯ И ШЛЕМ В SMOLTCP (клиенту)
|
||||
if socket.can_send() && !self.client_endpoints.is_empty() {
|
||||
loop {
|
||||
match self.core.rx.try_recv() {
|
||||
Ok(data) => {
|
||||
// БРОАДКАСТ: Отправляем ответ на ВСЕ порты, которые мы запомнили
|
||||
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;
|
||||
}
|
||||
// Исходящие из туннеля (от Telegram) — всё еще просто Bytes
|
||||
if socket.can_send() {
|
||||
if let Some(client_endpoint) = self.last_client_endpoint {
|
||||
while let Ok(data) = self.core.rx.try_recv() {
|
||||
let _ = socket.send_slice(&data, client_endpoint);
|
||||
self.last_activity = std::time::Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@ use netrunner_core::{
|
||||
net::network::NetworkConfig,
|
||||
rawcast::{LocalProtocol, RawCastEvent, RawCastFrame},
|
||||
};
|
||||
use netrunner_logger::{debug, error, info, trace};
|
||||
use netrunner_logger::{debug, error, info, trace, warn};
|
||||
use smoltcp::{
|
||||
iface::{SocketHandle, SocketSet},
|
||||
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 tokio::sync::mpsc;
|
||||
@@ -94,18 +96,13 @@ impl SessionTracker {
|
||||
})
|
||||
}
|
||||
|
||||
fn has_udp(
|
||||
&self,
|
||||
dst_addr: smoltcp::wire::IpAddress,
|
||||
dst_port: u16,
|
||||
socket_set: &SocketSet,
|
||||
) -> bool {
|
||||
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;
|
||||
fn get_id_by_port(&self, port: u16) -> Option<u64> {
|
||||
self.active_udp.iter().find_map(|(handle, conn)| {
|
||||
if conn.has_client(port) {
|
||||
self.handle_to_id.get(handle).copied()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -215,6 +212,7 @@ impl ConnectionManager {
|
||||
pub fn try_inject_inbound(&mut self, frame: RawCastFrame) -> Result<(), RawCastFrame> {
|
||||
if frame.event != RawCastEvent::Data {
|
||||
if frame.event == RawCastEvent::Close {
|
||||
info!("💀 [Stream {}] Received CLOSE from tunnel", frame.socket_id);
|
||||
self.tracker.inbound_tx.remove(&frame.socket_id);
|
||||
}
|
||||
return Ok(());
|
||||
@@ -222,30 +220,36 @@ impl ConnectionManager {
|
||||
|
||||
if let Some(tx) = self.tracker.inbound_tx.get(&frame.socket_id) {
|
||||
match tx.try_send(frame.payload.clone()) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
|
||||
error!(
|
||||
"🟡 WARNING: Inbound channel FULL for stream {}",
|
||||
frame.socket_id
|
||||
Ok(_) => {
|
||||
// Логируем входящие данные (trace чтобы не спамить, но можно временно info)
|
||||
trace!(
|
||||
"📥 [Stream {}] Inbound data: {} bytes",
|
||||
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(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
|
||||
error!(
|
||||
"🔴 WARNING: Inbound channel CLOSED for stream {}",
|
||||
frame.socket_id
|
||||
);
|
||||
error!("🔴 [Stream {}] Inbound channel CLOSED", frame.socket_id);
|
||||
self.tracker.inbound_tx.remove(&frame.socket_id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Если это происходит во время Connecting - значит сервер шлет ответы,
|
||||
// а клиент уже забыл про этот ID или еще не создал его.
|
||||
error!(
|
||||
"👻 ORPHAN PACKET: No local socket for stream {}",
|
||||
"👻 [Stream {}] ORPHAN packet from tunnel. ID mismatch?",
|
||||
frame.socket_id
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_sockets(n_icmp: usize) -> SocketSet<'static> {
|
||||
let mut sockets = SocketSet::new(Vec::with_capacity(48));
|
||||
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) {
|
||||
let Ok(ip_packet) = Ipv4Packet::new_checked(packet) else {
|
||||
trace!("try_create_socket: Failed to parse IPv4 packet, ignoring");
|
||||
if packet.is_empty() {
|
||||
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;
|
||||
};
|
||||
let (src, dst) = (ip.src_addr().into(), ip.dst_addr().into());
|
||||
|
||||
let dst_addr = ip_packet.dst_addr();
|
||||
|
||||
match ip_packet.next_header() {
|
||||
match ip.next_header() {
|
||||
IpProtocol::Tcp => {
|
||||
let src_addr = ip_packet.src_addr();
|
||||
let dst_addr = ip_packet.dst_addr();
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
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(udp_packet) = UdpPacket::new_checked(ip_packet.payload()) {
|
||||
let dst_port = udp_packet.dst_port();
|
||||
|
||||
// Блокируем порты локального вещания и 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");
|
||||
if let Ok(p) = UdpPacket::new_checked(ip.payload()) {
|
||||
self.intercept_udp(src, dst, p.src_port(), p.dst_port(), socket_set);
|
||||
}
|
||||
}
|
||||
protocol => {
|
||||
trace!(
|
||||
"try_create_socket: Ignored unsupported IP protocol: {:?}",
|
||||
protocol
|
||||
);
|
||||
}
|
||||
p => trace!("Skip IPv4 protocol {:?}", p),
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
for (handle, socket) in socket_set.iter_mut() {
|
||||
if let Some(tcp) = tcp::Socket::downcast_mut(socket) {
|
||||
self.handle_tcp(handle, tcp);
|
||||
} else if let Some(udp) = udp::Socket::downcast_mut(socket) {
|
||||
self.handle_udp(handle, udp);
|
||||
} else if let Some(icmp) = icmp::Socket::downcast_mut(socket) {
|
||||
self.handle_icmp(handle, icmp);
|
||||
if let Some(s) = tcp::Socket::downcast_mut(socket) {
|
||||
self.handle_tcp(handle, s);
|
||||
} else if let Some(s) = udp::Socket::downcast_mut(socket) {
|
||||
self.handle_udp(handle, s);
|
||||
} else if let Some(s) = icmp::Socket::downcast_mut(socket) {
|
||||
// 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) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@@ -387,151 +508,198 @@ impl ConnectionManager {
|
||||
if socket.state() == tcp::State::Established
|
||||
&& !self.tracker.active_tcp.contains_key(&handle)
|
||||
{
|
||||
let (dst_ip, dst_port) = match socket.local_endpoint() {
|
||||
Some(ep) => match ep.addr {
|
||||
smoltcp::wire::IpAddress::Ipv4(ip) => (std::net::Ipv4Addr::from(ip), ep.port),
|
||||
_ => return,
|
||||
},
|
||||
None => return,
|
||||
let Some(ep) = socket.local_endpoint() else {
|
||||
return;
|
||||
};
|
||||
|
||||
info!(%handle, ip = %dst_ip, port = dst_port, "New TCP session intercepted");
|
||||
|
||||
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);
|
||||
|
||||
let (conn, mut rx_from_smol, tx_to_smol, handshake_tx) = TcpConnection::new(handle);
|
||||
|
||||
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 обратно в домен
|
||||
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::Tcp, socket_id, dst_ip, dst_port);
|
||||
// Кладем доменное имя в payload, чтобы ClientHandler его прочитал
|
||||
connect_frame.payload = bytes::Bytes::from(target_str);
|
||||
|
||||
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;
|
||||
// ДОБАВЛЕНО ЛОГИРОВАНИЕ ДЛЯ TCP
|
||||
let (dst_ip, target) = match ep.addr {
|
||||
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!(
|
||||
"🔍 [TCP {}] Reverse lookup MATCH: {} -> {}",
|
||||
socket_id, std_ip, domain
|
||||
);
|
||||
(std_ip, format!("{}:{}", domain, ep.port))
|
||||
} else {
|
||||
info!(
|
||||
"🔍 [TCP {}] Reverse lookup MISS: using raw IP {}",
|
||||
socket_id, std_ip
|
||||
);
|
||||
(std_ip, format!("{}:{}", std_ip, ep.port))
|
||||
}
|
||||
}
|
||||
smoltcp::wire::IpAddress::Ipv6(ip) => (
|
||||
std::net::Ipv4Addr::new(0, 0, 0, 0),
|
||||
format!("[{}]:{}", ip, ep.port),
|
||||
),
|
||||
};
|
||||
|
||||
let close_frame =
|
||||
RawCastFrame::close(LocalProtocol::Tcp, socket_id, dst_ip, dst_port);
|
||||
let _ = tx_tunnel.send(close_frame).await;
|
||||
info!("🔗 [TCP {}] established -> {}", socket_id, target);
|
||||
|
||||
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 !conn.tick(socket) {
|
||||
info!("⚠️ [TCP] Tick failed, aborting handle {:?}", handle);
|
||||
socket.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_udp(&mut self, handle: SocketHandle, socket: &mut udp::Socket) {
|
||||
self.tracker.last_activity.insert(handle, StdInstant::now());
|
||||
|
||||
// 1. DNS перехват с логированием
|
||||
if socket.endpoint().port == 53 {
|
||||
while socket.can_recv() {
|
||||
if let Ok((data, meta)) = socket.recv() {
|
||||
if let Some(response) = self.resolver.process_dns_query(data) {
|
||||
let _ = socket.send_slice(&response, meta);
|
||||
while let Ok((data, meta)) = socket.recv() {
|
||||
if let Some(res) = self.resolver.process_dns_query(data) {
|
||||
// Используем debug, чтобы не заливать консоль, но видеть активность
|
||||
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;
|
||||
}
|
||||
|
||||
if socket.is_open() && !self.tracker.active_udp.contains_key(&handle) {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Жизненный цикл обычных UDP сессий
|
||||
if let Some(conn) = self.tracker.active_udp.get_mut(&handle) {
|
||||
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.active_udp.remove(&handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_icmp(&mut self, _handle: SocketHandle, socket: &mut icmp::Socket) {
|
||||
if socket.can_recv() {
|
||||
let _ = socket.recv();
|
||||
if !socket.can_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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user