update processes and code style
This commit is contained in:
Generated
+1
-101
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
@@ -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::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,4 +1,4 @@
|
|||||||
mod bridges;
|
mod bridge;
|
||||||
pub mod codec;
|
pub mod codec;
|
||||||
pub mod frame;
|
pub mod frame;
|
||||||
mod padding;
|
mod padding;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 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| {
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,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;
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
|
||||||
Reference in New Issue
Block a user