update processes and code style
This commit is contained in:
@@ -75,14 +75,13 @@ impl AeadPacker for ChaChaCipher {
|
||||
// Сначала получаем текущий counter для лога (до того, как next_nonce его инкрементирует)
|
||||
let current_counter = self.encrypt_state.counter;
|
||||
let nonce = self.encrypt_state.next_nonce();
|
||||
let nonce_hex = hex::encode(nonce);
|
||||
let data_len = data.len();
|
||||
|
||||
match self.encrypt_cipher.encrypt_in_place(&nonce, &[], data) {
|
||||
Ok(_) => {
|
||||
tracing::trace!(
|
||||
counter = current_counter,
|
||||
nonce = %nonce_hex,
|
||||
nonce = %hex::encode(nonce),
|
||||
len = data_len,
|
||||
"Encryption successful"
|
||||
);
|
||||
@@ -91,7 +90,7 @@ impl AeadPacker for ChaChaCipher {
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
counter = current_counter,
|
||||
nonce = %nonce_hex,
|
||||
nonce = %hex::encode(nonce),
|
||||
len = data_len,
|
||||
error = ?e,
|
||||
"AEAD encryption failure"
|
||||
@@ -104,29 +103,27 @@ impl AeadPacker for ChaChaCipher {
|
||||
fn decrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error> {
|
||||
let current_counter = self.decrypt_state.counter;
|
||||
let nonce = self.decrypt_state.next_nonce();
|
||||
let nonce_hex = hex::encode(nonce);
|
||||
let data_len = data.len();
|
||||
|
||||
let data_prefix = if data.len() >= 8 {
|
||||
hex::encode(&data[..8])
|
||||
} else {
|
||||
hex::encode(data.as_ref())
|
||||
};
|
||||
|
||||
match self.decrypt_cipher.decrypt_in_place(&nonce, &[], data) {
|
||||
Ok(_) => {
|
||||
tracing::trace!(
|
||||
counter = current_counter,
|
||||
nonce = %nonce_hex,
|
||||
nonce = %hex::encode(nonce),
|
||||
len = data_len,
|
||||
"Decryption successful"
|
||||
);
|
||||
Ok(data.split().freeze())
|
||||
}
|
||||
Err(e) => {
|
||||
let data_prefix = if data.len() >= 8 {
|
||||
hex::encode(&data[..8])
|
||||
} else {
|
||||
hex::encode(data.as_ref())
|
||||
};
|
||||
tracing::error!(
|
||||
counter = current_counter,
|
||||
nonce = %nonce_hex,
|
||||
nonce = %hex::encode(nonce),
|
||||
len = data_len,
|
||||
prefix = %data_prefix,
|
||||
error = ?e,
|
||||
|
||||
@@ -7,7 +7,7 @@ pub fn logger_init() {
|
||||
.with_line_number(true); // Показывать строку кода (очень полезно для дебага)
|
||||
|
||||
let filter_layer = EnvFilter::try_from_default_env()
|
||||
.or_else(|_| EnvFilter::try_new("trace")) // По умолчанию уровень info
|
||||
.or_else(|_| EnvFilter::try_new("info")) // По умолчанию уровень info
|
||||
.unwrap();
|
||||
|
||||
tracing_subscriber::registry()
|
||||
|
||||
@@ -168,12 +168,6 @@ impl TlsBridge {
|
||||
}
|
||||
}
|
||||
|
||||
/// Вспомогательный метод для упаковки уже готового Handshake-сообщения в TlsRecord
|
||||
pub fn pack_handshake(payload: Bytes) -> Bytes {
|
||||
let record = TlsRecord::new(ContentType::Handshake, ProtocolVersion::Tls12, payload);
|
||||
record.serialize()
|
||||
}
|
||||
|
||||
pub fn pack_app_data(buffer: Bytes) -> Bytes {
|
||||
TlsRecord::build_application_data(buffer)
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod netr_bridge;
|
||||
pub mod tls_bridge;
|
||||
@@ -3,7 +3,7 @@ use bytes::{Bytes, BytesMut};
|
||||
use crate::crypto::aead::AeadPacker;
|
||||
use crate::crypto::chacha::ChaChaCipher;
|
||||
use crate::crypto::session::SessionKeys;
|
||||
use crate::protocol::codec::bridges::tls_bridge::TlsBridge;
|
||||
use crate::protocol::codec::bridge::TlsBridge;
|
||||
use crate::protocol::codec::frame::{Frame, FrameHeader, FrameType};
|
||||
use crate::protocol::codec::padding::Padding;
|
||||
use crate::protocol::errors::{ErrorAction, ErrorStage, TlsError};
|
||||
@@ -24,30 +24,24 @@ impl Codec {
|
||||
staging: BytesMut::new(),
|
||||
}
|
||||
}
|
||||
//maybe generator?
|
||||
//should anwer socks5 and open connection to proxy server
|
||||
/// Логика для Клиента: Генерирует байты ClientHello для инициализации соединения.
|
||||
/// Клиент: генерирует TLS Record [ ClientHello ]
|
||||
|
||||
pub fn make_client_handshake(
|
||||
&mut self,
|
||||
profile: &BrowserProfile,
|
||||
host: &str,
|
||||
) -> Result<Bytes, TlsError> {
|
||||
// 1. Извлекаем публичный ключ нашей текущей сессии
|
||||
let my_pub_key = self.session_keys.ecdh.public_key.to_bytes();
|
||||
// (Убедись, что метод возвращает [u8; 32])
|
||||
let pub_key = self.session_keys.ecdh.public_key.to_bytes();
|
||||
|
||||
// 2. Передаем его в мост
|
||||
Ok(TlsBridge::wrap_client_hello(
|
||||
profile,
|
||||
host,
|
||||
&my_pub_key,
|
||||
&pub_key,
|
||||
self.session_keys.salt.get_local(),
|
||||
))
|
||||
}
|
||||
/// Сервер: берет буфер, достает ClientHello и генерирует в ответ TLS Record [ ServerHello ]
|
||||
|
||||
pub fn make_server_handshake(&mut self, buffer: &mut BytesMut) -> Result<Bytes, TlsError> {
|
||||
// 1. Распаковываем сообщение клиента
|
||||
let client_msg = TlsBridge::unpack_handshake(buffer)?.ok_or_else(|| {
|
||||
TlsError::new(
|
||||
ErrorStage::Handshake("No CH"),
|
||||
@@ -56,7 +50,6 @@ impl Codec {
|
||||
)
|
||||
})?;
|
||||
|
||||
// 2. Генерируем ответный ServerHello рекорд
|
||||
let server_pub_key = self.session_keys.ecdh.public_key.to_bytes();
|
||||
let server_hello_record = TlsBridge::wrap_server_hello(
|
||||
&client_msg,
|
||||
@@ -64,8 +57,6 @@ impl Codec {
|
||||
self.session_keys.salt.get_local(),
|
||||
)?;
|
||||
|
||||
// 3. ОБНОВЛЕНИЕ КЛЮЧЕЙ НА СЕРВЕРЕ
|
||||
// Передаем true, так как сервер ПАРСИТ ClientHello (смещение 6 байт)
|
||||
let (w_key, w_iv, r_key, r_iv) = self
|
||||
.session_keys
|
||||
.update_keys(client_msg.random(), client_msg.extensions(), true)
|
||||
@@ -170,68 +161,51 @@ impl Codec {
|
||||
}
|
||||
|
||||
pub fn inbound(&mut self, buffer: &mut BytesMut) -> Result<Option<Frame>, TlsError> {
|
||||
// Логгируем входящее состояние сетевого буфера (TLS слой)
|
||||
if !buffer.is_empty() {
|
||||
let header = &buffer[..std::cmp::min(buffer.len(), 5)];
|
||||
tracing::debug!(
|
||||
buf_len = buffer.len(),
|
||||
header_hex = %hex::encode(header),
|
||||
"RAW TLS buffer state"
|
||||
);
|
||||
// 1. Сначала проверяем, нет ли уже готового фрейма в staging с прошлого раза
|
||||
if !self.staging.is_empty() {
|
||||
if let Some(frame) = self.try_parse_frame()? {
|
||||
return Ok(Some(frame));
|
||||
}
|
||||
}
|
||||
|
||||
// --- ШАГ 1: Извлекаем ВСЕ доступные TLS-рекорды и расшифровываем в staging ---
|
||||
// Мы крутим цикл, пока TlsBridge может "откусить" целый TLS-рекорд из buffer
|
||||
// 2. Распаковываем ВСЕ доступные TLS-рекорды из сетевого буфера
|
||||
while let Some(app_data) = TlsBridge::unpack_app_data(buffer)? {
|
||||
let mut encrypted_chunk = BytesMut::from(app_data.payload.as_ref());
|
||||
let raw_len = encrypted_chunk.len();
|
||||
// Берем Bytes напрямую (app_data.payload — это уже Bytes)
|
||||
let mut data_to_decrypt = BytesMut::from(app_data.payload);
|
||||
|
||||
// Расшифровываем кусок
|
||||
let decrypted_chunk = self.crypto.decrypt(&mut encrypted_chunk).map_err(|e| {
|
||||
tracing::error!(len = raw_len, "Decryption failed: {:?}", e);
|
||||
// Дешифруем "на месте" (In-place decryption)
|
||||
// Твоя библиотека ChaCha скорее всего поддерживает дешифровку прямо в том же буфере
|
||||
let decrypted = self.crypto.decrypt(&mut data_to_decrypt).map_err(|_| {
|
||||
TlsError::new(
|
||||
ErrorStage::Tls("Decryption error"),
|
||||
ErrorStage::Tls("Decr error"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
)
|
||||
})?;
|
||||
|
||||
// КЛАДЕМ В ЧИСТУЮ ЗОНУ: расшифрованный поток байтов нашего протокола
|
||||
self.staging.extend_from_slice(&decrypted_chunk);
|
||||
// ВАЖНО: Вместо extend_from_slice (копирование), используем split_off/unsplit или просто Bytes
|
||||
// Если staging — это BytesMut, используй put или reserve
|
||||
self.staging.extend_from_slice(&decrypted); // Увы, BytesMut требует копирования для конкатенации
|
||||
|
||||
tracing::debug!(
|
||||
added = decrypted_chunk.len(),
|
||||
total_staging = self.staging.len(),
|
||||
"Decrypted data moved to staging"
|
||||
);
|
||||
}
|
||||
|
||||
// --- ШАГ 2: Парсим Frame из "чистых" данных в staging ---
|
||||
if !self.staging.is_empty() {
|
||||
// Важно: Frame::parse должен вызывать advance() или split_to() у staging
|
||||
match Frame::parse(&mut self.staging) {
|
||||
Ok(Some(frame)) => {
|
||||
tracing::info!(
|
||||
stream_id = frame.header.stream_id,
|
||||
"Frame successfully parsed from staging"
|
||||
);
|
||||
return Ok(Some(frame));
|
||||
}
|
||||
Ok(None) => {
|
||||
tracing::debug!("Frame is incomplete in staging, waiting for more TLS records");
|
||||
return Ok(None);
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Frame parse error: {:?}", e);
|
||||
return Err(TlsError::new(
|
||||
ErrorStage::Tls("Frame parse error"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
));
|
||||
}
|
||||
// НО! Мы можем попытаться распарсить фрейм сразу после добавления каждого рекорда
|
||||
if let Some(frame) = self.try_parse_frame()? {
|
||||
return Ok(Some(frame));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
// Выносим парсинг в отдельный метод, чтобы не дублировать код
|
||||
fn try_parse_frame(&mut self) -> Result<Option<Frame>, TlsError> {
|
||||
match Frame::parse(&mut self.staging) {
|
||||
Ok(Some(frame)) => Ok(Some(frame)),
|
||||
Ok(None) => Ok(None),
|
||||
Err(_) => Err(TlsError::new(
|
||||
ErrorStage::Tls("Parse error"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mod bridges;
|
||||
mod bridge;
|
||||
pub mod codec;
|
||||
pub mod frame;
|
||||
mod padding;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
|
||||
use crate::protocol::parser::parser::Parser;
|
||||
|
||||
pub const SOCKS5_VERSION: u8 = 0x05;
|
||||
pub const REPLY_SUCCESS: u8 = 0x00;
|
||||
pub const REPLY_AUTH_FAILURE: u8 = 0xFF;
|
||||
@@ -17,6 +19,48 @@ pub enum SocksRequest {
|
||||
Connect { command: u8, target: SocksTarget },
|
||||
Unknown,
|
||||
}
|
||||
impl SocksRequest {
|
||||
pub async fn handle_handshake<S>(
|
||||
stream: &mut S,
|
||||
buf: &mut BytesMut,
|
||||
) -> Result<SocksTarget, String>
|
||||
where
|
||||
S: tokio::io::AsyncReadExt + tokio::io::AsyncWriteExt + Unpin,
|
||||
{
|
||||
// 1. Handshake Phase
|
||||
loop {
|
||||
// Используем трейт Parser
|
||||
if let Some(req) = Self::parse(buf)? {
|
||||
if let SocksRequest::Handshake { .. } = req {
|
||||
let mut reply = BytesMut::with_capacity(2);
|
||||
SocksReply::HandshakeSelect { method: 0x00 }.write_to(&mut reply);
|
||||
stream.write_all(&reply).await.map_err(|e| e.to_string())?;
|
||||
break;
|
||||
}
|
||||
return Err("Expected Handshake, got something else".into());
|
||||
}
|
||||
if stream.read_buf(buf).await.map_err(|e| e.to_string())? == 0 {
|
||||
return Err("Client closed during greeting".into());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Connect Request Phase
|
||||
loop {
|
||||
if let Some(req) = Self::parse(buf)? {
|
||||
if let SocksRequest::Connect { command, target } = req {
|
||||
// Проверяем, что это именно CONNECT (0x01)
|
||||
if command != 0x01 {
|
||||
return Err(format!("Unsupported SOCKS command: 0x{:02X}", command));
|
||||
}
|
||||
return Ok(target);
|
||||
}
|
||||
}
|
||||
if stream.read_buf(buf).await.map_err(|e| e.to_string())? == 0 {
|
||||
return Err("Client closed during connect request".into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SocksReply {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
use crate::protocol::codec::frame::FrameType;
|
||||
use crate::proxy::connection::muxer::{MuxMessage, Muxer};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{debug, error};
|
||||
pub async fn run_proxy_bridge<R, W>(
|
||||
stream_id: u32,
|
||||
mut reader: R,
|
||||
mut writer: W,
|
||||
muxer: Muxer,
|
||||
mut v_rx: mpsc::Receiver<Bytes>,
|
||||
) where
|
||||
R: tokio::io::AsyncReadExt + Unpin,
|
||||
W: tokio::io::AsyncWriteExt + Unpin,
|
||||
{
|
||||
let mut buf = BytesMut::with_capacity(16384);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
res = reader.read_buf(&mut buf) => {
|
||||
match res {
|
||||
Ok(0) => {
|
||||
debug!(stream_id, "Socket closed (EOF)");
|
||||
break;
|
||||
}
|
||||
Ok(_) => {
|
||||
let msg = MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Data,
|
||||
data: buf.split().freeze(),
|
||||
};
|
||||
if muxer.to_network.send(msg).await.is_err() { break; }
|
||||
}
|
||||
Err(e) => {
|
||||
error!(stream_id, error = %e, "Socket read error");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Читаем из туннеля (v_rx) -> шлем в сокет
|
||||
maybe_data = v_rx.recv() => {
|
||||
match maybe_data {
|
||||
Some(data) => {
|
||||
if data.is_empty() { break; } // EOF от другой стороны
|
||||
if let Err(e) = writer.write_all(&data).await {
|
||||
error!(stream_id, error = %e, "Socket write error");
|
||||
break;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
debug!(stream_id, "Virtual channel closed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Финализация (общая для всех)
|
||||
let _ = muxer
|
||||
.to_network
|
||||
.send(MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Close,
|
||||
data: Bytes::new(),
|
||||
})
|
||||
.await;
|
||||
|
||||
muxer.remove_stream(stream_id).await;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
use bytes::BytesMut;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; // <--- ОБЯЗАТЕЛЬНО ТУТ
|
||||
|
||||
pub struct BufPair {
|
||||
pub write_buf: BytesMut,
|
||||
pub read_buf: BytesMut,
|
||||
}
|
||||
const BUF_SIZE: usize = 4096;
|
||||
|
||||
impl BufPair {
|
||||
pub fn new() -> Self {
|
||||
let write_buf = BytesMut::with_capacity(BUF_SIZE);
|
||||
let read_buf = BytesMut::with_capacity(BUF_SIZE);
|
||||
Self {
|
||||
write_buf,
|
||||
read_buf,
|
||||
}
|
||||
}
|
||||
pub async fn read_from(&mut self, reader: &mut OwnedReadHalf) -> Result<usize, String> {
|
||||
let n = reader
|
||||
.read_buf(&mut self.read_buf)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if n == 0 {
|
||||
return Err("Connection closed by peer".to_string());
|
||||
}
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
pub async fn write_from(&mut self, writer: &mut OwnedWriteHalf) -> Result<(), String> {
|
||||
writer
|
||||
.write_all_buf(&mut self.write_buf)
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.read_buf.clear();
|
||||
self.write_buf.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use tracing::{instrument, info, debug, error, trace, warn};
|
||||
use std::{collections::HashMap, net::SocketAddr};
|
||||
use std::{net::SocketAddr};
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::{
|
||||
@@ -21,22 +21,16 @@ use crate::{
|
||||
parser::parser::Parser,
|
||||
},
|
||||
proxy::connection::{
|
||||
buf_pair::BufPair,
|
||||
engine::TunnelEngine,
|
||||
handler::spawn_client_local_handler_with_rx,
|
||||
muxer::{MuxMessage, Muxer},
|
||||
bridge::run_proxy_bridge, engine::TunnelEngine, handler::StreamHandler, muxer::{MuxMessage, Muxer}
|
||||
},
|
||||
};
|
||||
|
||||
pub struct VirtualStreams {
|
||||
pub channels: HashMap<u32, tokio::sync::mpsc::Sender<Bytes>>,
|
||||
pub struct BufPair {
|
||||
pub write_buf: BytesMut,
|
||||
pub read_buf: BytesMut,
|
||||
}
|
||||
pub const BUF_SIZE: usize = 16384;
|
||||
|
||||
pub enum ConnectionState {
|
||||
Authorize,
|
||||
Tunnel(VirtualStreams),
|
||||
Close,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ConnectionRole {
|
||||
@@ -48,7 +42,7 @@ pub struct Connection {
|
||||
addr: SocketAddr,
|
||||
pub inbound: OwnedReadHalf,
|
||||
pub outbound: OwnedWriteHalf,
|
||||
pub buffers: BufPair,
|
||||
pub read_buf: BytesMut,
|
||||
pub codec: Codec,
|
||||
}
|
||||
|
||||
@@ -63,194 +57,197 @@ impl Connection {
|
||||
addr,
|
||||
inbound,
|
||||
outbound,
|
||||
buffers: BufPair::new(),
|
||||
read_buf: BytesMut::with_capacity(BUF_SIZE),
|
||||
codec: Codec::new(init),
|
||||
}
|
||||
}
|
||||
|
||||
/// Читает и парсит запрос SOCKS5 из входящего потока
|
||||
async fn read_socks_request(&mut self) -> Result<SocksRequest, String> {
|
||||
loop {
|
||||
// Попытка парсинга из текущего буфера
|
||||
match SocksRequest::parse(&mut self.buffers.read_buf) {
|
||||
Ok(Some(req)) => {
|
||||
// Используем Debug-вывод (?req), так как SocksRequest обычно Enum
|
||||
info!(client = %self.addr, request = ?req, "SOCKS request successfully parsed");
|
||||
return Ok(req);
|
||||
async fn read_socks_request(&mut self) -> Result<SocksRequest, String> {
|
||||
loop {
|
||||
// Попытка парсинга из текущего буфера
|
||||
match SocksRequest::parse(&mut self.read_buf) {
|
||||
Ok(Some(req)) => {
|
||||
// Используем Debug-вывод (?req), так как SocksRequest обычно Enum
|
||||
info!(client = %self.addr, request = ?req, "SOCKS request successfully parsed");
|
||||
return Ok(req);
|
||||
}
|
||||
Ok(None) => {
|
||||
// Это не ошибка, просто данных в сокете пока меньше, чем размер структуры SOCKS
|
||||
trace!(client = %self.addr, buffer_len = self.read_buf.len(), "SOCKS parse: need more data");
|
||||
}
|
||||
Err(e) => {
|
||||
error!(client = %self.addr, error = %e, "SOCKS protocol violation");
|
||||
return Err(format!("Socks parse error: {}", e));
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
// Это не ошибка, просто данных в сокете пока меньше, чем размер структуры SOCKS
|
||||
trace!(client = %self.addr, buffer_len = self.buffers.read_buf.len(), "SOCKS parse: need more data");
|
||||
}
|
||||
Err(e) => {
|
||||
error!(client = %self.addr, error = %e, "SOCKS protocol violation");
|
||||
return Err(format!("Socks parse error: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// Чтение новых данных из сокета
|
||||
let n = self
|
||||
.inbound
|
||||
.read_buf(&mut self.buffers.read_buf)
|
||||
// Чтение новых данных из сокета
|
||||
let n = self
|
||||
.inbound
|
||||
.read_buf(&mut self.read_buf)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(client = %self.addr, error = %e, "Failed to read from socket during SOCKS handshake");
|
||||
e.to_string()
|
||||
})?;
|
||||
|
||||
if n == 0 {
|
||||
warn!(client = %self.addr, "Client closed connection prematurely during SOCKS handshake");
|
||||
return Err("Client closed connection during SOCKS handshake".into());
|
||||
}
|
||||
|
||||
trace!(client = %self.addr, read_bytes = n, "Read data from client for SOCKS handshake");
|
||||
}
|
||||
}
|
||||
|
||||
/// Отправляет SOCKS ответ
|
||||
async fn send_socks_reply(&mut self, reply: SocksReply) -> Result<(), String> {
|
||||
let mut buf = BytesMut::with_capacity(24);
|
||||
debug!(client = %self.addr, reply = ?reply, "Sending SOCKS reply to client");
|
||||
reply.write_to(&mut buf);
|
||||
|
||||
|
||||
|
||||
self.outbound
|
||||
.write_all(&buf)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(client = %self.addr, error = %e, "Failed to read from socket during SOCKS handshake");
|
||||
error!(client = %self.addr, error = %e, "Failed to send SOCKS reply");
|
||||
e.to_string()
|
||||
})?;
|
||||
|
||||
if n == 0 {
|
||||
warn!(client = %self.addr, "Client closed connection prematurely during SOCKS handshake");
|
||||
return Err("Client closed connection during SOCKS handshake".into());
|
||||
}
|
||||
|
||||
trace!(client = %self.addr, read_bytes = n, "Read data from client for SOCKS handshake");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Отправляет SOCKS ответ
|
||||
async fn send_socks_reply(&mut self, reply: SocksReply) -> Result<(), String> {
|
||||
let mut buf = BytesMut::with_capacity(24);
|
||||
debug!(client = %self.addr, reply = ?reply, "Sending SOCKS reply to client");
|
||||
reply.write_to(&mut buf);
|
||||
#[instrument(
|
||||
name = "socks_handler",
|
||||
skip(self, muxer),
|
||||
fields(addr = %self.addr)
|
||||
)]
|
||||
pub async fn handle_socks_client(mut self, muxer: Muxer) -> Result<(), String> {
|
||||
info!("Starting SOCKS multiplexed handling");
|
||||
|
||||
|
||||
|
||||
self.outbound
|
||||
.write_all(&buf)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(client = %self.addr, error = %e, "Failed to send SOCKS reply");
|
||||
e.to_string()
|
||||
// 1. SOCKS Handshake
|
||||
debug!("Reading SOCKS handshake request");
|
||||
let _ = self.read_socks_request().await.map_err(|e| {
|
||||
error!("SOCKS handshake failed: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
name = "socks_handler",
|
||||
skip(self, muxer),
|
||||
fields(addr = %self.addr)
|
||||
)]
|
||||
pub async fn handle_socks_client(mut self, muxer: Muxer) -> Result<(), String> {
|
||||
info!("Starting SOCKS multiplexed handling");
|
||||
|
||||
// 1. SOCKS Handshake
|
||||
debug!("Reading SOCKS handshake request");
|
||||
let _ = self.read_socks_request().await.map_err(|e| {
|
||||
error!("SOCKS handshake failed: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
self.send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 }).await?;
|
||||
|
||||
// 2. SOCKS Connect
|
||||
// 2. SOCKS Connect - читаем, КУДА хочет браузер
|
||||
let req = self.read_socks_request().await?;
|
||||
let target = if let SocksRequest::Connect { target, .. } = req {
|
||||
target
|
||||
} else {
|
||||
return Err("Expected Connect".into());
|
||||
};
|
||||
|
||||
let stream_id = muxer.next_id();
|
||||
let target_str = match &target {
|
||||
SocksTarget { host, port } => format!("{}:{}", String::from_utf8_lossy(host), port),
|
||||
};
|
||||
|
||||
// --- НОВАЯ ЛОГИКА ОЖИДАНИЯ ---
|
||||
// Регистрируем временный канал, чтобы получить Connect-подтверждение от сервера
|
||||
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(100);
|
||||
muxer.register_stream(stream_id, v_tx).await;
|
||||
|
||||
// Отправляем Connect-кадр на сервер
|
||||
muxer.to_network.send(MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Connect,
|
||||
data: Bytes::from(target_str),
|
||||
}).await.map_err(|e| e.to_string())?;
|
||||
|
||||
let first_payload = match tokio::time::timeout(std::time::Duration::from_secs(10), v_rx.recv()).await {
|
||||
Ok(Some(data)) => data,
|
||||
_ => {
|
||||
error!(stream_id, "Server timeout or failed to send Connect confirmation");
|
||||
// Шлем браузеру ошибку, если сервер промолчал
|
||||
self.send_socks_reply(SocksReply::ConnectResult {
|
||||
reply_code: 0x01, atyp: 0x01, addr: [0, 0, 0, 0], port: 0,
|
||||
}).await.ok();
|
||||
return Err("Target connection failed".into());
|
||||
}
|
||||
};
|
||||
|
||||
// Проверяем код ответа (второй байт в SOCKS5)
|
||||
if first_payload.len() >= 2 && first_payload[1] == 0x00 {
|
||||
debug!(stream_id, "Server confirmed connection, forwarding SOCKS reply to browser");
|
||||
|
||||
// ВАЖНО: Отправляем браузеру ТО, что прислал сервер (те самые 10 байт)
|
||||
// Не создаем новый SocksReply вручную, а пробрасываем байты сервера
|
||||
self.outbound.write_all(&first_payload).await.map_err(|e| e.to_string())?;
|
||||
} else {
|
||||
// Если сервер прислал ошибку (reply_code != 0), тоже пробрасываем её браузеру и выходим
|
||||
self.outbound.write_all(&first_payload).await.ok();
|
||||
return Err("Server rejected connection".into());
|
||||
self.send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 }).await?;
|
||||
|
||||
// 2. SOCKS Connect
|
||||
// 2. SOCKS Connect - читаем, КУДА хочет браузер
|
||||
let req = self.read_socks_request().await?;
|
||||
let target = if let SocksRequest::Connect { target, .. } = req {
|
||||
target
|
||||
} else {
|
||||
return Err("Expected Connect".into());
|
||||
};
|
||||
|
||||
let stream_id = muxer.next_id();
|
||||
let target_str = match &target {
|
||||
SocksTarget { host, port } => format!("{}:{}", String::from_utf8_lossy(host), port),
|
||||
};
|
||||
|
||||
// --- НОВАЯ ЛОГИКА ОЖИДАНИЯ ---
|
||||
// Регистрируем временный канал, чтобы получить Connect-подтверждение от сервера
|
||||
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(1024);
|
||||
muxer.register_stream(stream_id, v_tx).await;
|
||||
|
||||
// Отправляем Connect-кадр на сервер
|
||||
muxer.to_network.send(MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Connect,
|
||||
data: Bytes::from(target_str),
|
||||
}).await.map_err(|e| e.to_string())?;
|
||||
|
||||
let first_payload = match tokio::time::timeout(std::time::Duration::from_secs(10), v_rx.recv()).await {
|
||||
Ok(Some(data)) => data,
|
||||
_ => {
|
||||
error!(stream_id, "Server timeout or failed to send Connect confirmation");
|
||||
// Шлем браузеру ошибку, если сервер промолчал
|
||||
self.send_socks_reply(SocksReply::ConnectResult {
|
||||
reply_code: 0x01, atyp: 0x01, addr: [0, 0, 0, 0], port: 0,
|
||||
}).await.ok();
|
||||
return Err("Target connection failed".into());
|
||||
}
|
||||
};
|
||||
|
||||
// Проверяем код ответа (второй байт в SOCKS5)
|
||||
if first_payload.len() >= 2 && first_payload[1] == 0x00 {
|
||||
debug!(stream_id, "Server confirmed connection, forwarding SOCKS reply to browser");
|
||||
|
||||
// ВАЖНО: Отправляем браузеру ТО, что прислал сервер (те самые 10 байт)
|
||||
// Не создаем новый SocksReply вручную, а пробрасываем байты сервера
|
||||
self.outbound.write_all(&first_payload).await.map_err(|e| e.to_string())?;
|
||||
} else {
|
||||
// Если сервер прислал ошибку (reply_code != 0), тоже пробрасываем её браузеру и выходим
|
||||
self.outbound.write_all(&first_payload).await.ok();
|
||||
return Err("Server rejected connection".into());
|
||||
}
|
||||
|
||||
// 4. Разбираем self и запускаем хендлер
|
||||
let Self { inbound: browser_in, outbound: browser_out, .. } = self;
|
||||
|
||||
let muxer_clone = muxer.clone();
|
||||
tokio::spawn(async move {
|
||||
run_proxy_bridge(stream_id, browser_in, browser_out, muxer_clone, v_rx).await;
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 4. Разбираем self и запускаем хендлер
|
||||
let Self { inbound: browser_in, outbound: browser_out, .. } = self;
|
||||
|
||||
// Передаем v_rx (в котором уже могут быть данные от сервера после "OK") в хендлер
|
||||
spawn_client_local_handler_with_rx(stream_id, browser_in, browser_out, muxer.clone(), v_rx);
|
||||
#[instrument(
|
||||
name = "server_tunnel",
|
||||
skip(self),
|
||||
fields(addr = %self.addr)
|
||||
)]
|
||||
pub async fn handle_server_tunnel(mut self) -> Result<(), String> {
|
||||
info!("Acting as TLS Server, waiting for ClientHello");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Создаем Muxer для сервера
|
||||
let (mux_tx, mux_rx) = mpsc::channel(16384);
|
||||
let muxer = Muxer::new(mux_tx.clone(), false); // false, так как это Сервер
|
||||
|
||||
#[instrument(
|
||||
name = "server_tunnel",
|
||||
skip(self),
|
||||
fields(addr = %self.addr)
|
||||
)]
|
||||
pub async fn handle_server_tunnel(mut self) -> Result<(), String> {
|
||||
info!("Acting as TLS Server, waiting for ClientHello");
|
||||
// 1. TLS Handshake
|
||||
let server_hello_bytes = loop {
|
||||
match self.codec.make_server_handshake(&mut self.read_buf) {
|
||||
Ok(bytes) => {
|
||||
info!("ClientHello received, sending ServerHello");
|
||||
break bytes;
|
||||
},
|
||||
Err(e) if e.action == ErrorAction::Wait => {
|
||||
let n = self.inbound.read_buf(&mut self.read_buf).await
|
||||
.map_err(|err| format!("Read error: {}", err))?;
|
||||
|
||||
// Создаем Muxer для сервера
|
||||
let (mux_tx, mux_rx) = mpsc::channel(1024);
|
||||
let muxer = Muxer::new(mux_tx.clone(), false); // false, так как это Сервер
|
||||
|
||||
// 1. TLS Handshake
|
||||
let server_hello_bytes = loop {
|
||||
match self.codec.make_server_handshake(&mut self.buffers.read_buf) {
|
||||
Ok(bytes) => {
|
||||
info!("ClientHello received, sending ServerHello");
|
||||
break bytes;
|
||||
},
|
||||
Err(e) if e.action == ErrorAction::Wait => {
|
||||
let n = self.inbound.read_buf(&mut self.buffers.read_buf).await
|
||||
.map_err(|err| format!("Read error: {}", err))?;
|
||||
|
||||
if n == 0 { return Err("Client closed connection".into()); }
|
||||
if n == 0 { return Err("Client closed connection".into()); }
|
||||
}
|
||||
Err(e) => return Err(format!("TLS error: {:?}", e)),
|
||||
}
|
||||
Err(e) => return Err(format!("TLS error: {:?}", e)),
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
self.outbound.write_all(&server_hello_bytes).await.map_err(|e| e.to_string())?;
|
||||
info!("TLS Tunnel established as server");
|
||||
self.outbound.write_all(&server_hello_bytes).await.map_err(|e| e.to_string())?;
|
||||
info!("TLS Tunnel established as server");
|
||||
|
||||
// 2. Передача управления в TunnelEngine
|
||||
debug!("Handover to TunnelEngine");
|
||||
let engine = TunnelEngine {
|
||||
inbound: self.inbound,
|
||||
outbound: self.outbound,
|
||||
codec: self.codec,
|
||||
buffers: self.buffers,
|
||||
mux_rx,
|
||||
muxer,
|
||||
role: ConnectionRole::Server,
|
||||
};
|
||||
let handler = std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Server));
|
||||
|
||||
engine.run().await.map_err(|e| {
|
||||
error!("TunnelEngine error: {}", e);
|
||||
e
|
||||
})
|
||||
}
|
||||
// 2. Передача управления в TunnelEngine
|
||||
debug!("Handover to TunnelEngine");
|
||||
let engine = TunnelEngine {
|
||||
inbound: self.inbound,
|
||||
outbound: self.outbound,
|
||||
codec: self.codec,
|
||||
read_buf: self.read_buf,
|
||||
mux_rx,
|
||||
handler
|
||||
};
|
||||
|
||||
engine.run().await.map_err(|e| {
|
||||
error!("TunnelEngine error: {}", e);
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,149 +1,109 @@
|
||||
use crate::{
|
||||
protocol::{
|
||||
codec::{
|
||||
codec::Codec,
|
||||
frame::{Frame, FrameType},
|
||||
},
|
||||
errors::ErrorAction,
|
||||
},
|
||||
proxy::connection::{
|
||||
buf_pair::BufPair,
|
||||
connection::ConnectionRole,
|
||||
handler::spawn_server_target_handler,
|
||||
muxer::{MuxMessage, Muxer},
|
||||
},
|
||||
use std::sync::Arc;
|
||||
|
||||
use bytes::BytesMut;
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||
sync::mpsc::Receiver,
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
use crate::{
|
||||
protocol::{codec::codec::Codec, errors::ErrorAction},
|
||||
proxy::connection::{handler::StreamHandler, muxer::MuxMessage},
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
pub struct TunnelEngine {
|
||||
pub inbound: OwnedReadHalf,
|
||||
pub outbound: OwnedWriteHalf,
|
||||
pub codec: Codec,
|
||||
pub buffers: BufPair,
|
||||
pub read_buf: BytesMut,
|
||||
pub mux_rx: Receiver<MuxMessage>,
|
||||
pub muxer: Muxer,
|
||||
pub role: ConnectionRole,
|
||||
pub handler: Arc<StreamHandler>, // Добавь это вместо прямого вызова логики
|
||||
}
|
||||
|
||||
impl TunnelEngine {
|
||||
pub async fn run(mut self) -> Result<(), String> {
|
||||
info!(role = ?self.role, "TunnelEngine spinning up");
|
||||
|
||||
pub async fn run(self) -> Result<(), String> {
|
||||
let mut inbound = self.inbound;
|
||||
let mut outbound = self.outbound;
|
||||
let mut codec = self.codec;
|
||||
let mut read_buf = self.read_buf;
|
||||
let mut mux_rx = self.mux_rx;
|
||||
let handler = self.handler;
|
||||
loop {
|
||||
tokio::select! {
|
||||
// 1. Физический Inbound (Сеть -> Muxer)
|
||||
// Теперь возвращает Vec<Frame>, чтобы обработать всё накопленное
|
||||
res = Self::process_inbound(&mut self.inbound, &mut self.codec, &mut self.buffers) => {
|
||||
match res {
|
||||
Ok(frames) => {
|
||||
for frame in frames {
|
||||
self.handle_incoming_frame(frame).await;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if e == "EOF" {
|
||||
info!("Physical connection closed by remote (EOF)");
|
||||
} else {
|
||||
error!(error = %e, "Critical error in process_inbound");
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
res = Self::process_inbound(&mut inbound, &mut codec, &mut read_buf, &handler) => {
|
||||
res?
|
||||
}
|
||||
|
||||
// 2. Физический Outbound (Muxer -> Сеть)
|
||||
Some(msg) = self.mux_rx.recv() => {
|
||||
if let Err(e) = self.handle_outbound_msg(msg).await {
|
||||
return Err(e);
|
||||
}
|
||||
// НУЖНО ОТПРАВИТЬ В СЕТЬ (В сторону удаленного прокси)
|
||||
Some(msg) = mux_rx.recv() => {
|
||||
Self::handle_outbound( &mut outbound, &mut codec, msg).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Логика обработки конкретного фрейма (разгружаем основной loop)
|
||||
async fn handle_incoming_frame(&mut self, frame: Frame) {
|
||||
let stream_id = frame.header.stream_id;
|
||||
let frame_type = frame.header.frame_type;
|
||||
|
||||
trace!(stream_id = frame.header.stream_id, f_type = ?frame.header.frame_type, len = frame.payload.len(), "Engine received frame from network");
|
||||
match frame_type {
|
||||
FrameType::Connect => {
|
||||
match self.role {
|
||||
ConnectionRole::Server => {
|
||||
let target = String::from_utf8_lossy(&frame.payload);
|
||||
info!(stream_id, target = %target, "New Connect request received");
|
||||
spawn_server_target_handler(stream_id, frame.payload, self.muxer.clone())
|
||||
.await;
|
||||
}
|
||||
ConnectionRole::Client => {
|
||||
// Тот самый фикс: клиент получает Connect как подтверждение (ACK)
|
||||
debug!(stream_id, "Connection confirmed by server");
|
||||
self.muxer.dispatch_to_local(stream_id, frame.payload).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
FrameType::Data => {
|
||||
self.muxer.dispatch_to_local(stream_id, frame.payload).await;
|
||||
}
|
||||
FrameType::Close => {
|
||||
info!(stream_id, "Received Close frame, tearing down stream");
|
||||
// Важно: muxer должен не просто удалить, а послать EOF в локальный канал
|
||||
self.muxer.dispatch_to_local(stream_id, Bytes::new()).await;
|
||||
self.muxer.remove_stream(stream_id).await;
|
||||
}
|
||||
_ => debug!(stream_id, ?frame_type, "Received unhandled frame type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Вспомогательная функция для чтения из сети
|
||||
async fn process_inbound(
|
||||
inbound: &mut OwnedReadHalf,
|
||||
codec: &mut Codec,
|
||||
buffers: &mut BufPair,
|
||||
) -> Result<Vec<Frame>, String> {
|
||||
let mut frames = Vec::new();
|
||||
|
||||
// Сначала читаем данные из сокета в буфер
|
||||
read_buf: &mut BytesMut,
|
||||
handler: &Arc<StreamHandler>,
|
||||
) -> Result<(), String> {
|
||||
let n = inbound
|
||||
.read_buf(&mut buffers.read_buf)
|
||||
.read_buf(read_buf)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if n == 0 && buffers.read_buf.is_empty() {
|
||||
if n == 0 && read_buf.is_empty() {
|
||||
return Err("EOF".into());
|
||||
}
|
||||
|
||||
// Теперь пытаемся достать столько фреймов, сколько получится
|
||||
loop {
|
||||
match codec.inbound(&mut buffers.read_buf) {
|
||||
Ok(Some(frame)) => frames.push(frame),
|
||||
Ok(None) => break, // Больше полных фреймов нет
|
||||
Err(e) if e.action == ErrorAction::Wait => break,
|
||||
Err(e) => return Err(format!("Codec error: {:?}", e)),
|
||||
match codec.inbound(read_buf) {
|
||||
// 1. Успешно достали фрейм
|
||||
Ok(Some(frame)) => {
|
||||
handler.handle(frame).await;
|
||||
}
|
||||
// 2. Данных в буфере недостаточно (нужно подождать еще)
|
||||
Ok(None) => break,
|
||||
|
||||
// 3. Ошибка кодека
|
||||
Err(e) => {
|
||||
// Если кодек говорит "подожди", выходим из цикла парсинга
|
||||
if e.action == ErrorAction::Wait {
|
||||
break;
|
||||
}
|
||||
// Иначе — это реальная проблема (кривой TLS и т.д.)
|
||||
return Err(format!("Codec error: {:?}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(frames)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_outbound_msg(&mut self, msg: MuxMessage) -> Result<(), String> {
|
||||
match self
|
||||
.codec
|
||||
.encrypt_data(msg.stream_id, msg.frame_type, msg.data)
|
||||
{
|
||||
async fn handle_outbound(
|
||||
outbound: &mut OwnedWriteHalf,
|
||||
codec: &mut Codec,
|
||||
msg: MuxMessage,
|
||||
) -> Result<(), String> {
|
||||
// 1. Шифруем данные, используя только кодек
|
||||
match codec.encrypt_data(msg.stream_id, msg.frame_type, msg.data) {
|
||||
Ok(pkt) => {
|
||||
self.outbound
|
||||
// 2. Пишем в сокет, используя только outbound
|
||||
outbound
|
||||
.write_all(&pkt)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
.map_err(|e| {
|
||||
error!(stream_id = msg.stream_id, error = %e, "Failed to write encrypted data to network");
|
||||
e.to_string()
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(format!("Encryption error: {:?}", e)),
|
||||
Err(e) => {
|
||||
error!(stream_id = msg.stream_id, error = ?e, "Encryption failed for outbound message");
|
||||
Err(format!("Encryption error: {:?}", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,190 +1,98 @@
|
||||
use crate::protocol::codec::frame::FrameType;
|
||||
use crate::protocol::codec::socks::{SocksReply, ATYP_IPV4, REPLY_SUCCESS};
|
||||
use crate::proxy::connection::muxer::{MuxMessage, Muxer};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
pub fn spawn_client_local_handler_with_rx(
|
||||
stream_id: u32,
|
||||
mut r: OwnedReadHalf,
|
||||
mut w: OwnedWriteHalf,
|
||||
use crate::{
|
||||
protocol::codec::{
|
||||
frame::{Frame, FrameType},
|
||||
socks::SocksReply,
|
||||
},
|
||||
proxy::connection::{bridge::run_proxy_bridge, connection::ConnectionRole, muxer::Muxer},
|
||||
};
|
||||
|
||||
// proxy/connection/stream_handler.rs
|
||||
pub struct StreamHandler {
|
||||
muxer: Muxer,
|
||||
mut v_rx: mpsc::Receiver<Bytes>, // Используем эту читалку, она уже зарегистрирована!
|
||||
) {
|
||||
// ВНИМАНИЕ: Здесь больше не создаем канал и не вызываем register_stream,
|
||||
// так как это уже сделал handle_socks_client до вызова этой функции.
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut buf = BytesMut::with_capacity(8192);
|
||||
debug!(
|
||||
stream_id,
|
||||
"Spawned client local handler with existing receiver"
|
||||
);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// 1. Читаем из браузера -> Шлем в общий туннель
|
||||
res = r.read_buf(&mut buf) => {
|
||||
match res {
|
||||
Ok(0) => {
|
||||
debug!(stream_id, "Browser closed connection (EOF)");
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
error!(stream_id, error = %e, "Read error from browser");
|
||||
break;
|
||||
}
|
||||
Ok(n) => {
|
||||
let msg = MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Data,
|
||||
data: buf.split().freeze(),
|
||||
};
|
||||
if muxer.to_network.send(msg).await.is_err() { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. Читаем из виртуального канала (данные из туннеля) -> Шлем браузеру
|
||||
maybe_data = v_rx.recv() => {
|
||||
match maybe_data {
|
||||
Some(data) => {
|
||||
if let Err(e) = w.write_all(&data).await {
|
||||
error!(stream_id, error = %e, "Write error to browser");
|
||||
break;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
debug!(stream_id, "Virtual channel closed by Muxer");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// КРИТИЧНО: Сообщаем серверу, что мы закрываем этот конкретный стрим
|
||||
let _ = muxer
|
||||
.to_network
|
||||
.send(MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Close,
|
||||
data: Bytes::new(),
|
||||
})
|
||||
.await;
|
||||
|
||||
// Чистим за собой в таблице стримов
|
||||
muxer.remove_stream(stream_id).await;
|
||||
info!(stream_id, "Client handler terminated");
|
||||
});
|
||||
role: ConnectionRole,
|
||||
}
|
||||
pub async fn spawn_server_target_handler(stream_id: u32, target_raw: Bytes, muxer: Muxer) {
|
||||
let addr = String::from_utf8_lossy(&target_raw).to_string();
|
||||
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(100);
|
||||
muxer.register_stream(stream_id, v_tx).await;
|
||||
|
||||
tokio::spawn(async move {
|
||||
info!(stream_id, target = %addr, "Attempting to connect to target");
|
||||
impl StreamHandler {
|
||||
pub fn new(muxer: Muxer, role: ConnectionRole) -> Self {
|
||||
Self { muxer, role }
|
||||
}
|
||||
|
||||
match TcpStream::connect(&addr).await {
|
||||
Ok(stream) => {
|
||||
// Формируем SOCKS5 Success Reply используя структуру
|
||||
let mut reply_buf = BytesMut::with_capacity(10);
|
||||
let reply = SocksReply::ConnectResult {
|
||||
reply_code: REPLY_SUCCESS,
|
||||
atyp: ATYP_IPV4,
|
||||
addr: [0, 0, 0, 0],
|
||||
port: 0,
|
||||
};
|
||||
reply.write_to(&mut reply_buf);
|
||||
pub async fn handle(&self, frame: Frame) {
|
||||
let stream_id = frame.header.stream_id;
|
||||
|
||||
// Отправляем подтверждение в сеть
|
||||
let _ = muxer
|
||||
.to_network
|
||||
.send(MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Connect,
|
||||
data: reply_buf.freeze(),
|
||||
})
|
||||
.await;
|
||||
match frame.header.frame_type {
|
||||
FrameType::Connect => self.on_connect(stream_id, frame.payload).await,
|
||||
FrameType::Data => self.on_data(stream_id, frame.payload).await,
|
||||
FrameType::Close => self.on_close(stream_id).await,
|
||||
_ => debug!(stream_id, "Unhandled frame type"),
|
||||
}
|
||||
}
|
||||
|
||||
info!(stream_id, target = %addr, "Connected to target host, SOCKS reply sent");
|
||||
async fn on_connect(&self, stream_id: u32, payload: Bytes) {
|
||||
if self.role == ConnectionRole::Server {
|
||||
let target_str = String::from_utf8_lossy(&payload).to_string();
|
||||
let muxer = self.muxer.clone();
|
||||
|
||||
let (mut r, mut w) = stream.into_split();
|
||||
let mut buf = BytesMut::with_capacity(8192);
|
||||
let (v_tx, v_rx) = tokio::sync::mpsc::channel(100);
|
||||
muxer.register_stream(stream_id, v_tx).await;
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// Сеть -> Прокси -> Интернет (Запись в целевой хост)
|
||||
Some(data) = v_rx.recv() => {
|
||||
if data.is_empty() { break; } // EOF от локального клиента
|
||||
if let Err(e) = w.write_all(&data).await {
|
||||
warn!(stream_id, error = ?e, "Target write failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Интернет -> Прокси -> Сеть (Чтение из целевого хоста)
|
||||
res = r.read_buf(&mut buf) => {
|
||||
match res {
|
||||
Ok(0) => {
|
||||
debug!(stream_id, "Target host closed connection (EOF)");
|
||||
break;
|
||||
}
|
||||
Ok(n) => {
|
||||
let msg = MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Data,
|
||||
data: buf.split().freeze(),
|
||||
};
|
||||
if muxer.to_network.send(msg).await.is_err() { break; }
|
||||
}
|
||||
Err(e) => {
|
||||
error!(stream_id, error = %e, "Target read error");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
tokio::spawn(async move {
|
||||
info!(stream_id, target = %target_str, "Attempting remote connection");
|
||||
|
||||
match tokio::net::TcpStream::connect(&target_str).await {
|
||||
Ok(stream) => {
|
||||
// --- ШАГ 2: ШЛЕМ ПОДТВЕРЖДЕНИЕ ---
|
||||
let mut reply_buf = BytesMut::with_capacity(10);
|
||||
let reply = SocksReply::ConnectResult {
|
||||
reply_code: 0x00,
|
||||
atyp: 0x01,
|
||||
addr: [0, 0, 0, 0],
|
||||
port: 0,
|
||||
};
|
||||
reply.write_to(&mut reply_buf);
|
||||
|
||||
let _ = muxer
|
||||
.send_control(stream_id, FrameType::Connect, reply_buf.freeze())
|
||||
.await;
|
||||
|
||||
// --- ШАГ 3: ЗАПУСКАЕМ МОСТ ---
|
||||
let (r, w) = stream.into_split();
|
||||
run_proxy_bridge(stream_id, r, w, muxer, v_rx).await;
|
||||
}
|
||||
Err(e) => {
|
||||
error!(stream_id, error = %e, "Connection failed");
|
||||
// Если не подключились — удаляем стрим, чтобы не висел в мапе
|
||||
muxer.remove_stream(stream_id).await;
|
||||
|
||||
let mut reply_buf = BytesMut::with_capacity(10);
|
||||
let reply = SocksReply::ConnectResult {
|
||||
reply_code: 0x01,
|
||||
atyp: 0x01,
|
||||
addr: [0, 0, 0, 0],
|
||||
port: 0,
|
||||
};
|
||||
reply.write_to(&mut reply_buf);
|
||||
let _ = muxer
|
||||
.send_control(stream_id, FrameType::Connect, reply_buf.freeze())
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!(stream_id, target = %addr, error = %e, "Connection to target failed");
|
||||
|
||||
// Формируем SOCKS5 Failure Reply (0x01 - General failure)
|
||||
let mut reply_buf = BytesMut::with_capacity(10);
|
||||
let reply = SocksReply::ConnectResult {
|
||||
reply_code: 0x01,
|
||||
atyp: ATYP_IPV4,
|
||||
addr: [0, 0, 0, 0],
|
||||
port: 0,
|
||||
};
|
||||
reply.write_to(&mut reply_buf);
|
||||
|
||||
let _ = muxer
|
||||
.to_network
|
||||
.send(MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Connect,
|
||||
data: reply_buf.freeze(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Логика для клиента (проброс ответа сервера браузеру)
|
||||
self.muxer.dispatch_to_local(stream_id, payload).await;
|
||||
}
|
||||
}
|
||||
|
||||
// Финализация: уведомляем сеть о закрытии и чистим муксер
|
||||
let _ = muxer
|
||||
.to_network
|
||||
.send(MuxMessage {
|
||||
stream_id,
|
||||
frame_type: FrameType::Close,
|
||||
data: Bytes::new(),
|
||||
})
|
||||
.await;
|
||||
async fn on_data(&self, stream_id: u32, payload: Bytes) {
|
||||
self.muxer.dispatch_to_local(stream_id, payload).await;
|
||||
}
|
||||
|
||||
muxer.remove_stream(stream_id).await;
|
||||
info!(stream_id, "Server target handler closed");
|
||||
});
|
||||
async fn on_close(&self, stream_id: u32) {
|
||||
self.muxer.dispatch_to_local(stream_id, Bytes::new()).await;
|
||||
self.muxer.remove_stream(stream_id).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod buf_pair;
|
||||
pub mod bridge;
|
||||
pub mod connection;
|
||||
pub mod engine;
|
||||
pub mod handler;
|
||||
|
||||
@@ -46,7 +46,6 @@ impl Muxer {
|
||||
}
|
||||
}
|
||||
|
||||
// Прокси-метод для получения ID
|
||||
pub fn next_id(&self) -> u32 {
|
||||
self.id_gen.next()
|
||||
}
|
||||
@@ -65,6 +64,22 @@ impl Muxer {
|
||||
self.streams.write().await.remove(&stream_id);
|
||||
}
|
||||
|
||||
pub async fn send_control(
|
||||
&self,
|
||||
stream_id: u32,
|
||||
f_type: FrameType,
|
||||
data: Bytes,
|
||||
) -> Result<(), String> {
|
||||
self.to_network
|
||||
.send(MuxMessage {
|
||||
stream_id,
|
||||
frame_type: f_type,
|
||||
data,
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Отправляет входящие данные конкретному локальному обработчику
|
||||
pub async fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
|
||||
// Асинхронный лок сам умеет корректно работать с .await
|
||||
|
||||
+42
-60
@@ -36,34 +36,26 @@ impl Network {
|
||||
|
||||
match self.role {
|
||||
ConnectionRole::Client => {
|
||||
// --- ЛОГИКА КЛИЕНТА ---
|
||||
// 1. Создаем ОДИН туннель до прокси-сервера при старте
|
||||
info!("Starting Client mode: Initializing persistent tunnel to proxy...");
|
||||
|
||||
let muxer = match self.initialize_client_tunnel().await {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
error!(error = %e, "Failed to initialize global tunnel. Exiting.");
|
||||
error!(error = %e, "Global tunnel failed. Exit.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// 2. Теперь слушаем SOCKS-запросы от браузера
|
||||
let listener = TcpListener::bind(&addr)
|
||||
.await
|
||||
.expect("Failed to bind SOCKS port");
|
||||
info!(socks_addr = %addr, "SOCKS5 server ready for browser connections");
|
||||
let listener = TcpListener::bind(&addr).await.expect("SOCKS bind failed");
|
||||
info!(socks_addr = %addr, "SOCKS5 ready");
|
||||
|
||||
loop {
|
||||
if let Ok((stream, client_addr)) = listener.accept().await {
|
||||
let current_muxer = muxer.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
// Используем новый метод handle_socks_client
|
||||
// Здесь мы просто создаем Connection и сразу в SOCKS
|
||||
let connection = Connection::new(stream, client_addr, false);
|
||||
if let Err(e) = connection.handle_socks_client(current_muxer).await {
|
||||
error!(client = %client_addr, error = %e, "SOCKS stream error");
|
||||
}
|
||||
let _ = connection.handle_socks_client(current_muxer).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -93,71 +85,61 @@ impl Network {
|
||||
|
||||
/// Вспомогательный метод для Клиента: создает TLS туннель и запускает TunnelEngine
|
||||
async fn initialize_client_tunnel(&self) -> Result<Muxer, String> {
|
||||
let server_addr = self
|
||||
.remote_proxy_addr
|
||||
.as_ref()
|
||||
.ok_or("Remote proxy address not configured")?;
|
||||
let server_addr = self.remote_proxy_addr.as_ref().ok_or("No proxy addr")?;
|
||||
|
||||
// 1. Устанавливаем TCP соединение с сервером
|
||||
// Вместо создания Connection (который нужен для обработки клиентов),
|
||||
// работаем напрямую с TcpStream для первичного TLS-хендшейка.
|
||||
let stream = TcpStream::connect(server_addr)
|
||||
.await
|
||||
.map_err(|e| format!("Connect to proxy failed: {}", e))?;
|
||||
.map_err(|e| e.to_string())?;
|
||||
let (mut inbound, mut outbound) = stream.into_split();
|
||||
|
||||
// 2. Создаем временный Connection
|
||||
let dummy_addr = server_addr.parse().unwrap_or("0.0.0.0:0".parse().unwrap());
|
||||
// Передаем stream (Connection сам сделает into_split внутри, если у тебя так написано в new)
|
||||
let mut conn = Connection::new(stream, dummy_addr, false);
|
||||
// Кодек создаем «с чистого листа»
|
||||
let mut codec = crate::protocol::codec::codec::Codec::new(false);
|
||||
|
||||
// 3. TLS Handshake (Клиентская часть)
|
||||
debug!("Starting persistent TLS handshake with proxy");
|
||||
let ch = conn
|
||||
.codec
|
||||
// --- TLS Handshake ---
|
||||
let ch = codec
|
||||
.make_client_handshake(&BrowserProfile::CHROME_131, "proxy.server")
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
conn.outbound
|
||||
.write_all(&ch)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
outbound.write_all(&ch).await.map_err(|e| e.to_string())?;
|
||||
|
||||
let mut sh_buf = BytesMut::with_capacity(2048);
|
||||
while let Err(e) = conn.codec.process_handshake(&mut sh_buf) {
|
||||
if e.action != ErrorAction::Wait {
|
||||
return Err(format!("Fatal handshake error: {:?}", e));
|
||||
}
|
||||
// Теперь read_buf найдется, так как мы импортировали AsyncReadExt
|
||||
let n = conn
|
||||
.inbound
|
||||
.read_buf(&mut sh_buf)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
if n == 0 {
|
||||
return Err("Server closed connection during handshake".into());
|
||||
loop {
|
||||
// Пытаемся обработать то, что уже есть в буфере
|
||||
match codec.process_handshake(&mut sh_buf) {
|
||||
Ok(_) => break, // Готово!
|
||||
Err(e) if e.action == ErrorAction::Wait => {
|
||||
let n = inbound
|
||||
.read_buf(&mut sh_buf)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
if n == 0 {
|
||||
return Err("EOF during handshake".into());
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(format!("TLS error: {:?}", e)),
|
||||
}
|
||||
}
|
||||
info!("Persistent TLS Tunnel established successfully!");
|
||||
|
||||
// 4. Инициализируем Muxer и TunnelEngine
|
||||
// Явно указываем тип сообщения для канала, чтобы убрать "cannot infer type"
|
||||
let (mux_tx, mux_rx) = tokio::sync::mpsc::channel::<MuxMessage>(1024);
|
||||
// --- Запуск инфраструктуры ---
|
||||
let (mux_tx, mux_rx) = tokio::sync::mpsc::channel(16384);
|
||||
let muxer = Muxer::new(mux_tx, true);
|
||||
|
||||
// 5. Запускаем TunnelEngine в фоне.
|
||||
let handler = std::sync::Arc::new(crate::proxy::connection::handler::StreamHandler::new(
|
||||
muxer.clone(),
|
||||
ConnectionRole::Client,
|
||||
));
|
||||
|
||||
let engine = TunnelEngine {
|
||||
inbound: conn.inbound,
|
||||
outbound: conn.outbound,
|
||||
codec: conn.codec,
|
||||
buffers: conn.buffers,
|
||||
inbound,
|
||||
outbound,
|
||||
codec,
|
||||
read_buf: sh_buf, // Передаем остатки данных из буфера хендшейка в движок!
|
||||
mux_rx,
|
||||
muxer: muxer.clone(),
|
||||
role: ConnectionRole::Client,
|
||||
handler,
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = engine.run().await {
|
||||
error!("Main TunnelEngine died: {}", e);
|
||||
}
|
||||
});
|
||||
tokio::spawn(async move { engine.run().await });
|
||||
|
||||
Ok(muxer)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use aead::rand_core::RngCore;
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
|
||||
use crate::{
|
||||
@@ -98,25 +97,16 @@ impl ClientHello {
|
||||
public_key: &[u8; 32],
|
||||
salt: [u8; 32],
|
||||
) -> Bytes {
|
||||
// 1. Key Exchange: Generate ECDH pair and get public key
|
||||
let tls_random = salt;
|
||||
|
||||
// 2. Authentication: Generate 32 bytes for TLS Random
|
||||
// [16 bytes entropy] + [16 bytes HMAC(timestamp)]
|
||||
let mut tls_random = salt;
|
||||
//todo
|
||||
let auth_token = [0; 16]; //generate_auth_tag(&[]);
|
||||
// tls_random[16..32].copy_from_slice(&auth_token);
|
||||
|
||||
// 3. Extensions: Build the extensions block using the profile
|
||||
let mut ext_builder = ExtensionBuilder::new();
|
||||
// Pass the public key into the KeyShare extension via apply_profile
|
||||
|
||||
ext_builder.apply_profile(profile, host, public_key);
|
||||
let extensions_bytes = ext_builder.build();
|
||||
|
||||
let mut session_id = BytesMut::with_capacity(32);
|
||||
session_id.put_slice(&[0u8; 32]);
|
||||
|
||||
// 4. Assemble ClientHello Handshake message
|
||||
let client_hello = ClientHello {
|
||||
version: ProtocolVersion::Tls12, // Legacy version for compatibility
|
||||
random: tls_random,
|
||||
@@ -125,14 +115,12 @@ impl ClientHello {
|
||||
extensions: extensions_bytes,
|
||||
};
|
||||
|
||||
// 5. Wrap ClientHello into a TLS Record
|
||||
let record = TlsRecord::new(
|
||||
ContentType::Handshake,
|
||||
ProtocolVersion::Tls10,
|
||||
client_hello.serialize(),
|
||||
);
|
||||
|
||||
// Final result: Byte buffer ready for the wire
|
||||
record.serialize()
|
||||
}
|
||||
}
|
||||
@@ -151,7 +139,7 @@ impl ServerHello {
|
||||
server_public_key: &[u8],
|
||||
salt: [u8; 32],
|
||||
) -> Self {
|
||||
let mut server_random = salt;
|
||||
let server_random = salt;
|
||||
|
||||
let selected_suite = client_hello
|
||||
.cipher_suites
|
||||
|
||||
Reference in New Issue
Block a user