update processes and code style

This commit is contained in:
2026-03-01 17:49:58 +07:00
parent 3590d1a435
commit 322dc5b6b0
20 changed files with 547 additions and 769 deletions
Generated
+1 -101
View File
@@ -96,17 +96,6 @@ version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
[[package]]
name = "async-trait"
version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@@ -244,16 +233,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "colored"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.6.7" version = "4.6.7"
@@ -610,15 +589,11 @@ name = "netrunner-common"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"aead", "aead",
"anyhow",
"async-trait",
"bytes", "bytes",
"chacha20poly1305", "chacha20poly1305",
"colored",
"hex", "hex",
"hkdf", "hkdf",
"hmac", "hmac",
"log",
"rand", "rand",
"sha2", "sha2",
"tokio", "tokio",
@@ -631,9 +606,7 @@ dependencies = [
name = "netrunner-server" name = "netrunner-server"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"clap", "clap",
"log",
"netrunner-common", "netrunner-common",
"tokio", "tokio",
] ]
@@ -1220,15 +1193,6 @@ dependencies = [
"windows-targets 0.42.2", "windows-targets 0.42.2",
] ]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.60.2" version = "0.60.2"
@@ -1262,22 +1226,6 @@ dependencies = [
"windows_x86_64_msvc 0.42.2", "windows_x86_64_msvc 0.42.2",
] ]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.53.5" version = "0.53.5"
@@ -1288,7 +1236,7 @@ dependencies = [
"windows_aarch64_gnullvm 0.53.1", "windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1", "windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1", "windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1", "windows_i686_gnullvm",
"windows_i686_msvc 0.53.1", "windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1", "windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1", "windows_x86_64_gnullvm 0.53.1",
@@ -1301,12 +1249,6 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.53.1" version = "0.53.1"
@@ -1319,12 +1261,6 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.53.1" version = "0.53.1"
@@ -1337,24 +1273,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.53.1" version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]] [[package]]
name = "windows_i686_gnullvm" name = "windows_i686_gnullvm"
version = "0.53.1" version = "0.53.1"
@@ -1367,12 +1291,6 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.53.1" version = "0.53.1"
@@ -1385,12 +1303,6 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.53.1" version = "0.53.1"
@@ -1403,12 +1315,6 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.53.1" version = "0.53.1"
@@ -1421,12 +1327,6 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.53.1" version = "0.53.1"
-4
View File
@@ -11,14 +11,10 @@ aead = { version = "0.5.2", features = ["bytes"] }
hkdf = "0.12" hkdf = "0.12"
hmac = "0.12" hmac = "0.12"
sha2 = "0.10" sha2 = "0.10"
anyhow = "1.0"
bytes = "1.5" bytes = "1.5"
log = "0.4"
tokio = "1.49.0" tokio = "1.49.0"
rand = "0.10.0" rand = "0.10.0"
async-trait = "0.1.89"
tracing = "0.1" tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
colored = "2.0"
hex = "0.4.3" hex = "0.4.3"
+9 -12
View File
@@ -75,14 +75,13 @@ impl AeadPacker for ChaChaCipher {
// Сначала получаем текущий counter для лога (до того, как next_nonce его инкрементирует) // Сначала получаем текущий counter для лога (до того, как next_nonce его инкрементирует)
let current_counter = self.encrypt_state.counter; let current_counter = self.encrypt_state.counter;
let nonce = self.encrypt_state.next_nonce(); let nonce = self.encrypt_state.next_nonce();
let nonce_hex = hex::encode(nonce);
let data_len = data.len(); let data_len = data.len();
match self.encrypt_cipher.encrypt_in_place(&nonce, &[], data) { match self.encrypt_cipher.encrypt_in_place(&nonce, &[], data) {
Ok(_) => { Ok(_) => {
tracing::trace!( tracing::trace!(
counter = current_counter, counter = current_counter,
nonce = %nonce_hex, nonce = %hex::encode(nonce),
len = data_len, len = data_len,
"Encryption successful" "Encryption successful"
); );
@@ -91,7 +90,7 @@ impl AeadPacker for ChaChaCipher {
Err(e) => { Err(e) => {
tracing::error!( tracing::error!(
counter = current_counter, counter = current_counter,
nonce = %nonce_hex, nonce = %hex::encode(nonce),
len = data_len, len = data_len,
error = ?e, error = ?e,
"AEAD encryption failure" "AEAD encryption failure"
@@ -104,29 +103,27 @@ impl AeadPacker for ChaChaCipher {
fn decrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error> { fn decrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error> {
let current_counter = self.decrypt_state.counter; let current_counter = self.decrypt_state.counter;
let nonce = self.decrypt_state.next_nonce(); let nonce = self.decrypt_state.next_nonce();
let nonce_hex = hex::encode(nonce);
let data_len = data.len(); 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) { match self.decrypt_cipher.decrypt_in_place(&nonce, &[], data) {
Ok(_) => { Ok(_) => {
tracing::trace!( tracing::trace!(
counter = current_counter, counter = current_counter,
nonce = %nonce_hex, nonce = %hex::encode(nonce),
len = data_len, len = data_len,
"Decryption successful" "Decryption successful"
); );
Ok(data.split().freeze()) Ok(data.split().freeze())
} }
Err(e) => { Err(e) => {
let data_prefix = if data.len() >= 8 {
hex::encode(&data[..8])
} else {
hex::encode(data.as_ref())
};
tracing::error!( tracing::error!(
counter = current_counter, counter = current_counter,
nonce = %nonce_hex, nonce = %hex::encode(nonce),
len = data_len, len = data_len,
prefix = %data_prefix, prefix = %data_prefix,
error = ?e, error = ?e,
+1 -1
View File
@@ -7,7 +7,7 @@ pub fn logger_init() {
.with_line_number(true); // Показывать строку кода (очень полезно для дебага) .with_line_number(true); // Показывать строку кода (очень полезно для дебага)
let filter_layer = EnvFilter::try_from_default_env() let filter_layer = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new("trace")) // По умолчанию уровень info .or_else(|_| EnvFilter::try_new("info")) // По умолчанию уровень info
.unwrap(); .unwrap();
tracing_subscriber::registry() 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 { pub fn pack_app_data(buffer: Bytes) -> Bytes {
TlsRecord::build_application_data(buffer) TlsRecord::build_application_data(buffer)
} }
-2
View File
@@ -1,2 +0,0 @@
pub mod netr_bridge;
pub mod tls_bridge;
+35 -61
View File
@@ -3,7 +3,7 @@ use bytes::{Bytes, BytesMut};
use crate::crypto::aead::AeadPacker; use crate::crypto::aead::AeadPacker;
use crate::crypto::chacha::ChaChaCipher; use crate::crypto::chacha::ChaChaCipher;
use crate::crypto::session::SessionKeys; 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::frame::{Frame, FrameHeader, FrameType};
use crate::protocol::codec::padding::Padding; use crate::protocol::codec::padding::Padding;
use crate::protocol::errors::{ErrorAction, ErrorStage, TlsError}; use crate::protocol::errors::{ErrorAction, ErrorStage, TlsError};
@@ -24,30 +24,24 @@ impl Codec {
staging: BytesMut::new(), staging: BytesMut::new(),
} }
} }
//maybe generator?
//should anwer socks5 and open connection to proxy server
/// Логика для Клиента: Генерирует байты ClientHello для инициализации соединения.
/// Клиент: генерирует TLS Record [ ClientHello ]
pub fn make_client_handshake( pub fn make_client_handshake(
&mut self, &mut self,
profile: &BrowserProfile, profile: &BrowserProfile,
host: &str, host: &str,
) -> Result<Bytes, TlsError> { ) -> Result<Bytes, TlsError> {
// 1. Извлекаем публичный ключ нашей текущей сессии let pub_key = self.session_keys.ecdh.public_key.to_bytes();
let my_pub_key = self.session_keys.ecdh.public_key.to_bytes();
// (Убедись, что метод возвращает [u8; 32])
// 2. Передаем его в мост // 2. Передаем его в мост
Ok(TlsBridge::wrap_client_hello( Ok(TlsBridge::wrap_client_hello(
profile, profile,
host, host,
&my_pub_key, &pub_key,
self.session_keys.salt.get_local(), self.session_keys.salt.get_local(),
)) ))
} }
/// Сервер: берет буфер, достает ClientHello и генерирует в ответ TLS Record [ ServerHello ]
pub fn make_server_handshake(&mut self, buffer: &mut BytesMut) -> Result<Bytes, TlsError> { pub fn make_server_handshake(&mut self, buffer: &mut BytesMut) -> Result<Bytes, TlsError> {
// 1. Распаковываем сообщение клиента
let client_msg = TlsBridge::unpack_handshake(buffer)?.ok_or_else(|| { let client_msg = TlsBridge::unpack_handshake(buffer)?.ok_or_else(|| {
TlsError::new( TlsError::new(
ErrorStage::Handshake("No CH"), 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_pub_key = self.session_keys.ecdh.public_key.to_bytes();
let server_hello_record = TlsBridge::wrap_server_hello( let server_hello_record = TlsBridge::wrap_server_hello(
&client_msg, &client_msg,
@@ -64,8 +57,6 @@ impl Codec {
self.session_keys.salt.get_local(), self.session_keys.salt.get_local(),
)?; )?;
// 3. ОБНОВЛЕНИЕ КЛЮЧЕЙ НА СЕРВЕРЕ
// Передаем true, так как сервер ПАРСИТ ClientHello (смещение 6 байт)
let (w_key, w_iv, r_key, r_iv) = self let (w_key, w_iv, r_key, r_iv) = self
.session_keys .session_keys
.update_keys(client_msg.random(), client_msg.extensions(), true) .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> { pub fn inbound(&mut self, buffer: &mut BytesMut) -> Result<Option<Frame>, TlsError> {
// Логгируем входящее состояние сетевого буфера (TLS слой) // 1. Сначала проверяем, нет ли уже готового фрейма в staging с прошлого раза
if !buffer.is_empty() { if !self.staging.is_empty() {
let header = &buffer[..std::cmp::min(buffer.len(), 5)]; if let Some(frame) = self.try_parse_frame()? {
tracing::debug!( return Ok(Some(frame));
buf_len = buffer.len(), }
header_hex = %hex::encode(header),
"RAW TLS buffer state"
);
} }
// --- ШАГ 1: Извлекаем ВСЕ доступные TLS-рекорды и расшифровываем в staging --- // 2. Распаковываем ВСЕ доступные TLS-рекорды из сетевого буфера
// Мы крутим цикл, пока TlsBridge может "откусить" целый TLS-рекорд из buffer
while let Some(app_data) = TlsBridge::unpack_app_data(buffer)? { while let Some(app_data) = TlsBridge::unpack_app_data(buffer)? {
let mut encrypted_chunk = BytesMut::from(app_data.payload.as_ref()); // Берем Bytes напрямую (app_data.payload — это уже Bytes)
let raw_len = encrypted_chunk.len(); let mut data_to_decrypt = BytesMut::from(app_data.payload);
// Расшифровываем кусок // Дешифруем "на месте" (In-place decryption)
let decrypted_chunk = self.crypto.decrypt(&mut encrypted_chunk).map_err(|e| { // Твоя библиотека ChaCha скорее всего поддерживает дешифровку прямо в том же буфере
tracing::error!(len = raw_len, "Decryption failed: {:?}", e); let decrypted = self.crypto.decrypt(&mut data_to_decrypt).map_err(|_| {
TlsError::new( TlsError::new(
ErrorStage::Tls("Decryption error"), ErrorStage::Tls("Decr error"),
ErrorAction::Drop, ErrorAction::Drop,
Bytes::new(), Bytes::new(),
) )
})?; })?;
// КЛАДЕМ В ЧИСТУЮ ЗОНУ: расшифрованный поток байтов нашего протокола // ВАЖНО: Вместо extend_from_slice (копирование), используем split_off/unsplit или просто Bytes
self.staging.extend_from_slice(&decrypted_chunk); // Если staging — это BytesMut, используй put или reserve
self.staging.extend_from_slice(&decrypted); // Увы, BytesMut требует копирования для конкатенации
tracing::debug!( // НО! Мы можем попытаться распарсить фрейм сразу после добавления каждого рекорда
added = decrypted_chunk.len(), if let Some(frame) = self.try_parse_frame()? {
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)); 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(),
));
}
}
} }
Ok(None) 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 -1
View File
@@ -1,4 +1,4 @@
mod bridges; mod bridge;
pub mod codec; pub mod codec;
pub mod frame; pub mod frame;
mod padding; mod padding;
+44
View File
@@ -1,5 +1,7 @@
use bytes::{BufMut, Bytes, BytesMut}; use bytes::{BufMut, Bytes, BytesMut};
use crate::protocol::parser::parser::Parser;
pub const SOCKS5_VERSION: u8 = 0x05; pub const SOCKS5_VERSION: u8 = 0x05;
pub const REPLY_SUCCESS: u8 = 0x00; pub const REPLY_SUCCESS: u8 = 0x00;
pub const REPLY_AUTH_FAILURE: u8 = 0xFF; pub const REPLY_AUTH_FAILURE: u8 = 0xFF;
@@ -17,6 +19,48 @@ pub enum SocksRequest {
Connect { command: u8, target: SocksTarget }, Connect { command: u8, target: SocksTarget },
Unknown, 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)] #[derive(Debug)]
pub enum SocksReply { pub enum SocksReply {
+70
View File
@@ -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;
}
-43
View File
@@ -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();
}
}
+23 -26
View File
@@ -1,6 +1,6 @@
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use tracing::{instrument, info, debug, error, trace, warn}; use tracing::{instrument, info, debug, error, trace, warn};
use std::{collections::HashMap, net::SocketAddr}; use std::{net::SocketAddr};
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
net::{ net::{
@@ -21,22 +21,16 @@ use crate::{
parser::parser::Parser, parser::parser::Parser,
}, },
proxy::connection::{ proxy::connection::{
buf_pair::BufPair, bridge::run_proxy_bridge, engine::TunnelEngine, handler::StreamHandler, muxer::{MuxMessage, Muxer}
engine::TunnelEngine,
handler::spawn_client_local_handler_with_rx,
muxer::{MuxMessage, Muxer},
}, },
}; };
pub struct VirtualStreams { pub struct BufPair {
pub channels: HashMap<u32, tokio::sync::mpsc::Sender<Bytes>>, 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)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConnectionRole { pub enum ConnectionRole {
@@ -48,7 +42,7 @@ pub struct Connection {
addr: SocketAddr, addr: SocketAddr,
pub inbound: OwnedReadHalf, pub inbound: OwnedReadHalf,
pub outbound: OwnedWriteHalf, pub outbound: OwnedWriteHalf,
pub buffers: BufPair, pub read_buf: BytesMut,
pub codec: Codec, pub codec: Codec,
} }
@@ -63,7 +57,7 @@ impl Connection {
addr, addr,
inbound, inbound,
outbound, outbound,
buffers: BufPair::new(), read_buf: BytesMut::with_capacity(BUF_SIZE),
codec: Codec::new(init), codec: Codec::new(init),
} }
} }
@@ -72,7 +66,7 @@ impl Connection {
async fn read_socks_request(&mut self) -> Result<SocksRequest, String> { async fn read_socks_request(&mut self) -> Result<SocksRequest, String> {
loop { loop {
// Попытка парсинга из текущего буфера // Попытка парсинга из текущего буфера
match SocksRequest::parse(&mut self.buffers.read_buf) { match SocksRequest::parse(&mut self.read_buf) {
Ok(Some(req)) => { Ok(Some(req)) => {
// Используем Debug-вывод (?req), так как SocksRequest обычно Enum // Используем Debug-вывод (?req), так как SocksRequest обычно Enum
info!(client = %self.addr, request = ?req, "SOCKS request successfully parsed"); info!(client = %self.addr, request = ?req, "SOCKS request successfully parsed");
@@ -80,7 +74,7 @@ impl Connection {
} }
Ok(None) => { Ok(None) => {
// Это не ошибка, просто данных в сокете пока меньше, чем размер структуры SOCKS // Это не ошибка, просто данных в сокете пока меньше, чем размер структуры SOCKS
trace!(client = %self.addr, buffer_len = self.buffers.read_buf.len(), "SOCKS parse: need more data"); trace!(client = %self.addr, buffer_len = self.read_buf.len(), "SOCKS parse: need more data");
} }
Err(e) => { Err(e) => {
error!(client = %self.addr, error = %e, "SOCKS protocol violation"); error!(client = %self.addr, error = %e, "SOCKS protocol violation");
@@ -91,7 +85,7 @@ impl Connection {
// Чтение новых данных из сокета // Чтение новых данных из сокета
let n = self let n = self
.inbound .inbound
.read_buf(&mut self.buffers.read_buf) .read_buf(&mut self.read_buf)
.await .await
.map_err(|e| { .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 read from socket during SOCKS handshake");
@@ -159,7 +153,7 @@ pub async fn handle_socks_client(mut self, muxer: Muxer) -> Result<(), String> {
// --- НОВАЯ ЛОГИКА ОЖИДАНИЯ --- // --- НОВАЯ ЛОГИКА ОЖИДАНИЯ ---
// Регистрируем временный канал, чтобы получить Connect-подтверждение от сервера // Регистрируем временный канал, чтобы получить Connect-подтверждение от сервера
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(100); let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(1024);
muxer.register_stream(stream_id, v_tx).await; muxer.register_stream(stream_id, v_tx).await;
// Отправляем Connect-кадр на сервер // Отправляем Connect-кадр на сервер
@@ -197,8 +191,10 @@ let first_payload = match tokio::time::timeout(std::time::Duration::from_secs(10
// 4. Разбираем self и запускаем хендлер // 4. Разбираем self и запускаем хендлер
let Self { inbound: browser_in, outbound: browser_out, .. } = self; let Self { inbound: browser_in, outbound: browser_out, .. } = self;
// Передаем v_rx (в котором уже могут быть данные от сервера после "OK") в хендлер let muxer_clone = muxer.clone();
spawn_client_local_handler_with_rx(stream_id, browser_in, browser_out, muxer.clone(), v_rx); tokio::spawn(async move {
run_proxy_bridge(stream_id, browser_in, browser_out, muxer_clone, v_rx).await;
});
Ok(()) Ok(())
} }
@@ -212,18 +208,18 @@ pub async fn handle_server_tunnel(mut self) -> Result<(), String> {
info!("Acting as TLS Server, waiting for ClientHello"); info!("Acting as TLS Server, waiting for ClientHello");
// Создаем Muxer для сервера // Создаем Muxer для сервера
let (mux_tx, mux_rx) = mpsc::channel(1024); let (mux_tx, mux_rx) = mpsc::channel(16384);
let muxer = Muxer::new(mux_tx.clone(), false); // false, так как это Сервер let muxer = Muxer::new(mux_tx.clone(), false); // false, так как это Сервер
// 1. TLS Handshake // 1. TLS Handshake
let server_hello_bytes = loop { let server_hello_bytes = loop {
match self.codec.make_server_handshake(&mut self.buffers.read_buf) { match self.codec.make_server_handshake(&mut self.read_buf) {
Ok(bytes) => { Ok(bytes) => {
info!("ClientHello received, sending ServerHello"); info!("ClientHello received, sending ServerHello");
break bytes; break bytes;
}, },
Err(e) if e.action == ErrorAction::Wait => { Err(e) if e.action == ErrorAction::Wait => {
let n = self.inbound.read_buf(&mut self.buffers.read_buf).await let n = self.inbound.read_buf(&mut self.read_buf).await
.map_err(|err| format!("Read error: {}", err))?; .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()); }
@@ -235,16 +231,17 @@ pub async fn handle_server_tunnel(mut self) -> Result<(), String> {
self.outbound.write_all(&server_hello_bytes).await.map_err(|e| e.to_string())?; self.outbound.write_all(&server_hello_bytes).await.map_err(|e| e.to_string())?;
info!("TLS Tunnel established as server"); info!("TLS Tunnel established as server");
let handler = std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Server));
// 2. Передача управления в TunnelEngine // 2. Передача управления в TunnelEngine
debug!("Handover to TunnelEngine"); debug!("Handover to TunnelEngine");
let engine = TunnelEngine { let engine = TunnelEngine {
inbound: self.inbound, inbound: self.inbound,
outbound: self.outbound, outbound: self.outbound,
codec: self.codec, codec: self.codec,
buffers: self.buffers, read_buf: self.read_buf,
mux_rx, mux_rx,
muxer, handler
role: ConnectionRole::Server,
}; };
engine.run().await.map_err(|e| { engine.run().await.map_err(|e| {
+71 -111
View File
@@ -1,149 +1,109 @@
use crate::{ use std::sync::Arc;
protocol::{
codec::{ use bytes::BytesMut;
codec::Codec, use tokio::{
frame::{Frame, FrameType}, io::{AsyncReadExt, AsyncWriteExt},
}, net::tcp::{OwnedReadHalf, OwnedWriteHalf},
errors::ErrorAction, sync::mpsc::Receiver,
}, };
proxy::connection::{ use tracing::error;
buf_pair::BufPair,
connection::ConnectionRole, use crate::{
handler::spawn_server_target_handler, protocol::{codec::codec::Codec, errors::ErrorAction},
muxer::{MuxMessage, Muxer}, 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 struct TunnelEngine {
pub inbound: OwnedReadHalf, pub inbound: OwnedReadHalf,
pub outbound: OwnedWriteHalf, pub outbound: OwnedWriteHalf,
pub codec: Codec, pub codec: Codec,
pub buffers: BufPair, pub read_buf: BytesMut,
pub mux_rx: Receiver<MuxMessage>, pub mux_rx: Receiver<MuxMessage>,
pub muxer: Muxer, pub handler: Arc<StreamHandler>, // Добавь это вместо прямого вызова логики
pub role: ConnectionRole,
} }
impl TunnelEngine { impl TunnelEngine {
pub async fn run(mut self) -> Result<(), String> { pub async fn run(self) -> Result<(), String> {
info!(role = ?self.role, "TunnelEngine spinning up"); 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 { loop {
tokio::select! { tokio::select! {
// 1. Физический Inbound (Сеть -> Muxer) res = Self::process_inbound(&mut inbound, &mut codec, &mut read_buf, &handler) => {
// Теперь возвращает Vec<Frame>, чтобы обработать всё накопленное res?
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);
}
}
} }
// 2. Физический Outbound (Muxer -> Сеть) // НУЖНО ОТПРАВИТЬ В СЕТЬ (В сторону удаленного прокси)
Some(msg) = self.mux_rx.recv() => { Some(msg) = mux_rx.recv() => {
if let Err(e) = self.handle_outbound_msg(msg).await { Self::handle_outbound( &mut outbound, &mut codec, msg).await?;
return Err(e);
}
} }
} }
} }
} }
/// Логика обработки конкретного фрейма (разгружаем основной 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( async fn process_inbound(
inbound: &mut OwnedReadHalf, inbound: &mut OwnedReadHalf,
codec: &mut Codec, codec: &mut Codec,
buffers: &mut BufPair, read_buf: &mut BytesMut,
) -> Result<Vec<Frame>, String> { handler: &Arc<StreamHandler>,
let mut frames = Vec::new(); ) -> Result<(), String> {
// Сначала читаем данные из сокета в буфер
let n = inbound let n = inbound
.read_buf(&mut buffers.read_buf) .read_buf(read_buf)
.await .await
.map_err(|e| e.to_string())?; .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()); return Err("EOF".into());
} }
// Теперь пытаемся достать столько фреймов, сколько получится
loop { loop {
match codec.inbound(&mut buffers.read_buf) { match codec.inbound(read_buf) {
Ok(Some(frame)) => frames.push(frame), // 1. Успешно достали фрейм
Ok(None) => break, // Больше полных фреймов нет Ok(Some(frame)) => {
Err(e) if e.action == ErrorAction::Wait => break, handler.handle(frame).await;
Err(e) => return Err(format!("Codec error: {:?}", e)),
}
} }
// 2. Данных в буфере недостаточно (нужно подождать еще)
Ok(None) => break,
Ok(frames) // 3. Ошибка кодека
Err(e) => {
// Если кодек говорит "подожди", выходим из цикла парсинга
if e.action == ErrorAction::Wait {
break;
}
// Иначе — это реальная проблема (кривой TLS и т.д.)
return Err(format!("Codec error: {:?}", e));
}
}
} }
async fn handle_outbound_msg(&mut self, msg: MuxMessage) -> Result<(), String> {
match self
.codec
.encrypt_data(msg.stream_id, msg.frame_type, msg.data)
{
Ok(pkt) => {
self.outbound
.write_all(&pkt)
.await
.map_err(|e| e.to_string())?;
Ok(()) Ok(())
} }
Err(e) => Err(format!("Encryption error: {:?}", e)),
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) => {
// 2. Пишем в сокет, используя только outbound
outbound
.write_all(&pkt)
.await
.map_err(|e| {
error!(stream_id = msg.stream_id, error = %e, "Failed to write encrypted data to network");
e.to_string()
})?;
Ok(())
}
Err(e) => {
error!(stream_id = msg.stream_id, error = ?e, "Encryption failed for outbound message");
Err(format!("Encryption error: {:?}", e))
}
} }
} }
} }
+58 -150
View File
@@ -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 bytes::{Bytes, BytesMut};
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tracing::{debug, error, info};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use tokio::net::TcpStream;
use tokio::sync::mpsc;
use tracing::{debug, error, info, trace, warn};
pub fn spawn_client_local_handler_with_rx( use crate::{
stream_id: u32, protocol::codec::{
mut r: OwnedReadHalf, frame::{Frame, FrameType},
mut w: OwnedWriteHalf, socks::SocksReply,
muxer: Muxer, },
mut v_rx: mpsc::Receiver<Bytes>, // Используем эту читалку, она уже зарегистрирована! proxy::connection::{bridge::run_proxy_bridge, connection::ConnectionRole, muxer::Muxer},
) {
// ВНИМАНИЕ: Здесь больше не создаем канал и не вызываем 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; }
} // proxy/connection/stream_handler.rs
} pub struct StreamHandler {
} muxer: Muxer,
// 2. Читаем из виртуального канала (данные из туннеля) -> Шлем браузеру role: ConnectionRole,
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;
}
} }
impl StreamHandler {
pub fn new(muxer: Muxer, role: ConnectionRole) -> Self {
Self { muxer, role }
} }
pub async fn handle(&self, frame: Frame) {
let stream_id = frame.header.stream_id;
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"),
} }
} }
// КРИТИЧНО: Сообщаем серверу, что мы закрываем этот конкретный стрим async fn on_connect(&self, stream_id: u32, payload: Bytes) {
let _ = muxer if self.role == ConnectionRole::Server {
.to_network let target_str = String::from_utf8_lossy(&payload).to_string();
.send(MuxMessage { let muxer = self.muxer.clone();
stream_id,
frame_type: FrameType::Close,
data: Bytes::new(),
})
.await;
// Чистим за собой в таблице стримов let (v_tx, v_rx) = tokio::sync::mpsc::channel(100);
muxer.remove_stream(stream_id).await;
info!(stream_id, "Client handler terminated");
});
}
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; muxer.register_stream(stream_id, v_tx).await;
tokio::spawn(async move { tokio::spawn(async move {
info!(stream_id, target = %addr, "Attempting to connect to target"); info!(stream_id, target = %target_str, "Attempting remote connection");
match TcpStream::connect(&addr).await { match tokio::net::TcpStream::connect(&target_str).await {
Ok(stream) => { Ok(stream) => {
// Формируем SOCKS5 Success Reply используя структуру // --- ШАГ 2: ШЛЕМ ПОДТВЕРЖДЕНИЕ ---
let mut reply_buf = BytesMut::with_capacity(10); let mut reply_buf = BytesMut::with_capacity(10);
let reply = SocksReply::ConnectResult { let reply = SocksReply::ConnectResult {
reply_code: REPLY_SUCCESS, reply_code: 0x00,
atyp: ATYP_IPV4, atyp: 0x01,
addr: [0, 0, 0, 0], addr: [0, 0, 0, 0],
port: 0, port: 0,
}; };
reply.write_to(&mut reply_buf); reply.write_to(&mut reply_buf);
// Отправляем подтверждение в сеть
let _ = muxer let _ = muxer
.to_network .send_control(stream_id, FrameType::Connect, reply_buf.freeze())
.send(MuxMessage {
stream_id,
frame_type: FrameType::Connect,
data: reply_buf.freeze(),
})
.await; .await;
info!(stream_id, target = %addr, "Connected to target host, SOCKS reply sent"); // --- ШАГ 3: ЗАПУСКАЕМ МОСТ ---
let (r, w) = stream.into_split();
let (mut r, mut w) = stream.into_split(); run_proxy_bridge(stream_id, r, w, muxer, v_rx).await;
let mut buf = BytesMut::with_capacity(8192);
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) => { Err(e) => {
error!(stream_id, error = %e, "Target read error"); error!(stream_id, error = %e, "Connection failed");
break; // Если не подключились — удаляем стрим, чтобы не висел в мапе
} muxer.remove_stream(stream_id).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 mut reply_buf = BytesMut::with_capacity(10);
let reply = SocksReply::ConnectResult { let reply = SocksReply::ConnectResult {
reply_code: 0x01, reply_code: 0x01,
atyp: ATYP_IPV4, atyp: 0x01,
addr: [0, 0, 0, 0], addr: [0, 0, 0, 0],
port: 0, port: 0,
}; };
reply.write_to(&mut reply_buf); reply.write_to(&mut reply_buf);
let _ = muxer let _ = muxer
.to_network .send_control(stream_id, FrameType::Connect, reply_buf.freeze())
.send(MuxMessage {
stream_id,
frame_type: FrameType::Connect,
data: reply_buf.freeze(),
})
.await; .await;
} }
} }
// Финализация: уведомляем сеть о закрытии и чистим муксер
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, "Server target handler closed");
}); });
} else {
// Логика для клиента (проброс ответа сервера браузеру)
self.muxer.dispatch_to_local(stream_id, payload).await;
}
}
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.dispatch_to_local(stream_id, Bytes::new()).await;
self.muxer.remove_stream(stream_id).await;
}
} }
+1 -1
View File
@@ -1,4 +1,4 @@
pub mod buf_pair; pub mod bridge;
pub mod connection; pub mod connection;
pub mod engine; pub mod engine;
pub mod handler; pub mod handler;
+16 -1
View File
@@ -46,7 +46,6 @@ impl Muxer {
} }
} }
// Прокси-метод для получения ID
pub fn next_id(&self) -> u32 { pub fn next_id(&self) -> u32 {
self.id_gen.next() self.id_gen.next()
} }
@@ -65,6 +64,22 @@ impl Muxer {
self.streams.write().await.remove(&stream_id); 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) { pub async fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
// Асинхронный лок сам умеет корректно работать с .await // Асинхронный лок сам умеет корректно работать с .await
+38 -56
View File
@@ -36,34 +36,26 @@ impl Network {
match self.role { match self.role {
ConnectionRole::Client => { ConnectionRole::Client => {
// --- ЛОГИКА КЛИЕНТА ---
// 1. Создаем ОДИН туннель до прокси-сервера при старте
info!("Starting Client mode: Initializing persistent tunnel to proxy..."); info!("Starting Client mode: Initializing persistent tunnel to proxy...");
let muxer = match self.initialize_client_tunnel().await { let muxer = match self.initialize_client_tunnel().await {
Ok(m) => m, Ok(m) => m,
Err(e) => { Err(e) => {
error!(error = %e, "Failed to initialize global tunnel. Exiting."); error!(error = %e, "Global tunnel failed. Exit.");
return; return;
} }
}; };
// 2. Теперь слушаем SOCKS-запросы от браузера let listener = TcpListener::bind(&addr).await.expect("SOCKS bind failed");
let listener = TcpListener::bind(&addr) info!(socks_addr = %addr, "SOCKS5 ready");
.await
.expect("Failed to bind SOCKS port");
info!(socks_addr = %addr, "SOCKS5 server ready for browser connections");
loop { loop {
if let Ok((stream, client_addr)) = listener.accept().await { if let Ok((stream, client_addr)) = listener.accept().await {
let current_muxer = muxer.clone(); let current_muxer = muxer.clone();
tokio::spawn(async move { tokio::spawn(async move {
// Используем новый метод handle_socks_client // Здесь мы просто создаем Connection и сразу в SOCKS
let connection = Connection::new(stream, client_addr, false); let connection = Connection::new(stream, client_addr, false);
if let Err(e) = connection.handle_socks_client(current_muxer).await { let _ = connection.handle_socks_client(current_muxer).await;
error!(client = %client_addr, error = %e, "SOCKS stream error");
}
}); });
} }
} }
@@ -93,71 +85,61 @@ impl Network {
/// Вспомогательный метод для Клиента: создает TLS туннель и запускает TunnelEngine /// Вспомогательный метод для Клиента: создает TLS туннель и запускает TunnelEngine
async fn initialize_client_tunnel(&self) -> Result<Muxer, String> { async fn initialize_client_tunnel(&self) -> Result<Muxer, String> {
let server_addr = self let server_addr = self.remote_proxy_addr.as_ref().ok_or("No proxy addr")?;
.remote_proxy_addr
.as_ref()
.ok_or("Remote proxy address not configured")?;
// 1. Устанавливаем TCP соединение с сервером // Вместо создания Connection (который нужен для обработки клиентов),
// работаем напрямую с TcpStream для первичного TLS-хендшейка.
let stream = TcpStream::connect(server_addr) let stream = TcpStream::connect(server_addr)
.await .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()); let mut codec = crate::protocol::codec::codec::Codec::new(false);
// Передаем stream (Connection сам сделает into_split внутри, если у тебя так написано в new)
let mut conn = Connection::new(stream, dummy_addr, false);
// 3. TLS Handshake (Клиентская часть) // --- TLS Handshake ---
debug!("Starting persistent TLS handshake with proxy"); let ch = codec
let ch = conn
.codec
.make_client_handshake(&BrowserProfile::CHROME_131, "proxy.server") .make_client_handshake(&BrowserProfile::CHROME_131, "proxy.server")
.map_err(|e| format!("{:?}", e))?; .map_err(|e| format!("{:?}", e))?;
outbound.write_all(&ch).await.map_err(|e| e.to_string())?;
conn.outbound
.write_all(&ch)
.await
.map_err(|e| e.to_string())?;
let mut sh_buf = BytesMut::with_capacity(2048); let mut sh_buf = BytesMut::with_capacity(2048);
while let Err(e) = conn.codec.process_handshake(&mut sh_buf) { loop {
if e.action != ErrorAction::Wait { // Пытаемся обработать то, что уже есть в буфере
return Err(format!("Fatal handshake error: {:?}", e)); match codec.process_handshake(&mut sh_buf) {
} Ok(_) => break, // Готово!
// Теперь read_buf найдется, так как мы импортировали AsyncReadExt Err(e) if e.action == ErrorAction::Wait => {
let n = conn let n = inbound
.inbound
.read_buf(&mut sh_buf) .read_buf(&mut sh_buf)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
if n == 0 { if n == 0 {
return Err("Server closed connection during handshake".into()); 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(16384);
let (mux_tx, mux_rx) = tokio::sync::mpsc::channel::<MuxMessage>(1024);
let muxer = Muxer::new(mux_tx, true); 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 { let engine = TunnelEngine {
inbound: conn.inbound, inbound,
outbound: conn.outbound, outbound,
codec: conn.codec, codec,
buffers: conn.buffers, read_buf: sh_buf, // Передаем остатки данных из буфера хендшейка в движок!
mux_rx, mux_rx,
muxer: muxer.clone(), handler,
role: ConnectionRole::Client,
}; };
tokio::spawn(async move { tokio::spawn(async move { engine.run().await });
if let Err(e) = engine.run().await {
error!("Main TunnelEngine died: {}", e);
}
});
Ok(muxer) Ok(muxer)
} }
+3 -15
View File
@@ -1,4 +1,3 @@
use aead::rand_core::RngCore;
use bytes::{BufMut, Bytes, BytesMut}; use bytes::{BufMut, Bytes, BytesMut};
use crate::{ use crate::{
@@ -98,25 +97,16 @@ impl ClientHello {
public_key: &[u8; 32], public_key: &[u8; 32],
salt: [u8; 32], salt: [u8; 32],
) -> Bytes { ) -> 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(); 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); ext_builder.apply_profile(profile, host, public_key);
let extensions_bytes = ext_builder.build(); let extensions_bytes = ext_builder.build();
let mut session_id = BytesMut::with_capacity(32); let mut session_id = BytesMut::with_capacity(32);
session_id.put_slice(&[0u8; 32]); session_id.put_slice(&[0u8; 32]);
// 4. Assemble ClientHello Handshake message
let client_hello = ClientHello { let client_hello = ClientHello {
version: ProtocolVersion::Tls12, // Legacy version for compatibility version: ProtocolVersion::Tls12, // Legacy version for compatibility
random: tls_random, random: tls_random,
@@ -125,14 +115,12 @@ impl ClientHello {
extensions: extensions_bytes, extensions: extensions_bytes,
}; };
// 5. Wrap ClientHello into a TLS Record
let record = TlsRecord::new( let record = TlsRecord::new(
ContentType::Handshake, ContentType::Handshake,
ProtocolVersion::Tls10, ProtocolVersion::Tls10,
client_hello.serialize(), client_hello.serialize(),
); );
// Final result: Byte buffer ready for the wire
record.serialize() record.serialize()
} }
} }
@@ -151,7 +139,7 @@ impl ServerHello {
server_public_key: &[u8], server_public_key: &[u8],
salt: [u8; 32], salt: [u8; 32],
) -> Self { ) -> Self {
let mut server_random = salt; let server_random = salt;
let selected_suite = client_hello let selected_suite = client_hello
.cipher_suites .cipher_suites
-2
View File
@@ -7,5 +7,3 @@ edition = "2021"
netrunner-common = { path = "../common" } netrunner-common = { path = "../common" }
tokio = { version = "1.36", features = ["full"] } tokio = { version = "1.36", features = ["full"] }
clap = { version = "4.4", features = ["derive"] } clap = { version = "4.4", features = ["derive"] }
anyhow = "1.0"
log = "0.4"