base proxy ready

This commit is contained in:
2026-03-01 14:24:53 +07:00
parent 2835108b7f
commit 3590d1a435
75 changed files with 3197 additions and 2089 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
use chacha20poly1305::aead::Buffer;
use bytes::{Bytes, BytesMut};
pub trait AeadPacker {
fn encrypt<B: Buffer>(&mut self, data: &mut B) -> Result<(), chacha20poly1305::aead::Error>;
fn decrypt<B: Buffer>(&mut self, data: &mut B) -> Result<(), chacha20poly1305::aead::Error>;
fn encrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error>;
fn decrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error>;
}
+103 -44
View File
@@ -1,80 +1,139 @@
use bytes::{Bytes, BytesMut};
use chacha20poly1305::aead::generic_array::GenericArray;
use chacha20poly1305::aead::{Buffer, OsRng};
use chacha20poly1305::{
AeadCore, AeadInPlace, ChaCha20Poly1305, ChaChaPoly1305, Key, KeyInit, Nonce,
};
use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, Key, KeyInit, Nonce};
use crate::crypto::aead::AeadPacker;
pub struct NonceState {
counter: u64,
nonce: Nonce,
handshake: bool,
base_iv: [u8; 12],
}
impl NonceState {
pub fn new() -> Self {
let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
pub fn new(base_iv: [u8; 12]) -> Self {
Self {
counter: 0,
nonce,
handshake: false,
base_iv,
}
}
pub fn get_handshake(&self) -> bool {
return self.handshake;
}
pub fn set_nonce(&mut self, nonce: Nonce) {
self.nonce = nonce;
self.handshake = true
}
pub fn increase_counter(&mut self) {
self.counter += 1;
// Возвращает nonce для текущего пакета и ПЕРЕХОДИТ к следующему
pub fn next_nonce(&mut self) -> Nonce {
let mut iv = self.base_iv;
// В TLS 1.3 используется Little Endian для счетчика при XOR
// Но если ты сам пишешь протокол, BE тоже пойдет.
// Главное — единообразие.
let counter_bytes = self.counter.to_be_bytes();
self.nonce[4..12].copy_from_slice(&counter_bytes);
for i in 0..8 {
iv[i + 4] ^= counter_bytes[i];
}
self.counter += 1;
*GenericArray::from_slice(&iv)
}
}
pub struct ChaChaCipher {
key: Key,
pub encrypt_cipher: ChaCha20Poly1305,
pub decrypt_cipher: ChaCha20Poly1305,
pub encrypt_state: NonceState,
pub decrypt_state: NonceState,
pub cipher: ChaCha20Poly1305,
}
impl ChaChaCipher {
pub fn new() -> Self {
let key = GenericArray::clone_from_slice(&[0; 32]);
let cipher = ChaCha20Poly1305::new(&key);
let start_key = Key::from([0u8; 32]);
let encrypt_cipher = ChaCha20Poly1305::new(&start_key);
let decrypt_cipher = ChaCha20Poly1305::new(&start_key);
Self {
key,
encrypt_state: NonceState::new(),
decrypt_state: NonceState::new(),
cipher,
encrypt_state: NonceState::new([0u8; 12]),
decrypt_state: NonceState::new([0u8; 12]),
encrypt_cipher,
decrypt_cipher,
}
}
pub fn set_key(&mut self, key: Key) -> () {
self.key = key;
self.cipher = ChaChaPoly1305::new(&self.key);
pub fn set_keys(
&mut self,
w_key: [u8; 32],
w_iv: [u8; 12], // Write (исходящие)
r_key: [u8; 32],
r_iv: [u8; 12], // Read (входящие)
) {
self.encrypt_cipher = ChaCha20Poly1305::new(Key::from_slice(&w_key));
self.decrypt_cipher = ChaCha20Poly1305::new(Key::from_slice(&r_key));
self.encrypt_state = NonceState::new(w_iv);
self.decrypt_state = NonceState::new(r_iv);
tracing::debug!("Cipher keys and IVs updated for both directions");
}
}
impl AeadPacker for ChaChaCipher {
fn encrypt<B: Buffer>(&mut self, data: &mut B) -> Result<(), chacha20poly1305::aead::Error> {
self.cipher
.encrypt_in_place(&self.encrypt_state.nonce, &[], data)?;
self.encrypt_state.increase_counter();
Ok(())
fn encrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error> {
// Сначала получаем текущий counter для лога (до того, как next_nonce его инкрементирует)
let current_counter = self.encrypt_state.counter;
let nonce = self.encrypt_state.next_nonce();
let nonce_hex = hex::encode(nonce);
let data_len = data.len();
match self.encrypt_cipher.encrypt_in_place(&nonce, &[], data) {
Ok(_) => {
tracing::trace!(
counter = current_counter,
nonce = %nonce_hex,
len = data_len,
"Encryption successful"
);
Ok(data.split().freeze())
}
Err(e) => {
tracing::error!(
counter = current_counter,
nonce = %nonce_hex,
len = data_len,
error = ?e,
"AEAD encryption failure"
);
Err(e)
}
}
}
fn decrypt<B: Buffer>(&mut self, data: &mut B) -> Result<(), chacha20poly1305::aead::Error> {
self.cipher
.decrypt_in_place(&self.decrypt_state.nonce, &[], data)?;
self.decrypt_state.increase_counter();
Ok(())
fn decrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error> {
let current_counter = self.decrypt_state.counter;
let nonce = self.decrypt_state.next_nonce();
let nonce_hex = hex::encode(nonce);
let data_len = data.len();
let data_prefix = if data.len() >= 8 {
hex::encode(&data[..8])
} else {
hex::encode(data.as_ref())
};
match self.decrypt_cipher.decrypt_in_place(&nonce, &[], data) {
Ok(_) => {
tracing::trace!(
counter = current_counter,
nonce = %nonce_hex,
len = data_len,
"Decryption successful"
);
Ok(data.split().freeze())
}
Err(e) => {
tracing::error!(
counter = current_counter,
nonce = %nonce_hex,
len = data_len,
prefix = %data_prefix,
error = ?e,
"AEAD decryption failure! Verification failed or data malformed"
);
Err(e)
}
}
}
}
+5 -4
View File
@@ -1,6 +1,7 @@
pub mod aead;
pub mod chacha;
pub mod ecdh;
pub mod hkdf;
pub mod hmac;
pub mod salt_pair;
mod ecdh;
mod hkdf;
mod hmac;
mod salt_pair;
pub mod session;
+128
View File
@@ -0,0 +1,128 @@
use x25519_dalek::PublicKey;
use crate::{
crypto::{ecdh::ECDH, hkdf::HKDF, salt_pair::SaltPair},
tlseng::extension::ExtensionStack,
};
pub struct SessionKeys {
pub salt: SaltPair,
pub ecdh: ECDH,
}
impl SessionKeys {
pub fn new(is_initiator: bool) -> Self {
Self {
salt: SaltPair::new(is_initiator),
ecdh: ECDH::new(),
}
}
pub fn update_keys(
&mut self,
salt: [u8; 32],
extensions: &ExtensionStack,
is_server: bool, // true если мы Сервер (парсим ClientHello), false если Клиент
) -> Result<([u8; 32], [u8; 12], [u8; 32], [u8; 12]), String> {
// Сохраняем соль от удаленной стороны
self.salt.set_remote_salt(salt);
tracing::debug!(
remote_salt = %hex::encode(&salt[..8]),
local_salt = %hex::encode(&self.salt.get_local()[..8]),
total_salt = %hex::encode(&self.salt.get_total()[28..36]),
"Updating keys with new salt"
);
const EXT_KEY_SHARE: u16 = 0x0033;
if let Some(dh_data) = extensions.find_by_type(EXT_KEY_SHARE) {
let mut key_bytes = [0u8; 32];
if is_server {
// МЫ СЕРВЕР: Парсим ClientHello KeyShare (там список)
// Минимум: 2 (длина списка) + 2 (группа) + 2 (длина ключа) + 32 (ключ) = 38
if dh_data.len() < 38 {
return Err(format!(
"Client KeyShare too short: {} bytes",
dh_data.len()
));
}
let mut found = false;
// Ищем маркер x25519 (00 1d) и длину 32 (00 20)
for i in 2..=(dh_data.len() - 34) {
if dh_data[i..i + 4] == [0x00, 0x1d, 0x00, 0x20] {
key_bytes.copy_from_slice(&dh_data[i + 4..i + 36]);
found = true;
break;
}
}
if !found {
return Err("Could not find x25519 key in ClientHello".into());
}
} else {
// МЫ КЛИЕНТ: Парсим ServerHello KeyShare (там сразу группа и ключ)
// [Group:2] [KeyLen:2] [Key:32] = 36 байт
if dh_data.len() < 36 {
return Err("Server KeyShare too short".into());
}
key_bytes.copy_from_slice(&dh_data[4..36]);
}
// Проверка на "кривой" ключ
if key_bytes.iter().all(|&x| x == 0) {
return Err("Extracted remote public key is all ZEROS!".into());
}
let public_key = PublicKey::from(key_bytes);
tracing::debug!(
remote_pub = %hex::encode(&key_bytes[..4]),
role = if is_server { "Server" } else { "Client" },
"Key exchange successful, deriving material..."
);
// Вызываем генерацию, которая вернет (w_key, w_iv, r_key, r_iv)
self.generate_keys(&public_key, is_server)
} else {
Err("No KeyShare extension found in handshake".into())
}
}
fn generate_keys(
&mut self,
public_key: &PublicKey,
is_server: bool,
) -> Result<([u8; 32], [u8; 12], [u8; 32], [u8; 12]), String> {
let shared_key = self
.ecdh
.get_shared(public_key)
.ok_or_else(|| "No shared secret".to_string())?;
tracing::debug!(
shared_prefix = %hex::encode(&shared_key[..8]),
"DH Shared secret derived"
);
let hkdf = HKDF::extract_key(&self.salt.get_total(), &shared_key);
let c_key = HKDF::expand_key::<32>(&hkdf, b"client_aead").map_err(|e| e.to_string())?;
let c_iv = HKDF::expand_key::<12>(&hkdf, b"client_iv").map_err(|e| e.to_string())?;
let s_key = HKDF::expand_key::<32>(&hkdf, b"server_aead").map_err(|e| e.to_string())?;
let s_iv = HKDF::expand_key::<12>(&hkdf, b"server_iv").map_err(|e| e.to_string())?;
tracing::info!(
client_key_short = %hex::encode(&c_key[..4]),
server_key_short = %hex::encode(&s_key[..4]),
"HKDF expansion complete"
);
// Распределяем: (write_key, write_iv, read_key, read_iv)
if is_server {
Ok((s_key, s_iv, c_key, c_iv)) // Сервер пишет своим, читает клиентским
} else {
Ok((c_key, c_iv, s_key, s_iv)) // Клиент пишет своим, читает серверным
}
}
}
+3
View File
@@ -1,5 +1,8 @@
pub mod crypto;
mod logger;
pub mod protocol;
pub mod proxy;
pub mod tlseng;
pub mod utils;
pub use logger::logger_init;
+17
View File
@@ -0,0 +1,17 @@
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
pub fn logger_init() {
let fmt_layer = fmt::layer()
.with_target(true) // Показывать, из какого модуля пришел лог
.with_thread_ids(false)
.with_line_number(true); // Показывать строку кода (очень полезно для дебага)
let filter_layer = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new("trace")) // По умолчанию уровень info
.unwrap();
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
}
+2 -1
View File
@@ -1 +1,2 @@
pub mod tls;
pub mod netr_bridge;
pub mod tls_bridge;
@@ -1,44 +0,0 @@
use bytes::{Bytes, BytesMut};
use crate::{
protocol::{
codec::bridges::tls::tls_interceptor::TlsInterceptor,
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
},
tlseng::{application_data::ApplicationData, tls_record::TlsRecord, types::ContentType},
};
impl TlsInterceptor for ApplicationData {
type Output = ApplicationData;
fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, InterceptorError> {
let mut payload = BytesMut::from(record.payload.as_ref());
match record.content_type {
ContentType::ApplicationData => Self::handle_application_data(&mut payload),
_ => {
println!("content type byte is: {:?}", record.content_type);
Err(InterceptorError::new(
ErrorType::ApplicationData("Not Application Data"),
ErrorAction::Drop,
record.serialize(),
))
}
}
}
}
impl ApplicationData {
fn handle_application_data(payload: &mut BytesMut) -> Result<Option<Self>, InterceptorError> {
println!("Bytes here?: {:?}", &payload);
let data_option = ApplicationData::start_process(payload)?;
let data = data_option.ok_or_else(|| {
InterceptorError::new(
ErrorType::ApplicationData("AppData Err TODO"),
ErrorAction::Drop,
Bytes::copy_from_slice(&[]),
)
})?;
println!("Получены Application Data: {} байт", payload.len());
Ok(Some(data))
}
}
@@ -1,80 +0,0 @@
use bytes::{Bytes, BytesMut};
use x25519_dalek::PublicKey;
use crate::{
protocol::codec::{
bridges::tls::{handshake::HandshakeMessage, tls_interceptor::TlsInterceptor},
codec::Codec,
},
tlseng::{application_data::ApplicationData, consts::EXT_KEY_SHARE, extension::ExtensionStack},
};
pub struct TlsBridge;
impl TlsBridge {
fn process_key_share(extensions: &ExtensionStack, codec: &mut Codec) -> Result<(), String> {
if let Some(dh_key) = extensions.find_by_type(EXT_KEY_SHARE) {
let mut key = [0u8; 32];
if dh_key.len() < 32 {
return Err("Key too short".into());
}
key.copy_from_slice(&dh_key[..32]);
let public_key = PublicKey::from(key);
codec
.session_keys
.generate_keys(&public_key)
.map_err(|e| format!("Key gen error: {:?}", e))?; // Обработка Result
Ok(())
} else {
Err("No key share extension found".into())
}
}
//unpack handshake
pub fn make_handshake(buffer: &mut BytesMut, codec: &mut Codec) -> Result<(), String> {
let incoming_process = HandshakeMessage::start_process(buffer);
println!("buffer {:02x?}", &buffer);
let message_option = match incoming_process {
Ok(mes) => mes,
Err(e) => {
println!("Error: {:?}", e);
None
}
};
if let Some(message) = message_option {
match message {
HandshakeMessage::Client { base, extensions } => {
codec.session_keys.salt.set_remote_salt(base.random);
Self::process_key_share(&extensions, codec)?;
}
HandshakeMessage::Server { base, extensions } => {
codec.session_keys.salt.set_remote_salt(base.random);
Self::process_key_share(&extensions, codec)?;
}
};
}
Ok(())
}
pub fn unpack_app_data(buffer: &mut BytesMut) -> Result<BytesMut, &str> {
println!("What is here?");
println!("Data {:?}", &buffer);
let incoming_process = ApplicationData::start_process(buffer);
let option_bytes = match incoming_process {
Ok(mes) => mes,
Err(e) => {
println!("Error: {:?}", e);
None
}
};
if let Some(b) = option_bytes {
return Ok(BytesMut::from(b.payload.as_ref()));
} else {
Err("no data")
}
}
pub fn pack_in_app_data(buffer: &mut BytesMut) -> Bytes {
ApplicationData::make_application_data(buffer)
}
}
@@ -1,104 +0,0 @@
use bytes::{Bytes, BytesMut};
use crate::{
protocol::{
codec::bridges::tls::tls_interceptor::TlsInterceptor,
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
parser::parser::FrameParser,
},
tlseng::{
extension::ExtensionStack,
handshake::{
client_hello::ClientHello, hello_header::HelloHeader, server_hello::ServerHello,
},
tls_record::TlsRecord,
types::{ContentType, HelloType},
},
};
pub enum HandshakeMessage {
Client {
base: ClientHello,
extensions: ExtensionStack,
},
Server {
base: ServerHello,
extensions: ExtensionStack,
},
}
impl TlsInterceptor for HandshakeMessage {
type Output = HandshakeMessage;
fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, InterceptorError> {
let mut payload = BytesMut::from(record.payload.as_ref());
match record.content_type {
ContentType::Handshake => Self::handle_handshake(&mut payload),
_ => {
// It is strange if in this prcoess not a Handshake
todo!()
}
}
}
}
impl HandshakeMessage {
fn handle_handshake(payload: &mut BytesMut) -> Result<Option<Self>, InterceptorError> {
if let Some(header) = HelloHeader::parse(payload)? {
match header.header_type {
HelloType::Server => {
let mut server_hello_body = payload;
if let Some(server_hello) = ServerHello::parse(&mut server_hello_body)? {
return Self::process_server_hello(server_hello);
}
}
HelloType::Client => {
let mut client_hello_body = payload;
if let Some(client_hello) = ClientHello::parse(&mut client_hello_body)? {
return Self::process_client_hello(client_hello);
}
}
}
}
Ok(None)
}
fn process_server_hello(hello: ServerHello) -> Result<Option<Self>, InterceptorError> {
println!("Server Hello получен! Random: {:02x?}", hello.random);
// Парсим расширения сервера, если нужно
let mut ext_bytes = BytesMut::from(hello.extensions.as_ref());
let ext_stack_option = ExtensionStack::parse(&mut ext_bytes)?;
let ext_stack = ext_stack_option.ok_or_else(|| {
InterceptorError::new(
ErrorType::Handshake("Extension Err TODO"),
ErrorAction::Drop,
Bytes::copy_from_slice(&[]),
)
})?;
Ok(Some(HandshakeMessage::Server {
base: hello,
extensions: ext_stack,
}))
}
fn process_client_hello(hello: ClientHello) -> Result<Option<Self>, InterceptorError> {
println!("Client Hello получен! Random: {:02x?}", hello.random);
// Парсим расширения клиента, если нужно
let mut ext_bytes = BytesMut::from(hello.extensions.as_ref());
let ext_stack_option = ExtensionStack::parse(&mut ext_bytes)?;
let ext_stack = ext_stack_option.ok_or_else(|| {
InterceptorError::new(
ErrorType::Handshake("Extension Err TODO"),
ErrorAction::Drop,
Bytes::copy_from_slice(&[]),
)
})?;
Ok(Some(HandshakeMessage::Client {
base: hello,
extensions: ext_stack,
}))
}
}
@@ -1,4 +0,0 @@
mod appdata;
pub mod bridge;
mod handshake;
mod tls_interceptor;
@@ -1,25 +0,0 @@
use bytes::{Bytes, BytesMut};
use crate::{
protocol::{
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
parser::parser::FrameParser,
},
tlseng::tls_record::TlsRecord,
};
pub trait TlsInterceptor {
type Output;
fn start_process(buffer: &mut BytesMut) -> Result<Option<Self::Output>, InterceptorError> {
match TlsRecord::parse(buffer) {
Ok(Some(record)) => Self::handle_record(record),
Ok(None) => Err(InterceptorError::new(
ErrorType::Tls("Not full Data"),
ErrorAction::Wait,
Bytes::new(),
)),
Err(e) => Err(e),
}
}
fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, InterceptorError>;
}
@@ -0,0 +1,180 @@
use crate::protocol::errors::{ErrorAction, ErrorStage, TlsError};
use crate::protocol::parser::parser::Parser;
use crate::tlseng::extension::ExtensionStack;
use crate::tlseng::handshake::{ClientHello, HelloHeader, ServerHello};
use crate::tlseng::profile::BrowserProfile;
use crate::tlseng::tls_record::TlsRecord;
use crate::tlseng::types::{ContentType, HelloType, ProtocolVersion};
use crate::tlseng::ApplicationData;
use bytes::{Bytes, BytesMut};
// --- 1. Общий интерфейс перехвата ---
pub trait TlsInterceptor {
type Output;
fn start_process(buffer: &mut BytesMut) -> Result<Option<Self::Output>, TlsError> {
match TlsRecord::parse(buffer) {
Ok(Some(record)) => Self::handle_record(record),
Ok(None) => Ok(None),
Err(e) => Err(e),
}
}
fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, TlsError>;
}
// --- 2. Обработка Handshake ---
pub enum HandshakeMessage {
Client {
base: ClientHello,
extensions: ExtensionStack,
},
Server {
base: ServerHello,
extensions: ExtensionStack,
},
}
impl HandshakeMessage {
pub fn random(&self) -> [u8; 32] {
match self {
Self::Client { base, .. } => base.random,
Self::Server { base, .. } => base.random,
}
}
pub fn extensions(&self) -> &ExtensionStack {
match self {
Self::Client { extensions, .. } => extensions,
Self::Server { extensions, .. } => extensions,
}
}
}
impl TlsInterceptor for HandshakeMessage {
type Output = HandshakeMessage;
fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, TlsError> {
if record.content_type != ContentType::Handshake {
return Err(TlsError::new(
ErrorStage::Handshake("Expected Handshake record"),
ErrorAction::Drop,
record.serialize(),
));
}
let mut payload = BytesMut::from(record.payload.as_ref());
if let Some(header) = HelloHeader::parse(&mut payload)? {
match header.header_type {
HelloType::Client => {
if let Some(hello) = ClientHello::parse(&mut payload)? {
let ext =
ExtensionStack::parse(&mut BytesMut::from(hello.extensions.as_ref()))?
.ok_or_else(|| {
TlsError::new(
ErrorStage::Handshake("Ext Err"),
ErrorAction::Drop,
Bytes::new(),
)
})?;
return Ok(Some(HandshakeMessage::Client {
base: hello,
extensions: ext,
}));
}
}
HelloType::Server => {
if let Some(hello) = ServerHello::parse(&mut payload)? {
let ext =
ExtensionStack::parse(&mut BytesMut::from(hello.extensions.as_ref()))?
.ok_or_else(|| {
TlsError::new(
ErrorStage::Handshake("Ext Err"),
ErrorAction::Drop,
Bytes::new(),
)
})?;
return Ok(Some(HandshakeMessage::Server {
base: hello,
extensions: ext,
}));
}
}
}
}
Ok(None)
}
}
// --- 3. Обработка Application Data ---
impl TlsInterceptor for ApplicationData {
type Output = ApplicationData;
fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, TlsError> {
if record.content_type != ContentType::ApplicationData {
return Err(TlsError::new(
ErrorStage::ApplicationData("Expected AppData record"),
ErrorAction::Drop,
record.serialize(),
));
}
Ok(Some(ApplicationData {
len: record.payload.len(),
payload: record.payload,
}))
}
}
// --- 4. Высокоуровневый Bridge API ---
pub struct TlsBridge;
impl TlsBridge {
// --- Распаковка (уже была) ---
pub fn unpack_handshake(buffer: &mut BytesMut) -> Result<Option<HandshakeMessage>, TlsError> {
HandshakeMessage::start_process(buffer)
}
pub fn unpack_app_data(buffer: &mut BytesMut) -> Result<Option<ApplicationData>, TlsError> {
ApplicationData::start_process(buffer)
}
// --- Запаковка (новое) ---
/// Создает полный TLS Record с ClientHello внутри
pub fn wrap_client_hello(
profile: &BrowserProfile,
host: &str,
public_key: &[u8; 32],
salt: [u8; 32],
) -> Bytes {
ClientHello::make_client_hello(profile, host, public_key, salt) // Передаем ключ дальше
}
/// Создает полный TLS Record с ServerHello, базируясь на данных из HandshakeMessage::Client
pub fn wrap_server_hello(
client_msg: &HandshakeMessage,
server_pub_key: &[u8],
salt: [u8; 32],
) -> Result<Bytes, TlsError> {
if let HandshakeMessage::Client { base, .. } = client_msg {
Ok(ServerHello::make_server_hello(base, server_pub_key, salt))
} else {
Err(TlsError::new(
ErrorStage::Handshake("Wrong message type for ServerHello generation"),
ErrorAction::Drop,
Bytes::new(),
))
}
}
/// Вспомогательный метод для упаковки уже готового Handshake-сообщения в TlsRecord
pub fn pack_handshake(payload: Bytes) -> Bytes {
let record = TlsRecord::new(ContentType::Handshake, ProtocolVersion::Tls12, payload);
record.serialize()
}
pub fn pack_app_data(buffer: Bytes) -> Bytes {
TlsRecord::build_application_data(buffer)
}
}
+213 -25
View File
@@ -1,14 +1,19 @@
use bytes::{Bytes, BytesMut};
use crate::crypto::chacha::ChaChaCipher;
use crate::protocol::codec::bridges::tls::bridge::TlsBridge;
use crate::protocol::codec::session_keys::SessionKeys;
use crate::crypto::aead::AeadPacker;
use crate::crypto::chacha::ChaChaCipher;
use crate::crypto::session::SessionKeys;
use crate::protocol::codec::bridges::tls_bridge::TlsBridge;
use crate::protocol::codec::frame::{Frame, FrameHeader, FrameType};
use crate::protocol::codec::padding::Padding;
use crate::protocol::errors::{ErrorAction, ErrorStage, TlsError};
use crate::protocol::parser::parser::Parser;
use crate::tlseng::profile::BrowserProfile;
pub struct Codec {
crypto: ChaChaCipher, //rename chacha
crypto: ChaChaCipher,
pub session_keys: SessionKeys,
staging: BytesMut,
}
impl Codec {
@@ -16,34 +21,217 @@ impl Codec {
Self {
crypto: ChaChaCipher::new(),
session_keys: SessionKeys::new(is_initiator),
staging: BytesMut::new(),
}
}
//maybe generator?
//should anwer socks5 and open connection to proxy server
/// Логика для Клиента: Генерирует байты ClientHello для инициализации соединения.
/// Клиент: генерирует TLS Record [ ClientHello ]
pub fn make_client_handshake(
&mut self,
profile: &BrowserProfile,
host: &str,
) -> Result<Bytes, TlsError> {
// 1. Извлекаем публичный ключ нашей текущей сессии
let my_pub_key = self.session_keys.ecdh.public_key.to_bytes();
// (Убедись, что метод возвращает [u8; 32])
// 2. Передаем его в мост
Ok(TlsBridge::wrap_client_hello(
profile,
host,
&my_pub_key,
self.session_keys.salt.get_local(),
))
}
/// Сервер: берет буфер, достает ClientHello и генерирует в ответ TLS Record [ ServerHello ]
pub fn make_server_handshake(&mut self, buffer: &mut BytesMut) -> Result<Bytes, TlsError> {
// 1. Распаковываем сообщение клиента
let client_msg = TlsBridge::unpack_handshake(buffer)?.ok_or_else(|| {
TlsError::new(
ErrorStage::Handshake("No CH"),
ErrorAction::Wait,
Bytes::new(),
)
})?;
// 2. Генерируем ответный ServerHello рекорд
let server_pub_key = self.session_keys.ecdh.public_key.to_bytes();
let server_hello_record = TlsBridge::wrap_server_hello(
&client_msg,
&server_pub_key,
self.session_keys.salt.get_local(),
)?;
// 3. ОБНОВЛЕНИЕ КЛЮЧЕЙ НА СЕРВЕРЕ
// Передаем true, так как сервер ПАРСИТ ClientHello (смещение 6 байт)
let (w_key, w_iv, r_key, r_iv) = self
.session_keys
.update_keys(client_msg.random(), client_msg.extensions(), true)
.map_err(|e| {
tracing::error!(error = %e, "Server failed to update keys from ClientHello");
TlsError::new(
ErrorStage::Handshake("Key Err"),
ErrorAction::Drop,
Bytes::new(),
)
})?;
// Инициализируем шифратор сервера
self.crypto.set_keys(w_key, w_iv, r_key, r_iv);
Ok(server_hello_record)
}
pub fn process_handshake(&mut self, buffer: &mut BytesMut) -> Result<(), TlsError> {
let mes_opt = TlsBridge::unpack_handshake(buffer)?;
let mes = mes_opt.ok_or_else(|| {
TlsError::new(
ErrorStage::Handshake("Incomplete record"),
ErrorAction::Wait,
Bytes::new(),
)
})?;
// ОБНОВЛЕНИЕ КЛЮЧЕЙ НА КЛИЕНТЕ
// Передаем false, так как клиент ПАРСИТ ServerHello (смещение 4 байта)
let (w_key, w_iv, r_key, r_iv) = self
.session_keys
.update_keys(mes.random(), mes.extensions(), false)
.map_err(|e| {
tracing::error!(error = %e, "Client failed to update keys from ServerHello");
TlsError::new(
ErrorStage::Handshake("Keys update error"),
ErrorAction::Drop,
Bytes::new(),
)
})?;
self.crypto.set_keys(w_key, w_iv, r_key, r_iv);
Ok(())
}
pub async fn try_handshake(&mut self, buffer: &mut BytesMut) -> Result<bool, TlsError> {
match self.process_handshake(buffer) {
Ok(_) => Ok(true),
Err(e) if e.action == ErrorAction::Wait => Ok(false),
Err(e) => Err(e),
}
}
pub fn make_handshake(&mut self, buffer: &mut BytesMut) {
println!("Handshake len in codec: {:?}", &buffer.len());
TlsBridge::make_handshake(buffer, self);
fn outbound(
&mut self,
stream_id: u32,
frame_type: FrameType,
payload: Bytes,
) -> Result<Bytes, TlsError> {
let padding = Padding::generate_padding();
let header = FrameHeader {
auth_tag: [0u8; 16],
stream_id,
frame_type,
payload_len: payload.len() as u16,
padding_len: padding.len as u16,
};
let frame = Frame {
header,
payload,
padding: padding.data,
};
let mut frame_bytes = frame.into_bytes();
// ВАЖНО: вызываем шифрование ОДИН РАЗ.
// Метод encrypt возвращает Result<Bytes, chacha20poly1305::Error>
// Мы вручную превращаем его ошибку в твой TlsError.
let encrypted_payload = self.crypto.encrypt(&mut frame_bytes).map_err(|e| {
tracing::error!("Encryption failed: {:?}", e);
TlsError::new(
ErrorStage::Tls("Encryption failed"),
ErrorAction::Drop,
Bytes::new(),
)
})?;
// Теперь передаем зашифрованные байты в новый метод упаковки
Ok(TlsBridge::pack_app_data(encrypted_payload))
}
pub fn unpack(&mut self, buffer: &mut BytesMut) -> Result<Bytes, String> {
println!("App data unpack len in codec?: {:?}", &buffer.len());
let mut data = TlsBridge::unpack_app_data(buffer);
//self.decrypt(&mut data);
match data {
Ok(bytes) => Ok(bytes.freeze()),
Err(e) => Err(e.to_string()),
pub fn encrypt_data(
&mut self,
stream_id: u32,
frame_type: FrameType,
data: Bytes,
) -> Result<Bytes, TlsError> {
self.outbound(stream_id, frame_type, data)
}
pub fn inbound(&mut self, buffer: &mut BytesMut) -> Result<Option<Frame>, TlsError> {
// Логгируем входящее состояние сетевого буфера (TLS слой)
if !buffer.is_empty() {
let header = &buffer[..std::cmp::min(buffer.len(), 5)];
tracing::debug!(
buf_len = buffer.len(),
header_hex = %hex::encode(header),
"RAW TLS buffer state"
);
}
}
pub fn pack(&mut self, buffer: &mut BytesMut) -> Bytes {
println!("App data len in codec?: {:?}", &buffer.len());
TlsBridge::pack_in_app_data(buffer)
}
// --- ШАГ 1: Извлекаем ВСЕ доступные TLS-рекорды и расшифровываем в staging ---
// Мы крутим цикл, пока TlsBridge может "откусить" целый TLS-рекорд из buffer
while let Some(app_data) = TlsBridge::unpack_app_data(buffer)? {
let mut encrypted_chunk = BytesMut::from(app_data.payload.as_ref());
let raw_len = encrypted_chunk.len();
pub fn encrypt(&mut self, data: &mut BytesMut) {
self.crypto.encrypt(data);
}
// Расшифровываем кусок
let decrypted_chunk = self.crypto.decrypt(&mut encrypted_chunk).map_err(|e| {
tracing::error!(len = raw_len, "Decryption failed: {:?}", e);
TlsError::new(
ErrorStage::Tls("Decryption error"),
ErrorAction::Drop,
Bytes::new(),
)
})?;
pub fn decrypt(&mut self, data: &mut BytesMut) {
self.crypto.decrypt(data);
// КЛАДЕМ В ЧИСТУЮ ЗОНУ: расшифрованный поток байтов нашего протокола
self.staging.extend_from_slice(&decrypted_chunk);
tracing::debug!(
added = decrypted_chunk.len(),
total_staging = self.staging.len(),
"Decrypted data moved to staging"
);
}
// --- ШАГ 2: Парсим Frame из "чистых" данных в staging ---
if !self.staging.is_empty() {
// Важно: Frame::parse должен вызывать advance() или split_to() у staging
match Frame::parse(&mut self.staging) {
Ok(Some(frame)) => {
tracing::info!(
stream_id = frame.header.stream_id,
"Frame successfully parsed from staging"
);
return Ok(Some(frame));
}
Ok(None) => {
tracing::debug!("Frame is incomplete in staging, waiting for more TLS records");
return Ok(None);
}
Err(e) => {
tracing::error!("Frame parse error: {:?}", e);
return Err(TlsError::new(
ErrorStage::Tls("Frame parse error"),
ErrorAction::Drop,
Bytes::new(),
));
}
}
}
Ok(None)
}
}
+7 -64
View File
@@ -1,9 +1,9 @@
use bytes::{Buf, BufMut, Bytes, BytesMut};
use bytes::{BufMut, Bytes, BytesMut};
use crate::{crypto::hmac::generate_auth_tag, protocol::codec::padding::Padding};
use crate::protocol::codec::padding::Padding;
#[derive(Copy, Clone)]
enum FrameType {
#[derive(Copy, Clone, Debug)]
pub enum FrameType {
Connect = 0x00,
Data = 0x01,
Close = 0x02,
@@ -11,7 +11,7 @@ enum FrameType {
}
#[derive(Copy, Clone)]
struct FrameHeader {
pub struct FrameHeader {
pub auth_tag: [u8; 16],
pub stream_id: u32,
pub frame_type: FrameType,
@@ -20,7 +20,7 @@ struct FrameHeader {
}
pub struct Frame {
header: FrameHeader,
pub header: FrameHeader,
pub payload: Bytes,
pub padding: Bytes,
}
@@ -34,69 +34,12 @@ const PADDING_LEN_SIZE: u16 = 2;
pub const FRAME_HEADER_SIZE: u16 =
AUTH_TAG_SIZE + STREAM_ID_SIZE + FRAME_TYPE_SIZE + PAYLOAD_LEN_SIZE + PADDING_LEN_SIZE;
impl FrameHeader {
fn get_header(bytes: &mut BytesMut) -> Option<Self> {
if bytes.len() < FRAME_HEADER_SIZE as usize {
return None;
}
let mut header_chunk = bytes.split_to(FRAME_HEADER_SIZE as usize);
let mut auth_tag = [0u8; 16];
header_chunk.copy_to_slice(&mut auth_tag);
let stream_id = header_chunk.get_u32();
let frame_type_byte = header_chunk.get_u8();
let frame_type = match frame_type_byte {
0x00 => FrameType::Connect,
0x01 => FrameType::Data,
0x02 => FrameType::Close,
0x03 => FrameType::Heartbeat,
_ => FrameType::Close,
};
let payload_len = header_chunk.get_u16();
let padding_len = header_chunk.get_u16();
Some(Self {
auth_tag,
stream_id,
frame_type,
payload_len,
padding_len,
})
}
}
impl Frame {
pub fn unpack(bytes: &mut BytesMut) -> Result<Option<Self>, String> {
if bytes.len() < FRAME_HEADER_SIZE as usize {
return Ok(None);
}
let p_len = u16::from_be_bytes([bytes[21], bytes[22]]) as usize;
let pad_len = u16::from_be_bytes([bytes[23], bytes[24]]) as usize;
if bytes.len() < (FRAME_HEADER_SIZE as usize + p_len + pad_len) {
return Ok(None);
}
let header = FrameHeader::get_header(bytes).unwrap();
let payload = bytes.split_to(p_len).freeze();
let padding = bytes.split_to(pad_len).freeze();
Ok(Some(Self {
header,
payload,
padding,
}))
}
pub fn into_bytes(self) -> BytesMut {
let updated_padding = Padding::generate_padding();
let total_size = FRAME_HEADER_SIZE as usize + self.payload.len() + self.padding.len();
let mut buf = BytesMut::with_capacity(total_size);
let hmac = generate_auth_tag(&[0; 16]);
let hmac = [0; 16]; //generate_auth_tag(&[0; 16]);
buf.put_slice(&hmac);
buf.put_u32(self.header.stream_id);
+1 -1
View File
@@ -2,4 +2,4 @@ mod bridges;
pub mod codec;
pub mod frame;
mod padding;
mod session_keys;
pub mod socks;
-49
View File
@@ -1,49 +0,0 @@
use x25519_dalek::PublicKey;
use crate::crypto::{ecdh::ECDH, hkdf::HKDF, salt_pair::SaltPair};
pub struct KeyPair {
pub aead_key: [u8; 32],
pub hmac_key: [u8; 32],
}
pub struct SessionKeys {
pub salt: SaltPair,
pub ecdh: ECDH,
key_pair: KeyPair,
}
impl SessionKeys {
pub fn new(is_initiator: bool) -> Self {
Self {
salt: SaltPair::new(is_initiator),
ecdh: ECDH::new(),
key_pair: KeyPair {
aead_key: [0; 32],
hmac_key: [0; 32],
},
}
}
pub fn generate_keys(&mut self, public_key: &PublicKey) -> Result<(), ()> {
let shared_key = self.ecdh.get_shared(public_key);
match shared_key {
Some(key) => {
let hkdf = HKDF::extract_key(&self.salt.get_total(), &key);
let aead = HKDF::expand_key::<32>(&hkdf, b"aead").map_err(|e| {
println!("Aead Key expand Error: {}", e);
})?;
self.key_pair.aead_key = aead;
let hmac: [u8; 32] = HKDF::expand_key::<32>(&hkdf, b"hmac").map_err(|e| {
println!("HMAC Key expand Error: {}", e);
})?;
self.key_pair.hmac_key = hmac;
Ok(())
}
None => {
println!("Error while generate keys. No Shared key");
Err(())
}
}
}
}
+68
View File
@@ -0,0 +1,68 @@
use bytes::{BufMut, Bytes, BytesMut};
pub const SOCKS5_VERSION: u8 = 0x05;
pub const REPLY_SUCCESS: u8 = 0x00;
pub const REPLY_AUTH_FAILURE: u8 = 0xFF;
pub const SOCKS5_MIN_HEADER: usize = 4;
pub const ATYP_IPV4: u8 = 0x01;
pub const ATYP_DOMAIN: u8 = 0x03;
pub const ATYP_IPV6: u8 = 0x04;
pub const IPV4_SIZE: usize = 4;
pub const IPV6_SIZE: usize = 16;
pub const PORT_SIZE: usize = 2;
#[derive(Debug)]
pub enum SocksRequest {
Handshake { methods: Vec<u8> },
Connect { command: u8, target: SocksTarget },
Unknown,
}
#[derive(Debug)]
pub enum SocksReply {
HandshakeSelect {
method: u8,
},
ConnectResult {
reply_code: u8,
atyp: u8,
addr: [u8; 4],
port: u16,
},
}
#[derive(Debug)]
pub struct SocksTarget {
pub host: Bytes,
pub port: u16,
}
impl SocksReply {
pub fn write_to(self, buf: &mut BytesMut) {
match self {
SocksReply::HandshakeSelect { method } => {
buf.put_u8(SOCKS5_VERSION);
buf.put_u8(method);
}
SocksReply::ConnectResult {
reply_code,
atyp,
addr,
port,
} => {
buf.put_u8(SOCKS5_VERSION);
buf.put_u8(reply_code);
buf.put_u8(0x00); // Reserved
buf.put_u8(atyp);
buf.put_slice(&addr);
buf.put_u16(port);
}
}
}
}
impl SocksTarget {
pub fn to_string(&self) -> String {
let host_str = String::from_utf8_lossy(&self.host);
format!("{}:{}", host_str, self.port)
}
}
+94
View File
@@ -0,0 +1,94 @@
use bytes::Bytes;
use tracing::{error, trace, warn};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorAction {
Wait,
Redirect,
Drop,
}
#[derive(Debug)]
pub enum ErrorStage {
Tls(&'static str),
Handshake(&'static str),
ApplicationData(&'static str),
}
#[derive(Debug)]
pub struct TlsError {
pub stage: ErrorStage,
pub action: ErrorAction,
pub data: Bytes,
}
impl TlsError {
pub fn new(stage: ErrorStage, action: ErrorAction, data: Bytes) -> Self {
Self {
stage,
action,
data,
}
}
fn log_error(&self) {
// Определяем уровень логирования в зависимости от действия
// Если мы просто ждем данные (Wait) — это не ошибка, а рабочий процесс (debug/trace)
// Если дропаем соединение (Drop) — это серьезно (error)
let stage_name = match &self.stage {
ErrorStage::Tls(_) => "TLS",
ErrorStage::Handshake(_) => "Handshake",
ErrorStage::ApplicationData(_) => "AppData",
};
let message = match &self.stage {
ErrorStage::Tls(m) | ErrorStage::Handshake(m) | ErrorStage::ApplicationData(m) => m,
};
// Подготавливаем превью данных (первые 8 байт в хексе)
let data_preview = if !self.data.is_empty() {
let limit = self.data.len().min(8);
format!(
"Hex: {:02x?}{}",
&self.data[..limit],
if self.data.len() > 8 { "..." } else { "" }
)
} else {
"No data".to_string()
};
match self.action {
ErrorAction::Wait => {
// Wait — это нормальное состояние асинхронного чтения
trace!(
stage = stage_name,
action = ?self.action,
data = %data_preview,
"{}", message
);
}
ErrorAction::Redirect => {
warn!(
stage = stage_name,
action = ?self.action,
data = %data_preview,
"⚠️ {}", message
);
}
ErrorAction::Drop => {
error!(
stage = stage_name,
action = ?self.action,
data = %data_preview,
"🚨 {}", message
);
}
}
}
pub fn execute_strategy(&self) -> ErrorAction {
self.log_error();
self.action
}
}
@@ -1,47 +0,0 @@
use bytes::Bytes;
#[derive(Debug, Clone, Copy)]
pub enum ErrorAction {
Wait,
Redirect,
Drop,
}
#[derive(Debug)]
pub enum ErrorType {
Tls(&'static str),
Handshake(&'static str),
ApplicationData(&'static str),
}
#[derive(Debug)]
pub struct InterceptorError {
pub error_type: ErrorType,
pub action: ErrorAction,
pub data: Bytes,
}
impl InterceptorError {
pub fn new(error_type: ErrorType, action: ErrorAction, data: Bytes) -> Self {
Self {
error_type,
action,
data,
}
}
fn log_error(&self) {
let (category, message) = match &self.error_type {
ErrorType::Tls(m) => ("TLS", m),
ErrorType::Handshake(m) => ("Handshake", m),
ErrorType::ApplicationData(m) => ("AppData", m),
};
println!(
"[{}] Error: {} (Byte: {:#02x})",
category, message, self.data
);
}
pub fn execute_strategy(&self) -> ErrorAction {
self.log_error();
self.action
}
}
-1
View File
@@ -1 +0,0 @@
pub mod error_interceptor;
-25
View File
@@ -1,25 +0,0 @@
use bytes::Buf;
pub trait PeekExt: Buf {
fn peek_u16(&self, offset: usize) -> Option<u16> {
let chunk = self.chunk();
if chunk.len() >= offset + 2 {
let b = &chunk[offset..offset + 2];
Some(u16::from_be_bytes([b[0], b[1]]))
} else {
None
}
}
fn peek_u24(&self, offset: usize) -> Option<u32> {
let chunk = self.chunk();
if chunk.len() >= offset + 3 {
let b = &chunk[offset..offset + 3];
Some(((b[0] as u32) << 16) | ((b[1] as u32) << 8) | (b[2] as u32))
} else {
None
}
}
}
impl<T: Buf> PeekExt for T {}
+2 -2
View File
@@ -1,3 +1,3 @@
pub mod codec;
pub mod interceptors;
mod parser;
pub mod errors;
pub mod parser;
+4
View File
@@ -1,2 +1,6 @@
mod netr;
pub mod parser;
mod socks;
mod tls;
use parser::Parser;
+100
View File
@@ -0,0 +1,100 @@
use bytes::{Buf, BytesMut};
use crate::protocol::{
codec::frame::{Frame, FrameHeader, FrameType, FRAME_HEADER_SIZE},
parser::parser::Parser,
};
impl Parser for FrameHeader {
type Error = String;
fn can_parse(bytes: &BytesMut) -> bool {
bytes.len() >= FRAME_HEADER_SIZE as usize
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if !Self::can_parse(bytes) {
return Ok(None);
}
let mut header_chunk = bytes.split_to(FRAME_HEADER_SIZE as usize);
let mut auth_tag = [0u8; 16];
header_chunk.copy_to_slice(&mut auth_tag);
let stream_id = header_chunk.get_u32();
let frame_type_byte = header_chunk.get_u8();
let frame_type = match frame_type_byte {
0x00 => FrameType::Connect,
0x01 => FrameType::Data,
0x02 => FrameType::Close,
0x03 => FrameType::Heartbeat,
_ => FrameType::Close,
};
let payload_len = header_chunk.get_u16();
let padding_len = header_chunk.get_u16();
Ok(Some(Self {
auth_tag,
stream_id,
frame_type,
payload_len,
padding_len,
}))
}
}
impl Parser for Frame {
type Error = String;
fn can_parse(bytes: &BytesMut) -> bool {
// 1. Сначала проверяем, есть ли хотя бы заголовок
if bytes.len() < FRAME_HEADER_SIZE as usize {
return false;
}
// 2. Извлекаем длины из заголовка (БЕЗ удаления байтов из буфера)
// По твоей структуре: Auth(16) + Stream(4) + Type(1) = 21 байт смещения
let p_len = u16::from_be_bytes([bytes[21], bytes[22]]) as usize;
let pad_len = u16::from_be_bytes([bytes[23], bytes[24]]) as usize;
tracing::debug!(
"CAN_PARSE: p_len={}, pad_len={}, total_needed={}, have={}",
p_len,
pad_len,
25 + p_len + pad_len,
bytes.len()
);
// 3. Проверяем, есть ли в буфере весь фрейм целиком
bytes.len() >= (FRAME_HEADER_SIZE as usize + p_len + pad_len)
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if !Self::can_parse(bytes) {
return Ok(None);
}
// Извлекаем заголовок (теперь split_to удалит эти байты из начала bytes)
let header = FrameHeader::parse(bytes)?.ok_or("Failed to parse header")?;
let p_len = header.payload_len as usize;
let pad_len = header.padding_len as usize;
// Теперь байты заголовка уже удалены, и в начале 'bytes' лежит Payload
if bytes.len() < p_len + pad_len {
return Err("Buffer corrupted: length mismatch after header parse".into());
}
let payload = bytes.split_to(p_len).freeze();
let padding = bytes.split_to(pad_len).freeze();
Ok(Some(Self {
header,
payload,
padding,
}))
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
use bytes::BytesMut;
pub trait FrameParser {
pub trait Parser {
type Error;
fn can_parse(bytes: &BytesMut) -> bool;
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error>
+111
View File
@@ -0,0 +1,111 @@
use bytes::{Buf, BytesMut};
use crate::protocol::{codec::socks::*, parser::parser::Parser};
impl Parser for SocksTarget {
type Error = String;
fn can_parse(bytes: &BytesMut) -> bool {
if bytes.len() < SOCKS5_MIN_HEADER {
return false;
}
let atyp = bytes[3];
match atyp {
ATYP_IPV4 => bytes.len() >= SOCKS5_MIN_HEADER + IPV4_SIZE + PORT_SIZE,
ATYP_DOMAIN => {
if bytes.len() < SOCKS5_MIN_HEADER + 1 {
return false;
}
let domain_len = bytes[4] as usize;
bytes.len() >= SOCKS5_MIN_HEADER + 1 + domain_len + PORT_SIZE
}
ATYP_IPV6 => bytes.len() >= SOCKS5_MIN_HEADER + IPV6_SIZE + PORT_SIZE,
_ => false,
}
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if !Self::can_parse(bytes) {
return Ok(None);
}
let atyp = bytes[3];
// Вычисляем длину еще раз для split_to (либо можно вынести в хелпер)
let total_len = match atyp {
ATYP_IPV4 => SOCKS5_MIN_HEADER + IPV4_SIZE + PORT_SIZE,
ATYP_DOMAIN => SOCKS5_MIN_HEADER + 1 + (bytes[4] as usize) + PORT_SIZE,
ATYP_IPV6 => SOCKS5_MIN_HEADER + IPV6_SIZE + PORT_SIZE,
_ => return Err("Unsupported address type".to_string()),
};
let mut packet = bytes.split_to(total_len);
packet.advance(SOCKS5_MIN_HEADER);
let host = if atyp == ATYP_DOMAIN {
let len = packet.get_u8() as usize;
packet.split_to(len).freeze()
} else if atyp == ATYP_IPV4 {
packet.split_to(IPV4_SIZE).freeze()
} else {
packet.split_to(IPV6_SIZE).freeze()
};
let port = packet.get_u16();
Ok(Some(SocksTarget { host, port }))
}
}
impl Parser for SocksRequest {
type Error = String;
fn can_parse(bytes: &BytesMut) -> bool {
if bytes.len() < 2 || bytes[0] != SOCKS5_VERSION {
return false;
}
let nmethods = bytes[1] as usize;
if bytes.len() >= 2 + nmethods {
// Это может быть Handshake. Проверяем, не Connect ли это (мин. 6-10 байт)
if bytes.len() >= SOCKS5_MIN_HEADER && SocksTarget::can_parse(bytes) {
return true;
}
// Если для Connect данных мало или структура не совпадает,
// но для Handshake достаточно — ок.
return true;
}
false
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if bytes.len() < 2 || bytes[0] != SOCKS5_VERSION {
return Ok(None);
}
// 1. Пытаемся распарсить как Connect (у него строгая структура)
if bytes.len() >= SOCKS5_MIN_HEADER && SocksTarget::can_parse(bytes) {
let command = bytes[1];
if let Some(target) = SocksTarget::parse(bytes)? {
return Ok(Some(SocksRequest::Connect { command, target }));
}
}
// 2. Если не Connect, пробуем Handshake
let nmethods = bytes[1] as usize;
let total_handshake = 2 + nmethods;
if bytes.len() >= total_handshake {
let mut packet = bytes.split_to(total_handshake);
packet.advance(2);
let mut methods = vec![0u8; nmethods];
packet.copy_to_slice(&mut methods);
return Ok(Some(SocksRequest::Handshake { methods }));
}
Ok(None)
}
}
+368
View File
@@ -0,0 +1,368 @@
use crate::{
protocol::{
errors::{ErrorAction, ErrorStage, TlsError},
parser::Parser,
},
tlseng::{
extension::{Extension, ExtensionStack},
handshake::*,
tls_record::TlsRecord,
types::{ContentType, HelloType, ProtocolVersion},
ApplicationData,
},
utils::u24::{BufExt, U24},
};
use bytes::{Buf, Bytes, BytesMut};
// =================================================================
// 1. RECORD LAYER
// =================================================================
impl Parser for TlsRecord {
type Error = TlsError;
fn can_parse(bytes: &BytesMut) -> bool {
// 1. Минимум 5 байт для заголовка
if bytes.len() < 5 {
return false;
}
// 2. Проверяем ContentType
let content_type = bytes[0];
let is_valid_type = content_type == ContentType::Handshake as u8
|| content_type == ContentType::ApplicationData as u8
|| content_type == ContentType::Alert as u8;
if !is_valid_type {
return false;
}
// 3. Извлекаем заявленную длину тела рекорда
let record_len = u16::from_be_bytes([bytes[3], bytes[4]]) as usize;
// 4. Специфика TLS 1.3:
// Если это зашифрованные данные (0x17), они ДОЛЖНЫ содержать тег (16 байт)
// + как минимум 1 байт зашифрованного типа контента.
if content_type == ContentType::ApplicationData as u8 {
if record_len < 17 {
// Если длина < 17, это либо неполный пакет, либо ошибка протокола.
// Возвращаем false, чтобы подождать еще данных из сокета.
return false;
}
}
// 5. Ждем, пока в буфере будет заголовок + всё тело
bytes.len() >= 5 + record_len
}
fn parse(bytes: &mut BytesMut) -> Result<Option<TlsRecord>, Self::Error> {
if !Self::can_parse(bytes) {
return Ok(None);
}
// --- ТОЛЬКО ТЕПЕРЬ МЫ ИЗМЕНЯЕМ БУФЕР ---
let raw_content_type = bytes.get_u8();
let raw_version = bytes.get_u16();
let record_len = bytes.get_u16() as usize;
let content_type = ContentType::try_from(raw_content_type)
.map_err(|e| TlsError::new(ErrorStage::Tls(e), ErrorAction::Drop, Bytes::new()))?;
let version = ProtocolVersion::try_from(raw_version)
.map_err(|e| TlsError::new(ErrorStage::Tls(e), ErrorAction::Drop, Bytes::new()))?;
// Забираем ровно столько, сколько указано в заголовке
let payload = bytes.split_to(record_len).freeze();
Ok(Some(TlsRecord::new(content_type, version, payload)))
}
}
// =================================================================
// 2. PAYLOAD TYPES
// =================================================================
impl Parser for ApplicationData {
type Error = TlsError;
fn can_parse(bytes: &BytesMut) -> bool {
!bytes.is_empty()
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
let len = bytes.len();
if len == 0 {
return Ok(None);
}
let payload = bytes.split_to(len).freeze();
Ok(Some(Self { len, payload }))
}
}
impl Parser for HelloHeader {
type Error = TlsError;
fn can_parse(bytes: &BytesMut) -> bool {
if bytes.len() < 4 {
return false;
}
bytes[0] == HelloType::Client as u8 || bytes[0] == HelloType::Server as u8
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if !Self::can_parse(bytes) {
return Ok(None);
}
let raw_type = bytes.get_u8();
let header_type = HelloType::try_from(raw_type).map_err(|e| {
TlsError::new(ErrorStage::Handshake(e), ErrorAction::Drop, Bytes::new())
})?;
let len = bytes.get_u24();
Ok(Some(Self {
header_type,
len: U24::from_u32(len),
}))
}
}
// =================================================================
// 3. HELLO MESSAGES
// =================================================================
impl Parser for ClientHello {
type Error = TlsError;
fn can_parse(bytes: &BytesMut) -> bool {
let mut offset = 34; // ProtocolVersion (2) + Random (32)
if bytes.len() < offset + 1 {
return false;
}
let session_id_len = bytes[offset] as usize;
offset += 1 + session_id_len;
if bytes.len() < offset + 2 {
return false;
}
let ciphers_len = u16::from_be_bytes([bytes[offset], bytes[offset + 1]]) as usize;
offset += 2 + ciphers_len;
if bytes.len() < offset + 1 {
return false;
}
let comp_len = bytes[offset] as usize;
offset += 1 + comp_len;
if bytes.len() >= offset + 2 {
let ext_len = u16::from_be_bytes([bytes[offset], bytes[offset + 1]]) as usize;
offset += 2 + ext_len;
}
bytes.len() >= offset
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
// --- ШАГ 1: Атомарная проверка всего пакета ---
let mut offset = 34; // Version + Random
if bytes.len() < offset + 1 {
return Ok(None);
}
let session_id_len = bytes[offset] as usize;
offset += 1 + session_id_len;
if bytes.len() < offset + 2 {
return Ok(None);
}
let ciphers_len = u16::from_be_bytes([bytes[offset], bytes[offset + 1]]) as usize;
offset += 2 + ciphers_len;
if bytes.len() < offset + 1 {
return Ok(None);
}
let comp_len = bytes[offset] as usize;
offset += 1 + comp_len;
if bytes.len() >= offset + 2 {
let ext_len = u16::from_be_bytes([bytes[offset], bytes[offset + 1]]) as usize;
offset += 2 + ext_len;
}
// Если нам не хватает данных для полного ClientHello, выходим, не трогая буфер
if bytes.len() < offset {
return Ok(None);
}
// --- ШАГ 2: Безопасное чтение ---
// Изолируем ровно тот кусок, который проверили.
let mut msg = bytes.split_to(offset);
let version = ProtocolVersion::try_from(msg.get_u16())
.map_err(|e| TlsError::new(ErrorStage::Tls(e), ErrorAction::Drop, Bytes::new()))?;
let mut random = [0u8; 32];
msg.copy_to_slice(&mut random);
let sid_len = msg.get_u8() as usize;
let session_id = msg.split_to(sid_len).freeze();
let c_len = msg.get_u16() as usize;
let mut cipher_suites = Vec::with_capacity(c_len / 2);
let mut ciphers_data = msg.split_to(c_len);
while ciphers_data.has_remaining() {
cipher_suites.push(ciphers_data.get_u16());
}
let cmp_len = msg.get_u8() as usize;
msg.advance(cmp_len); // пропускаем методы сжатия
let extensions = if msg.remaining() >= 2 {
let ext_len = msg.get_u16() as usize;
msg.split_to(ext_len).freeze()
} else {
Bytes::new()
};
Ok(Some(Self {
version,
random,
session_id,
cipher_suites,
extensions,
}))
}
}
impl Parser for ServerHello {
type Error = TlsError;
fn can_parse(bytes: &BytesMut) -> bool {
let mut offset = 34; // ProtocolVersion (2) + Random (32)
if bytes.len() < offset + 1 {
return false;
}
let session_id_len = bytes[offset] as usize;
offset += 1 + session_id_len;
// Cipher suite (2) + Compression (1)
offset += 3;
if bytes.len() >= offset + 2 {
let ext_len = u16::from_be_bytes([bytes[offset], bytes[offset + 1]]) as usize;
offset += 2 + ext_len;
}
bytes.len() >= offset
}
fn parse(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error> {
// --- ШАГ 1: Атомарная проверка всего пакета ---
let mut offset = 34; // Version + Random
if bytes.len() < offset + 1 {
return Ok(None);
}
let session_id_len = bytes[offset] as usize;
offset += 1 + session_id_len;
offset += 3; // Cipher Suite (2) + Compression (1)
if bytes.len() >= offset + 2 {
let ext_len = u16::from_be_bytes([bytes[offset], bytes[offset + 1]]) as usize;
offset += 2 + ext_len;
}
if bytes.len() < offset {
return Ok(None);
}
// --- ШАГ 2: Безопасное чтение ---
let mut msg = bytes.split_to(offset);
let version = ProtocolVersion::try_from(msg.get_u16())
.map_err(|e| TlsError::new(ErrorStage::Tls(e), ErrorAction::Drop, Bytes::new()))?;
let mut random = [0u8; 32];
msg.copy_to_slice(&mut random);
let sid_len = msg.get_u8() as usize;
let session_id = msg.split_to(sid_len).freeze();
let cipher_suite = msg.get_u16();
msg.advance(1); // compression
let extensions = if msg.remaining() >= 2 {
let ext_len = msg.get_u16() as usize;
msg.split_to(ext_len)
} else {
BytesMut::new()
};
Ok(Some(Self {
version,
random,
session_id,
cipher_suite,
extensions,
}))
}
}
// =================================================================
// 4. EXTENSIONS
// =================================================================
impl Parser for ExtensionStack {
type Error = TlsError;
fn can_parse(bytes: &BytesMut) -> bool {
let mut offset = 0;
let data_len = bytes.len();
while offset + 4 <= data_len {
let elen = u16::from_be_bytes([bytes[offset + 2], bytes[offset + 3]]) as usize;
offset += 4 + elen;
}
offset <= data_len
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
// Проверяем на целостность всех расширений
let mut offset = 0;
let data_len = bytes.len();
while offset + 4 <= data_len {
let elen = u16::from_be_bytes([bytes[offset + 2], bytes[offset + 3]]) as usize;
offset += 4 + elen;
}
// Если offset > data_len, значит кусок данных расширения отсечен, ждем еще данных
if offset > data_len {
return Ok(None);
}
// Если offset < data_len, есть лишние байты (Trailing garbage). Но так как мы
// обычно передаем сюда точный срез `extensions`, это может быть ошибкой формата.
if offset != data_len {
return Err(TlsError::new(
ErrorStage::Tls("Malformed extension stack: trailing data"),
ErrorAction::Drop,
Bytes::new(),
));
}
// Теперь гарантированно безопасно парсить всё до конца
let mut extensions = Vec::new();
while bytes.remaining() >= 4 {
let etype = bytes.get_u16();
let elen = bytes.get_u16() as usize;
let data = bytes.split_to(elen).freeze();
extensions.push(Extension::new(etype, data));
}
Ok(Some(Self { extensions }))
}
}
@@ -1,28 +0,0 @@
use crate::{
protocol::{interceptors::error_interceptor::InterceptorError, parser::parser::FrameParser},
tlseng::application_data::ApplicationData,
};
impl FrameParser for ApplicationData {
type Error = InterceptorError;
fn can_parse(bytes: &bytes::BytesMut) -> bool {
// Application Data не может быть пустым по спецификации (хотя бы 1 байт)
!bytes.is_empty()
}
fn parse(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
where
Self: Sized,
{
let len = bytes.len();
if len == 0 {
return Ok(None);
}
let payload = bytes.split_to(len).freeze();
Ok(Some(Self { len, payload }))
}
}
@@ -1,103 +0,0 @@
use bytes::{Buf, Bytes};
use crate::{
protocol::{
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
parser::parser::FrameParser,
},
tlseng::{handshake::client_hello::ClientHello, types::ProtocolVersion},
};
impl FrameParser for ClientHello {
type Error = InterceptorError;
fn can_parse(bytes: &bytes::BytesMut) -> bool {
// Мы предполагаем, что HelloHeader уже проверил тип.
// Здесь можно проверить минимально допустимый размер ClientHello
// (Version 2 + Random 32 + SessionID_len 1 = 35 байт)
bytes.len() >= 35
}
fn parse(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
where
Self: Sized,
{
if bytes.len() < 35 {
return Ok(None);
}
// 2. Version (2 bytes)
let raw_version = bytes.get_u16();
let version = ProtocolVersion::try_from(raw_version).map_err(|e| {
InterceptorError::new(
ErrorType::Tls(e),
ErrorAction::Drop,
Bytes::copy_from_slice(&raw_version.to_be_bytes()),
)
})?;
// 3. Random (32 bytes)
let mut random = [0u8; 32];
bytes.copy_to_slice(&mut random);
// 4. Session ID (Variable length: 1 byte len + data)
let session_id_len = bytes.get_u8() as usize;
if bytes.len() < session_id_len {
return Ok(None); // Данные неполные
}
let session_id = bytes.split_to(session_id_len).freeze();
// 5. Cipher Suites (Variable length: 2 bytes len + data)
if bytes.len() < 2 {
return Ok(None);
}
let ciphers_len = bytes.get_u16() as usize;
if bytes.len() < ciphers_len {
return Ok(None);
}
let mut cipher_suites = Vec::with_capacity(ciphers_len / 2);
let mut ciphers_data = bytes.split_to(ciphers_len);
while ciphers_data.has_remaining() {
cipher_suites.push(ciphers_data.get_u16());
}
// 6. Compression Methods (1 byte len + data) - в TLS 1.3 обычно [0x01, 0x00]
if bytes.len() < 1 {
return Ok(None);
}
let comp_len = bytes.get_u8() as usize;
if bytes.len() < comp_len {
return Ok(None);
}
bytes.advance(comp_len); // Мы их просто пропускаем, они не нужны в структуре
// 7. Extensions (2 bytes len + data)
if bytes.is_empty() {
// Расширений может не быть (теоретически в старых TLS)
return Ok(Some(Self {
version,
random,
session_id,
cipher_suites,
extensions: Bytes::new(),
}));
}
if bytes.len() < 2 {
return Ok(None);
}
let extensions_len = bytes.get_u16() as usize;
if bytes.len() < extensions_len {
return Ok(None);
}
let extensions = bytes.split_to(extensions_len).freeze();
Ok(Some(Self {
version,
random,
session_id,
cipher_suites,
extensions,
}))
}
}
@@ -1,55 +0,0 @@
use bytes::{Buf, Bytes, BytesMut};
use crate::{
protocol::{
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
parser::parser::FrameParser,
},
tlseng::extension::{Extension, ExtensionStack},
};
impl FrameParser for ExtensionStack {
type Error = InterceptorError;
fn can_parse(bytes: &BytesMut) -> bool {
// Минимальное расширение: тип(2) + длина(2) = 4 байта
bytes.len() >= 4
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
let mut extensions = Vec::new();
while bytes.remaining() >= 4 {
let etype = bytes.get_u16();
let elen = bytes.get_u16() as usize;
if bytes.remaining() < elen {
// Если данных меньше, чем обещала длина расширения — это ошибка протокола
return Err(InterceptorError::new(
ErrorType::Tls("Invalid extension payload"),
ErrorAction::Drop,
Bytes::new(),
));
}
let data = bytes.split_to(elen).freeze();
extensions.push(Extension::new(etype, data));
}
Ok(Some(Self { extensions }))
}
}
impl ExtensionStack {
/// Возвращает данные расширения по его типу
pub fn find_by_type(&self, etype: u16) -> Option<Bytes> {
self.extensions
.iter()
.find(|e| e.etype == etype)
.map(|e| e.data.clone()) // Клонирование Bytes дешево (increment refcount)
}
/// Проверяет наличие расширения
pub fn has_extension(&self, etype: u16) -> bool {
self.extensions.iter().any(|e| e.etype == etype)
}
}
@@ -1,47 +0,0 @@
use bytes::{Buf, Bytes};
use crate::{
protocol::{
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
parser::parser::FrameParser,
},
tlseng::{handshake::hello_header::HelloHeader, types::HelloType},
utils::u24::{BufExt, U24},
};
impl FrameParser for HelloHeader {
type Error = InterceptorError;
fn can_parse(bytes: &bytes::BytesMut) -> bool {
if bytes.is_empty() {
return false;
}
let is_valid = match bytes[0] {
x if x == HelloType::Client as u8 => true,
x if x == HelloType::Server as u8 => true,
_ => false,
};
is_valid
}
fn parse(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
where
Self: Sized,
{
if bytes.len() < 4 {
return Ok(None);
}
let raw_header_type = bytes.get_u8();
let header_type = HelloType::try_from(raw_header_type).map_err(|e| {
InterceptorError::new(
ErrorType::Handshake(e),
ErrorAction::Drop,
Bytes::copy_from_slice(&raw_header_type.to_be_bytes()),
)
})?;
let len = bytes.get_u24();
Ok(Some(Self {
header_type,
len: U24::from_u32(len),
}))
}
}
-6
View File
@@ -1,6 +0,0 @@
mod app_data_parser;
mod client_hello_parser;
mod extension_parser;
mod h_header_parser;
mod server_hello_parser;
mod tls_header_parser;
@@ -1,94 +0,0 @@
use bytes::{Buf, Bytes, BytesMut};
use crate::{
protocol::{
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
parser::parser::FrameParser,
},
tlseng::{handshake::server_hello::ServerHello, types::ProtocolVersion},
};
impl FrameParser for ServerHello {
type Error = InterceptorError;
fn can_parse(bytes: &bytes::BytesMut) -> bool {
// Минимальный ServerHello:
// Version(2) + Random(32) + SessionID_len(1) + Cipher(2) + Compression(1) = 38 байт
bytes.len() >= 38
}
fn parse(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
where
Self: Sized,
{
// 1. Базовая проверка длины (базовые поля до компрессии включительно)
if bytes.len() < 38 {
return Ok(None);
}
// 2. Version (2 bytes)
let raw_version = bytes.get_u16();
let version = ProtocolVersion::try_from(raw_version).map_err(|e| {
InterceptorError::new(
ErrorType::Tls(e),
ErrorAction::Drop,
Bytes::copy_from_slice(&raw_version.to_be_bytes()),
)
})?;
// 3. Random (32 bytes)
let mut random = [0u8; 32];
bytes.copy_to_slice(&mut random);
// 4. Session ID (1 byte len + data)
let session_id_len = bytes.get_u8() as usize;
if bytes.len() < session_id_len {
return Ok(None);
}
let session_id = bytes.split_to(session_id_len).freeze();
// 5. Cipher Suite (2 bytes)
if bytes.len() < 2 {
return Ok(None);
}
let cipher_suite = bytes.get_u16();
// 6. Compression Method (1 byte)
if bytes.len() < 1 {
return Ok(None);
}
let _compression = bytes.get_u8();
// 7. Extensions (2 bytes len + data)
// Если после компрессии байтов нет — расширений просто нет.
if bytes.is_empty() {
return Ok(Some(Self {
version,
random,
session_id,
cipher_suite,
extensions: BytesMut::new(),
}));
}
// Если байты есть, проверяем заголовок длины расширений
if bytes.len() < 2 {
return Ok(None);
}
let extensions_len = bytes.get_u16() as usize;
if bytes.len() < extensions_len {
return Ok(None);
}
let extensions = bytes.split_to(extensions_len);
Ok(Some(Self {
version,
random,
session_id,
cipher_suite,
extensions,
}))
}
}
@@ -1,56 +0,0 @@
use bytes::{Buf, Bytes, BytesMut};
use crate::{
protocol::{
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
parser::parser::FrameParser,
},
tlseng::{
tls_record::TlsRecord,
types::{ContentType, ProtocolVersion},
},
};
impl FrameParser for TlsRecord {
type Error = InterceptorError;
fn can_parse(bytes: &BytesMut) -> bool {
if bytes.is_empty() {
return false;
}
let is_valid = match bytes[0] {
x if x == ContentType::Handshake as u8 => true,
x if x == ContentType::ApplicationData as u8 => true,
x if x == ContentType::Alert as u8 => true,
_ => false,
};
is_valid
}
fn parse(bytes: &mut BytesMut) -> Result<Option<TlsRecord>, Self::Error> {
if bytes.len() < 5 {
return Ok(None);
}
let len = u16::from_be_bytes([bytes[3], bytes[4]]);
if bytes.len() < 5 + len as usize {
return Ok(None);
}
let raw_content_type = bytes.get_u8();
let raw_version = bytes.get_u16();
let content_type = ContentType::try_from(raw_content_type).map_err(|e| {
InterceptorError::new(
ErrorType::Tls(e),
ErrorAction::Drop,
Bytes::copy_from_slice(&raw_content_type.to_be_bytes()),
)
})?;
let version = ProtocolVersion::try_from(raw_version).map_err(|e| {
InterceptorError::new(
ErrorType::Tls(e),
ErrorAction::Drop,
Bytes::copy_from_slice(&raw_version.to_be_bytes()),
)
})?;
let _raw_len = bytes.get_u16();
let payload = bytes.split_to(len as usize).freeze();
Ok(Some(TlsRecord::new(content_type, version, payload)))
}
}
+1 -3
View File
@@ -18,7 +18,6 @@ impl BufPair {
}
}
pub async fn read_from(&mut self, reader: &mut OwnedReadHalf) -> Result<usize, String> {
self.read_buf.clear(); // Вот она, автоматическая чистка
let n = reader
.read_buf(&mut self.read_buf)
.await
@@ -27,11 +26,10 @@ impl BufPair {
if n == 0 {
return Err("Connection closed by peer".to_string());
}
//println!("Reader {:?}", self.read_buf);
Ok(n)
}
pub async fn write_to(&mut self, writer: &mut OwnedWriteHalf) -> Result<(), String> {
pub async fn write_from(&mut self, writer: &mut OwnedWriteHalf) -> Result<(), String> {
writer
.write_all_buf(&mut self.write_buf)
.await
+231 -55
View File
@@ -1,80 +1,256 @@
use std::{net::SocketAddr, sync::Arc};
use tokio::net::{
tcp::{OwnedReadHalf, OwnedWriteHalf},
TcpStream,
use bytes::{Bytes, BytesMut};
use tracing::{instrument, info, debug, error, trace, warn};
use std::{collections::HashMap, net::SocketAddr};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{
tcp::{OwnedReadHalf, OwnedWriteHalf},
TcpStream,
},
sync::mpsc::{self},
};
use crate::{
protocol::codec::codec::Codec,
protocol::{
codec::{
codec::Codec,
frame::FrameType,
socks::{SocksReply, SocksRequest, SocksTarget},
},
errors::ErrorAction,
parser::parser::Parser,
},
proxy::connection::{
buf_pair::BufPair, handler::handler::ProxyHandler, state::ConnectionState,
buf_pair::BufPair,
engine::TunnelEngine,
handler::spawn_client_local_handler_with_rx,
muxer::{MuxMessage, Muxer},
},
};
pub struct VirtualStreams {
pub channels: HashMap<u32, tokio::sync::mpsc::Sender<Bytes>>,
}
pub enum ConnectionState {
Authorize,
Tunnel(VirtualStreams),
Close,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConnectionRole {
Client,
Server,
}
pub struct Connection {
pub addr: SocketAddr,
inbound: OwnedReadHalf,
outbound: OwnedWriteHalf,
state: ConnectionState,
buffers: BufPair,
codec: Codec,
addr: SocketAddr,
pub inbound: OwnedReadHalf,
pub outbound: OwnedWriteHalf,
pub buffers: BufPair,
pub codec: Codec,
}
impl Connection {
pub fn new(stream: TcpStream, addr: SocketAddr, init: bool) -> Self {
pub fn new(
stream: TcpStream,
addr: SocketAddr,
init: bool,
) -> Self {
let (inbound, outbound) = stream.into_split();
Self {
addr,
inbound,
outbound,
state: ConnectionState::New,
buffers: BufPair::new(),
codec: Codec::new(init),
}
}
pub async fn handle(
&mut self,
handler: Arc<dyn ProxyHandler + Send + Sync>,
) -> Result<ConnectionState, String> {
loop {
match &mut self.state {
ConnectionState::New => {
self.state = handler
.init_session(&mut self.inbound, &mut self.outbound, &mut self.buffers)
.await?
}
ConnectionState::Handshake => {
self.state = handler
.authorize_request(
&mut self.inbound,
&mut self.outbound,
&mut self.buffers,
&mut self.codec,
)
.await?;
}
ConnectionState::Tunnel(ref mut stream) => {
self.state = handler
.exchange_data(
&mut self.inbound,
&mut self.outbound,
&mut self.buffers,
&mut self.codec,
stream,
)
.await?;
}
ConnectionState::Close => {
self.state = handler
.finalize_session(&mut self.inbound, &mut self.outbound, &mut self.buffers)
.await?;
}
ConnectionState::Disconnected => {
println!("Disconnected");
return Ok(ConnectionState::Disconnected);
}
/// Читает и парсит запрос SOCKS5 из входящего потока
async fn read_socks_request(&mut self) -> Result<SocksRequest, String> {
loop {
// Попытка парсинга из текущего буфера
match SocksRequest::parse(&mut self.buffers.read_buf) {
Ok(Some(req)) => {
// Используем Debug-вывод (?req), так как SocksRequest обычно Enum
info!(client = %self.addr, request = ?req, "SOCKS request successfully parsed");
return Ok(req);
}
Ok(None) => {
// Это не ошибка, просто данных в сокете пока меньше, чем размер структуры SOCKS
trace!(client = %self.addr, buffer_len = self.buffers.read_buf.len(), "SOCKS parse: need more data");
}
Err(e) => {
error!(client = %self.addr, error = %e, "SOCKS protocol violation");
return Err(format!("Socks parse error: {}", e));
}
}
// Чтение новых данных из сокета
let n = self
.inbound
.read_buf(&mut self.buffers.read_buf)
.await
.map_err(|e| {
error!(client = %self.addr, error = %e, "Failed to read from socket during SOCKS handshake");
e.to_string()
})?;
if n == 0 {
warn!(client = %self.addr, "Client closed connection prematurely during SOCKS handshake");
return Err("Client closed connection during SOCKS handshake".into());
}
trace!(client = %self.addr, read_bytes = n, "Read data from client for SOCKS handshake");
}
}
/// Отправляет SOCKS ответ
async fn send_socks_reply(&mut self, reply: SocksReply) -> Result<(), String> {
let mut buf = BytesMut::with_capacity(24);
debug!(client = %self.addr, reply = ?reply, "Sending SOCKS reply to client");
reply.write_to(&mut buf);
self.outbound
.write_all(&buf)
.await
.map_err(|e| {
error!(client = %self.addr, error = %e, "Failed to send SOCKS reply");
e.to_string()
})?;
Ok(())
}
#[instrument(
name = "socks_handler",
skip(self, muxer),
fields(addr = %self.addr)
)]
pub async fn handle_socks_client(mut self, muxer: Muxer) -> Result<(), String> {
info!("Starting SOCKS multiplexed handling");
// 1. SOCKS Handshake
debug!("Reading SOCKS handshake request");
let _ = self.read_socks_request().await.map_err(|e| {
error!("SOCKS handshake failed: {}", e);
e
})?;
self.send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 }).await?;
// 2. SOCKS Connect
// 2. SOCKS Connect - читаем, КУДА хочет браузер
let req = self.read_socks_request().await?;
let target = if let SocksRequest::Connect { target, .. } = req {
target
} else {
return Err("Expected Connect".into());
};
let stream_id = muxer.next_id();
let target_str = match &target {
SocksTarget { host, port } => format!("{}:{}", String::from_utf8_lossy(host), port),
};
// --- НОВАЯ ЛОГИКА ОЖИДАНИЯ ---
// Регистрируем временный канал, чтобы получить Connect-подтверждение от сервера
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(100);
muxer.register_stream(stream_id, v_tx).await;
// Отправляем Connect-кадр на сервер
muxer.to_network.send(MuxMessage {
stream_id,
frame_type: FrameType::Connect,
data: Bytes::from(target_str),
}).await.map_err(|e| e.to_string())?;
let first_payload = match tokio::time::timeout(std::time::Duration::from_secs(10), v_rx.recv()).await {
Ok(Some(data)) => data,
_ => {
error!(stream_id, "Server timeout or failed to send Connect confirmation");
// Шлем браузеру ошибку, если сервер промолчал
self.send_socks_reply(SocksReply::ConnectResult {
reply_code: 0x01, atyp: 0x01, addr: [0, 0, 0, 0], port: 0,
}).await.ok();
return Err("Target connection failed".into());
}
};
// Проверяем код ответа (второй байт в SOCKS5)
if first_payload.len() >= 2 && first_payload[1] == 0x00 {
debug!(stream_id, "Server confirmed connection, forwarding SOCKS reply to browser");
// ВАЖНО: Отправляем браузеру ТО, что прислал сервер (те самые 10 байт)
// Не создаем новый SocksReply вручную, а пробрасываем байты сервера
self.outbound.write_all(&first_payload).await.map_err(|e| e.to_string())?;
} else {
// Если сервер прислал ошибку (reply_code != 0), тоже пробрасываем её браузеру и выходим
self.outbound.write_all(&first_payload).await.ok();
return Err("Server rejected connection".into());
}
// 4. Разбираем self и запускаем хендлер
let Self { inbound: browser_in, outbound: browser_out, .. } = self;
// Передаем v_rx (в котором уже могут быть данные от сервера после "OK") в хендлер
spawn_client_local_handler_with_rx(stream_id, browser_in, browser_out, muxer.clone(), v_rx);
Ok(())
}
#[instrument(
name = "server_tunnel",
skip(self),
fields(addr = %self.addr)
)]
pub async fn handle_server_tunnel(mut self) -> Result<(), String> {
info!("Acting as TLS Server, waiting for ClientHello");
// Создаем Muxer для сервера
let (mux_tx, mux_rx) = mpsc::channel(1024);
let muxer = Muxer::new(mux_tx.clone(), false); // false, так как это Сервер
// 1. TLS Handshake
let server_hello_bytes = loop {
match self.codec.make_server_handshake(&mut self.buffers.read_buf) {
Ok(bytes) => {
info!("ClientHello received, sending ServerHello");
break bytes;
},
Err(e) if e.action == ErrorAction::Wait => {
let n = self.inbound.read_buf(&mut self.buffers.read_buf).await
.map_err(|err| format!("Read error: {}", err))?;
if n == 0 { return Err("Client closed connection".into()); }
}
Err(e) => return Err(format!("TLS error: {:?}", e)),
}
};
self.outbound.write_all(&server_hello_bytes).await.map_err(|e| e.to_string())?;
info!("TLS Tunnel established as server");
// 2. Передача управления в TunnelEngine
debug!("Handover to TunnelEngine");
let engine = TunnelEngine {
inbound: self.inbound,
outbound: self.outbound,
codec: self.codec,
buffers: self.buffers,
mux_rx,
muxer,
role: ConnectionRole::Server,
};
engine.run().await.map_err(|e| {
error!("TunnelEngine error: {}", e);
e
})
}
}
+149
View File
@@ -0,0 +1,149 @@
use crate::{
protocol::{
codec::{
codec::Codec,
frame::{Frame, FrameType},
},
errors::ErrorAction,
},
proxy::connection::{
buf_pair::BufPair,
connection::ConnectionRole,
handler::spawn_server_target_handler,
muxer::{MuxMessage, Muxer},
},
};
use bytes::Bytes;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use tokio::sync::mpsc::Receiver;
use tracing::{debug, error, info, trace, warn};
pub struct TunnelEngine {
pub inbound: OwnedReadHalf,
pub outbound: OwnedWriteHalf,
pub codec: Codec,
pub buffers: BufPair,
pub mux_rx: Receiver<MuxMessage>,
pub muxer: Muxer,
pub role: ConnectionRole,
}
impl TunnelEngine {
pub async fn run(mut self) -> Result<(), String> {
info!(role = ?self.role, "TunnelEngine spinning up");
loop {
tokio::select! {
// 1. Физический Inbound (Сеть -> Muxer)
// Теперь возвращает Vec<Frame>, чтобы обработать всё накопленное
res = Self::process_inbound(&mut self.inbound, &mut self.codec, &mut self.buffers) => {
match res {
Ok(frames) => {
for frame in frames {
self.handle_incoming_frame(frame).await;
}
}
Err(e) => {
if e == "EOF" {
info!("Physical connection closed by remote (EOF)");
} else {
error!(error = %e, "Critical error in process_inbound");
}
return Err(e);
}
}
}
// 2. Физический Outbound (Muxer -> Сеть)
Some(msg) = self.mux_rx.recv() => {
if let Err(e) = self.handle_outbound_msg(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(
inbound: &mut OwnedReadHalf,
codec: &mut Codec,
buffers: &mut BufPair,
) -> Result<Vec<Frame>, String> {
let mut frames = Vec::new();
// Сначала читаем данные из сокета в буфер
let n = inbound
.read_buf(&mut buffers.read_buf)
.await
.map_err(|e| e.to_string())?;
if n == 0 && buffers.read_buf.is_empty() {
return Err("EOF".into());
}
// Теперь пытаемся достать столько фреймов, сколько получится
loop {
match codec.inbound(&mut buffers.read_buf) {
Ok(Some(frame)) => frames.push(frame),
Ok(None) => break, // Больше полных фреймов нет
Err(e) if e.action == ErrorAction::Wait => break,
Err(e) => return Err(format!("Codec error: {:?}", e)),
}
}
Ok(frames)
}
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(())
}
Err(e) => Err(format!("Encryption error: {:?}", e)),
}
}
}
+190
View File
@@ -0,0 +1,190 @@
use crate::protocol::codec::frame::FrameType;
use crate::protocol::codec::socks::{SocksReply, ATYP_IPV4, REPLY_SUCCESS};
use crate::proxy::connection::muxer::{MuxMessage, Muxer};
use bytes::{Bytes, BytesMut};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use tokio::net::TcpStream;
use tokio::sync::mpsc;
use tracing::{debug, error, info, trace, warn};
pub fn spawn_client_local_handler_with_rx(
stream_id: u32,
mut r: OwnedReadHalf,
mut w: OwnedWriteHalf,
muxer: Muxer,
mut v_rx: mpsc::Receiver<Bytes>, // Используем эту читалку, она уже зарегистрирована!
) {
// ВНИМАНИЕ: Здесь больше не создаем канал и не вызываем register_stream,
// так как это уже сделал handle_socks_client до вызова этой функции.
tokio::spawn(async move {
let mut buf = BytesMut::with_capacity(8192);
debug!(
stream_id,
"Spawned client local handler with existing receiver"
);
loop {
tokio::select! {
// 1. Читаем из браузера -> Шлем в общий туннель
res = r.read_buf(&mut buf) => {
match res {
Ok(0) => {
debug!(stream_id, "Browser closed connection (EOF)");
break;
}
Err(e) => {
error!(stream_id, error = %e, "Read error from browser");
break;
}
Ok(n) => {
let msg = MuxMessage {
stream_id,
frame_type: FrameType::Data,
data: buf.split().freeze(),
};
if muxer.to_network.send(msg).await.is_err() { break; }
}
}
}
// 2. Читаем из виртуального канала (данные из туннеля) -> Шлем браузеру
maybe_data = v_rx.recv() => {
match maybe_data {
Some(data) => {
if let Err(e) = w.write_all(&data).await {
error!(stream_id, error = %e, "Write error to browser");
break;
}
}
None => {
debug!(stream_id, "Virtual channel closed by Muxer");
break;
}
}
}
}
}
// КРИТИЧНО: Сообщаем серверу, что мы закрываем этот конкретный стрим
let _ = muxer
.to_network
.send(MuxMessage {
stream_id,
frame_type: FrameType::Close,
data: Bytes::new(),
})
.await;
// Чистим за собой в таблице стримов
muxer.remove_stream(stream_id).await;
info!(stream_id, "Client handler terminated");
});
}
pub async fn spawn_server_target_handler(stream_id: u32, target_raw: Bytes, muxer: Muxer) {
let addr = String::from_utf8_lossy(&target_raw).to_string();
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(100);
muxer.register_stream(stream_id, v_tx).await;
tokio::spawn(async move {
info!(stream_id, target = %addr, "Attempting to connect to target");
match TcpStream::connect(&addr).await {
Ok(stream) => {
// Формируем SOCKS5 Success Reply используя структуру
let mut reply_buf = BytesMut::with_capacity(10);
let reply = SocksReply::ConnectResult {
reply_code: REPLY_SUCCESS,
atyp: ATYP_IPV4,
addr: [0, 0, 0, 0],
port: 0,
};
reply.write_to(&mut reply_buf);
// Отправляем подтверждение в сеть
let _ = muxer
.to_network
.send(MuxMessage {
stream_id,
frame_type: FrameType::Connect,
data: reply_buf.freeze(),
})
.await;
info!(stream_id, target = %addr, "Connected to target host, SOCKS reply sent");
let (mut r, mut w) = stream.into_split();
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) => {
error!(stream_id, error = %e, "Target read error");
break;
}
}
}
}
}
}
Err(e) => {
error!(stream_id, target = %addr, error = %e, "Connection to target failed");
// Формируем SOCKS5 Failure Reply (0x01 - General failure)
let mut reply_buf = BytesMut::with_capacity(10);
let reply = SocksReply::ConnectResult {
reply_code: 0x01,
atyp: ATYP_IPV4,
addr: [0, 0, 0, 0],
port: 0,
};
reply.write_to(&mut reply_buf);
let _ = muxer
.to_network
.send(MuxMessage {
stream_id,
frame_type: FrameType::Connect,
data: reply_buf.freeze(),
})
.await;
}
}
// Финализация: уведомляем сеть о закрытии и чистим муксер
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");
});
}
@@ -1,40 +0,0 @@
use async_trait::async_trait;
use tokio::net::{
tcp::{OwnedReadHalf, OwnedWriteHalf},
TcpStream,
};
use crate::{
protocol::codec::codec::Codec,
proxy::connection::{buf_pair::BufPair, state::ConnectionState},
};
#[async_trait]
pub trait ProxyHandler {
async fn init_session(
&self,
client_reader: &mut OwnedReadHalf,
client_writer: &mut OwnedWriteHalf,
buffers: &mut BufPair,
) -> Result<ConnectionState, String>;
async fn authorize_request(
&self,
client_reader: &mut OwnedReadHalf,
client_writer: &mut OwnedWriteHalf,
buffers: &mut BufPair,
codec: &mut Codec,
) -> Result<ConnectionState, String>;
async fn exchange_data(
&self,
client_reader: &mut OwnedReadHalf,
client_writer: &mut OwnedWriteHalf,
buffers: &mut BufPair,
codec: &mut Codec,
target: &mut TcpStream,
) -> Result<ConnectionState, String>;
async fn finalize_session(
&self,
client_reader: &mut OwnedReadHalf,
client_writer: &mut OwnedWriteHalf,
buffers: &mut BufPair,
) -> Result<ConnectionState, String>;
}
@@ -1,4 +0,0 @@
pub mod handler;
pub mod netr2tcp;
pub mod tcp2netr;
mod utils;
@@ -1,130 +0,0 @@
use crate::{
protocol::codec::codec::Codec,
proxy::connection::{
buf_pair::BufPair,
handler::{handler::ProxyHandler, utils::relay_data},
state::ConnectionState,
},
};
use async_trait::async_trait;
use bytes::BytesMut;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{
tcp::{OwnedReadHalf, OwnedWriteHalf},
TcpStream,
},
};
pub struct Netr2Tcp;
impl Netr2Tcp {
pub fn raw_addr_to_string(raw: &mut BytesMut) -> Result<String, String> {
println!("len is {:?}", raw.len());
if raw.is_empty() {
return Err("Buffer is empty".into());
}
let len = raw[0] as usize;
if raw.len() < 1 + len + 2 {
return Err(format!(
"Buffer too short: expected {}, got {}",
1 + len + 2,
raw.len()
)
.into());
}
let address = String::from_utf8_lossy(&raw[1..1 + len]);
let port_start = 1 + len;
let port = u16::from_be_bytes([raw[port_start], raw[port_start + 1]]);
Ok(format!("{}:{}", address, port))
}
}
#[async_trait]
impl ProxyHandler for Netr2Tcp {
async fn init_session(
&self,
_client_reader: &mut OwnedReadHalf,
_client_writer: &mut OwnedWriteHalf,
_buffers: &mut BufPair,
) -> Result<ConnectionState, String> {
Ok(ConnectionState::Handshake)
}
async fn authorize_request(
&self,
client_reader: &mut OwnedReadHalf,
_client_writer: &mut OwnedWriteHalf,
buffers: &mut BufPair,
_codec: &mut Codec,
) -> Result<ConnectionState, String> {
buffers.read_from(client_reader).await?;
let header_len = if !buffers.read_buf.is_empty() {
1 + (buffers.read_buf[0] as usize) + 2
} else {
return Err("Buffer is empty".into());
};
let address_to_connect = Netr2Tcp::raw_addr_to_string(&mut buffers.read_buf)?;
println!("Address is: {:?}", address_to_connect);
// Отрезаем заголовок, оставляя только данные приложения (TLS и т.д.)
let _header = buffers.read_buf.split_to(header_len);
let target_stream = TcpStream::connect(address_to_connect)
.await
.map_err(|e| e.to_string())?;
Ok(ConnectionState::Tunnel(target_stream))
}
async fn exchange_data(
&self,
client_reader: &mut OwnedReadHalf,
client_writer: &mut OwnedWriteHalf,
buffers: &mut BufPair,
_codec: &mut Codec,
target: &mut TcpStream,
) -> Result<ConnectionState, String> {
let (mut target_reader, mut target_writer) = target.split();
println!("Netr2Tcp Стартанул в тонель");
loop {
tokio::select! {
// 1. От клиента к целевому серверу
res = client_reader.read_buf(&mut buffers.read_buf) => {
let should_break =
relay_data(res, &mut target_writer, &mut buffers.read_buf).await?;
if should_break { break; }
}
// 2. От целевого сервера к клиенту
res = target_reader.read_buf(&mut buffers.write_buf) => {
let should_break =
relay_data(res, client_writer, &mut buffers.write_buf).await?;
if should_break { break;}
}
}
}
println!("Cycles breaked");
client_writer.shutdown().await.map_err(|e| e.to_string())?;
target_writer.shutdown().await.map_err(|e| e.to_string())?;
Ok(ConnectionState::Close)
}
async fn finalize_session(
&self,
_client_reader: &mut OwnedReadHalf,
_client_writer: &mut OwnedWriteHalf,
_buffers: &mut BufPair,
) -> Result<ConnectionState, String> {
Ok(ConnectionState::Disconnected)
}
}
@@ -1,208 +0,0 @@
use crate::{
protocol::codec::codec::Codec,
proxy::connection::{
buf_pair::BufPair,
handler::{handler::ProxyHandler, utils::relay_data},
state::ConnectionState,
},
};
use async_trait::async_trait;
use bytes::{BufMut, Bytes};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{
tcp::{OwnedReadHalf, OwnedWriteHalf},
TcpStream,
},
};
use bytes::BytesMut;
enum SocksMsg {
Hello, // Hello (0x05, 0x00)
AuthFailed, // Error (0x05, 0xFF)
ConnectOk, // Connection is OK
Custom(Vec<u8>), // If needed raw bytes
}
impl SocksMsg {
pub fn write_to(self, buf: &mut BytesMut) {
buf.clear(); // Чистим перед записью ВСЕГДА
match self {
SocksMsg::Hello => {
buf.put_slice(&[0x05, 0x00]);
}
SocksMsg::AuthFailed => {
buf.put_slice(&[0x05, 0xFF]);
}
SocksMsg::ConnectOk => {
// SOCKS5 требует 10 байт в ответ на CONNECT:
// VER, REP(0), RSV, ATYP(1), ADDR(0,0,0,0), PORT(0,0)
buf.put_slice(&[0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]);
}
SocksMsg::Custom(data) => {
buf.put_slice(&data);
}
}
}
}
pub struct Tcp2Netr {
socks5: bool,
proxy_address: String,
}
impl Tcp2Netr {
pub fn new(socks5: bool, address: String) -> Self {
Self {
socks5,
proxy_address: address,
}
}
fn get_addr_raw(data: &mut BytesMut) -> Result<Bytes, String> {
if data.len() < 4 {
return Err("Too short".into());
}
let atyp = data[3];
let total_len = match atyp {
0x01 => 10, // 4 (header) + 4 (ip) + 2 (port)
0x03 => {
let domain_len = data[4] as usize;
4 + 1 + domain_len + 2 // 4 (header) + 1 (len) + N (domain) + 2 (port)
}
_ => return Err("Unsupported address type".to_string()),
};
if data.len() < total_len {
return Err("Incomplete SOCKS packet".into());
}
// ВАЖНО: split_to удаляет эти байты из data и возвращает их нам
// Теперь в data останется только TLS ClientHello!
let socks_packet = data.split_to(total_len);
// Формируем твой кастомный адрес (длина + данные + порт)
let mut result = BytesMut::new();
if atyp == 0x01 {
result.put_u8(4);
result.put_slice(&socks_packet[4..10]);
} else {
let len = socks_packet[4];
result.put_u8(len);
result.put_slice(&socks_packet[5..total_len]);
}
Ok(result.freeze())
}
}
#[async_trait]
impl ProxyHandler for Tcp2Netr {
// 1. Инициализация: отвечаем SOCKS5 Hello (0x05, 0x00)
async fn init_session(
&self,
client_reader: &mut OwnedReadHalf,
client_writer: &mut OwnedWriteHalf,
buffers: &mut BufPair,
) -> Result<ConnectionState, String> {
buffers.read_from(client_reader).await?;
SocksMsg::Hello.write_to(&mut buffers.write_buf);
buffers.write_to(client_writer).await?;
Ok(ConnectionState::Handshake)
}
// 2. Авторизация/Парсинг: достаем адрес из SOCKS5 Connect и подключаемся к Netr
async fn authorize_request(
&self,
client_reader: &mut OwnedReadHalf,
client_writer: &mut OwnedWriteHalf,
buffers: &mut BufPair,
_codec: &mut Codec,
) -> Result<ConnectionState, String> {
buffers.read_from(client_reader).await?;
// Извлекаем адрес и удаляем SOCKS-заголовок из буфера
let address_to_connect = Tcp2Netr::get_addr_raw(&mut buffers.read_buf)?;
// Коннектимся к твоему Netr серверу (proxy_address)
let mut target_stream = TcpStream::connect(&self.proxy_address)
.await
.map_err(|e| e.to_string())?;
// Шлем кастомный заголовок адреса в сторону Netr
target_stream
.write_all(&address_to_connect)
.await
.map_err(|e| e.to_string())?;
// Если в буфере остался TLS ClientHello (после split_to в get_addr_raw),
// проталкиваем его немедленно, чтобы сервер не ждал
if !buffers.read_buf.is_empty() {
target_stream
.write_all(&buffers.read_buf)
.await
.map_err(|e| e.to_string())?;
buffers.read_buf.clear();
}
// Отвечаем клиенту (браузеру), что SOCKS-соединение установлено
SocksMsg::ConnectOk.write_to(&mut buffers.write_buf);
buffers.write_to(client_writer).await?;
// Переходим в режим туннеля, передавая сокет до Netr-сервера
Ok(ConnectionState::Tunnel(target_stream))
}
// 3. Обмен данными: гоняем байты между клиентом и Netr-сервером
async fn exchange_data(
&self,
client_reader: &mut OwnedReadHalf,
client_writer: &mut OwnedWriteHalf,
buffers: &mut BufPair,
_codec: &mut Codec,
target: &mut TcpStream,
) -> Result<ConnectionState, String> {
buffers.reset();
let (mut target_reader, mut target_writer) = target.split();
println!("Socks2Netr Стартанул в тонель");
loop {
tokio::select! {
// Из браузера -> в сторону Netr
res = client_reader.read_buf(&mut buffers.read_buf) => {
let should_break =
relay_data(res, &mut target_writer, &mut buffers.read_buf).await?;
if should_break { break;}
}
// Из Netr -> обратно в браузер
res = target_reader.read_buf(&mut buffers.write_buf) => {
let should_break =
relay_data(res, client_writer, &mut buffers.write_buf).await?;
if should_break { break;}
}
}
}
client_writer.shutdown().await.map_err(|e| e.to_string())?;
target_writer.shutdown().await.map_err(|e| e.to_string())?;
Ok(ConnectionState::Close)
}
// 4. Финализация: логируем закрытие
async fn finalize_session(
&self,
_client_reader: &mut OwnedReadHalf,
_client_writer: &mut OwnedWriteHalf,
_buffers: &mut BufPair,
) -> Result<ConnectionState, String> {
println!("SOCKS5 CONNECTION CLOSED");
Ok(ConnectionState::Disconnected)
}
}
@@ -1,26 +0,0 @@
use std::io::Error;
use bytes::BytesMut;
use tokio::io::AsyncWriteExt;
pub async fn relay_data<W>(
res: Result<usize, Error>,
writer: &mut W,
buffer: &mut BytesMut,
) -> Result<bool, String>
where
W: AsyncWriteExt + Unpin,
{
match res {
Ok(0) => {
println!("Read 0 bytes - closing half of connection");
return Ok(true); // Это закроет туннель
}
Ok(n) => println!("Relayed {} bytes", n),
Err(e) => println!("Relay error: {}", e),
}
println!("What is here {:?}", &buffer);
writer.write_buf(buffer).await.map_err(|e| e.to_string())?;
Ok(false)
}
+2 -1
View File
@@ -1,4 +1,5 @@
pub mod buf_pair;
pub mod connection;
pub mod engine;
pub mod handler;
pub mod state;
pub mod muxer;
+81
View File
@@ -0,0 +1,81 @@
use crate::protocol::codec::frame::FrameType;
use bytes::Bytes;
use std::collections::HashMap;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use tokio::sync::mpsc::Sender;
use tokio::sync::RwLock;
use tracing::error;
pub struct IdGenerator {
counter: AtomicU32,
}
impl IdGenerator {
pub fn new(is_client: bool) -> Self {
let start = if is_client { 1 } else { 2 };
Self {
counter: AtomicU32::new(start),
}
}
pub fn next(&self) -> u32 {
self.counter.fetch_add(2, Ordering::Relaxed)
}
}
pub struct MuxMessage {
pub stream_id: u32,
pub frame_type: FrameType,
pub data: Bytes,
}
#[derive(Clone)]
pub struct Muxer {
pub to_network: Sender<MuxMessage>,
streams: Arc<RwLock<HashMap<u32, Sender<Bytes>>>>,
id_gen: Arc<IdGenerator>,
}
impl Muxer {
pub fn new(to_network: Sender<MuxMessage>, is_client: bool) -> Self {
Self {
to_network,
streams: Arc::new(RwLock::new(HashMap::new())),
id_gen: Arc::new(IdGenerator::new(is_client)),
}
}
// Прокси-метод для получения ID
pub fn next_id(&self) -> u32 {
self.id_gen.next()
}
pub async fn register_stream(&self, stream_id: u32, tx: Sender<Bytes>) {
let mut lock = self.streams.write().await;
lock.insert(stream_id, tx);
// ДОБАВЬ ЭТО:
tracing::debug!(
stream_id,
total_active = lock.len(),
"STREAMS_MAP_UPDATE: Registered new stream"
);
}
pub async fn remove_stream(&self, stream_id: u32) {
self.streams.write().await.remove(&stream_id);
}
/// Отправляет входящие данные конкретному локальному обработчику
pub async fn dispatch_to_local(&self, stream_id: u32, data: Bytes) {
// Асинхронный лок сам умеет корректно работать с .await
let tx = self.streams.read().await.get(&stream_id).cloned();
if let Some(tx) = tx {
if tx.send(data).await.is_err() {
self.remove_stream(stream_id).await;
}
} else {
error!(stream_id, "MUXER: Received data for UNKNOWN stream_id");
}
}
}
-10
View File
@@ -1,10 +0,0 @@
use tokio::net::TcpStream;
//todo split to codec that uses my frame and tls codec that remove camouflage
pub enum ConnectionState {
New,
Handshake,
Tunnel(TcpStream),
Close,
Disconnected,
}
+144 -45
View File
@@ -1,65 +1,164 @@
use std::sync::Arc;
use crate::proxy::connection::{
connection::Connection, handler::handler::ProxyHandler, state::ConnectionState,
use crate::{
protocol::errors::ErrorAction,
proxy::connection::{
connection::{Connection, ConnectionRole},
engine::TunnelEngine,
muxer::{MuxMessage, Muxer},
},
tlseng::profile::BrowserProfile,
};
use tokio::net::TcpListener;
use bytes::BytesMut;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{TcpListener, TcpStream},
};
use tracing::{debug, error, info, instrument}; // Импортируем макросы
pub struct Network {
inbound_handler: Arc<dyn ProxyHandler + Send + Sync>,
outbound_handler: Arc<dyn ProxyHandler + Send + Sync>,
port: u16,
role: ConnectionRole,
remote_proxy_addr: Option<String>,
}
impl Network {
pub fn new(
inbound_handler: Arc<dyn ProxyHandler + Send + Sync>,
outbound_handler: Arc<dyn ProxyHandler + Send + Sync>,
port: u16,
) -> Self {
pub fn new(port: u16, role: ConnectionRole, remote_proxy_addr: Option<String>) -> Self {
Self {
inbound_handler,
outbound_handler,
port,
role,
remote_proxy_addr,
}
}
// Добавляем инструмент, чтобы видеть параметры запуска сети в логах
#[instrument(skip(self), fields(role = ?self.role, port = self.port))]
pub async fn run(&self) {
let port = self.port;
let listener: TcpListener = TcpListener::bind(format!("127.0.0.1:{port}"))
.await
.unwrap();
println!("Server Listening on Port {}...", port);
loop {
let (stream, addr) = listener.accept().await.expect("Error on connection");
let handler = self.inbound_handler.clone();
tokio::spawn(async move {
let mut conection: Connection = Connection::new(stream, addr, false);
let addr = format!("127.0.0.1:{}", self.port);
let res = conection.handle(handler).await;
match res {
Ok(state) => match state {
ConnectionState::New => {
println!("New connection {}", addr)
}
ConnectionState::Handshake => {
println!("Connection {} handshaked", addr)
}
ConnectionState::Tunnel(_stream) => {
println!("Connection {} tunnel", addr)
}
ConnectionState::Close => {
println!("Connection {} closed", addr)
}
ConnectionState::Disconnected => {
println!("Connection {} Disconnected", addr)
}
},
match self.role {
ConnectionRole::Client => {
// --- ЛОГИКА КЛИЕНТА ---
// 1. Создаем ОДИН туннель до прокси-сервера при старте
info!("Starting Client mode: Initializing persistent tunnel to proxy...");
let muxer = match self.initialize_client_tunnel().await {
Ok(m) => m,
Err(e) => {
eprintln!("Ошибка соединения с {}: {}", conection.addr, e);
error!(error = %e, "Failed to initialize global tunnel. Exiting.");
return;
}
};
});
// 2. Теперь слушаем SOCKS-запросы от браузера
let listener = TcpListener::bind(&addr)
.await
.expect("Failed to bind SOCKS port");
info!(socks_addr = %addr, "SOCKS5 server ready for browser connections");
loop {
if let Ok((stream, client_addr)) = listener.accept().await {
let current_muxer = muxer.clone();
tokio::spawn(async move {
// Используем новый метод handle_socks_client
let connection = Connection::new(stream, client_addr, false);
if let Err(e) = connection.handle_socks_client(current_muxer).await {
error!(client = %client_addr, error = %e, "SOCKS stream error");
}
});
}
}
}
ConnectionRole::Server => {
// --- ЛОГИКА СЕРВЕРА ---
let listener = TcpListener::bind(&addr)
.await
.expect("Failed to bind Server port");
info!(listen_addr = %addr, "Proxy Server listening for incoming tunnels");
loop {
if let Ok((stream, client_addr)) = listener.accept().await {
tokio::spawn(async move {
// Сервер использует handle_server_tunnel
let connection = Connection::new(stream, client_addr, true);
if let Err(e) = connection.handle_server_tunnel().await {
error!(client = %client_addr, error = %e, "Tunnel error");
}
});
}
}
}
}
}
/// Вспомогательный метод для Клиента: создает TLS туннель и запускает TunnelEngine
async fn initialize_client_tunnel(&self) -> Result<Muxer, String> {
let server_addr = self
.remote_proxy_addr
.as_ref()
.ok_or("Remote proxy address not configured")?;
// 1. Устанавливаем TCP соединение с сервером
let stream = TcpStream::connect(server_addr)
.await
.map_err(|e| format!("Connect to proxy failed: {}", e))?;
// 2. Создаем временный Connection
let dummy_addr = server_addr.parse().unwrap_or("0.0.0.0:0".parse().unwrap());
// Передаем stream (Connection сам сделает into_split внутри, если у тебя так написано в new)
let mut conn = Connection::new(stream, dummy_addr, false);
// 3. TLS Handshake (Клиентская часть)
debug!("Starting persistent TLS handshake with proxy");
let ch = conn
.codec
.make_client_handshake(&BrowserProfile::CHROME_131, "proxy.server")
.map_err(|e| format!("{:?}", e))?;
conn.outbound
.write_all(&ch)
.await
.map_err(|e| e.to_string())?;
let mut sh_buf = BytesMut::with_capacity(2048);
while let Err(e) = conn.codec.process_handshake(&mut sh_buf) {
if e.action != ErrorAction::Wait {
return Err(format!("Fatal handshake error: {:?}", e));
}
// Теперь read_buf найдется, так как мы импортировали AsyncReadExt
let n = conn
.inbound
.read_buf(&mut sh_buf)
.await
.map_err(|e| e.to_string())?;
if n == 0 {
return Err("Server closed connection during handshake".into());
}
}
info!("Persistent TLS Tunnel established successfully!");
// 4. Инициализируем Muxer и TunnelEngine
// Явно указываем тип сообщения для канала, чтобы убрать "cannot infer type"
let (mux_tx, mux_rx) = tokio::sync::mpsc::channel::<MuxMessage>(1024);
let muxer = Muxer::new(mux_tx, true);
// 5. Запускаем TunnelEngine в фоне.
let engine = TunnelEngine {
inbound: conn.inbound,
outbound: conn.outbound,
codec: conn.codec,
buffers: conn.buffers,
mux_rx,
muxer: muxer.clone(),
role: ConnectionRole::Client,
};
tokio::spawn(async move {
if let Err(e) = engine.run().await {
error!("Main TunnelEngine died: {}", e);
}
});
Ok(muxer)
}
}
-22
View File
@@ -1,22 +0,0 @@
use bytes::{Bytes, BytesMut};
use crate::tlseng::{
tls_record::TlsRecord,
types::{ContentType, ProtocolVersion},
};
pub struct ApplicationData {
pub len: usize,
pub payload: Bytes,
}
impl ApplicationData {
pub fn make_application_data(bytes: &mut BytesMut) -> Bytes {
let record = TlsRecord::new(
ContentType::ApplicationData,
ProtocolVersion::Tls12,
bytes.split_to(bytes.len()).freeze(),
);
record.serialize()
}
}
+13 -39
View File
@@ -1,48 +1,22 @@
// --- Core Handshake Identifiers ---
/// ClientHello handshake message type
/// Handshake message types
pub const HANDSHAKE_TYPE_CLIENT_HELLO: u8 = 0x01;
pub const HANDSHAKE_TYPE_SERVER_HELLO: u8 = 0x02;
// --- TLS Extension Type Codes (IANA) ---
/// Server Name Indication (SNI) - maps a hostname to the IP
pub const EXT_TYPE_SNI: u16 = 0x0000;
/// Certificate Status Request (OCSP Stapling)
pub const EXT_STATUS_REQUEST: u16 = 0x0005;
/// Supported Elliptic Curves (Named Groups)
pub const EXT_SUPPORTED_GROUPS: u16 = 0x000a;
/// Supported Point Formats (Legacy, but required for compatibility)
pub const EXT_EC_POINT_FORMATS: u16 = 0x000b;
/// Signature Algorithms the client can verify
pub const EXT_SIGNATURE_ALGORITHMS: u16 = 0x000d;
/// Application-Layer Protocol Negotiation (h2, http/1.1)
pub const EXT_ALPN: u16 = 0x0010;
/// Signed Certificate Timestamp (SCT) - used for Certificate Transparency
pub const EXT_SIGNED_CERT_TIMESTAMP: u16 = 0x0012;
/// Padding extension to avoid MTU issues or fingerprinting
pub const EXT_PADDING: u16 = 0x0015;
/// Extended Master Secret - prevents MITM key synchronization attacks
pub const EXT_EXTENDED_MASTER_SECRET: u16 = 0x0017;
/// Certificate Compression (Used by modern browsers like Chrome)
pub const EXT_COMPRESS_CERTIFICATE: u16 = 0x001b;
/// Delegated Credentials (RFC 9345)
pub const EXT_DELEGATED_CREDENTIAL: u16 = 0x0022;
///SESSION TICKET
pub const EXT_SESSION_TICKET: u16 = 0x0023;
/// Negotiated TLS Versions (Crucial for TLS 1.3)
pub const EXT_SUPPORTED_VERSIONS: u16 = 0x002b;
/// Pre-Shared Key (PSK) Exchange Modes
pub const EXT_PSK_KEY_EXCHANGE_MODES: u16 = 0x002d;
/// Key Share - carries the Diffie-Hellman public keys
pub const EXT_KEY_SHARE: u16 = 0x0033;
/// Application Settings (ALPS) - Chrome specific protocol settings
pub const EXT_ALPS: u16 = 0x44cd;
/// SNI (Server Name Indication) specific
pub const TYPE_HOST_NAME: u8 = 0x00;
// --- Compatibility & Anti-Detection (Fingerprinting) ---
/// Secure Renegotiation Indication (RFC 5746)
pub const EXT_RENEGOTIATION_INFO: u16 = 0xff01;
/// PSK (Pre-Shared Key) modes
pub const PSK_DHE_KE_MODE: u8 = 0x01;
/// Certificate compression algorithms
pub const CERT_COMPRESSION_BROTLI: u16 = 0x0002;
/// Extension internal status types
pub const OCSP_STATUS_TYPE: u8 = 0x01;
//pub const EC_POINT_FORMAT_UNCOMPRESSED: u8 = 0x00;
/// GREASE (Generate Random Extensions And Sustain Extensibility)
/// Used to prevent server bugs where unknown extensions cause failures.
/// Используется для предотвращения ошибок серверов при встрече с неизвестными ID.
pub const GREASE_IDENTIFIERS: [u16; 16] = [
0x0A0A, 0x1A1A, 0x2A2A, 0x3A3A, 0x4A4A, 0x5A5A, 0x6A6A, 0x7A7A, 0x8A8A, 0x9A9A, 0xAAAA, 0xBABA,
0xCACA, 0xDADA, 0xEAEA, 0xFAFA,
+142 -138
View File
@@ -1,12 +1,14 @@
use bytes::{BufMut, Bytes, BytesMut};
use rand::Rng;
use rand::RngExt;
// Using your provided constants and types
use crate::tlseng::{
consts::*,
params::{TlsGroups, TlsSignatures, TlsVersions},
profile::profile::BrowserProfile,
values::*,
consts::{
CERT_COMPRESSION_BROTLI, GREASE_IDENTIFIERS, OCSP_STATUS_TYPE, PSK_DHE_KE_MODE,
TYPE_HOST_NAME,
},
profile::BrowserProfile,
types::{TlsExtensions, TlsGroups, TlsSignatures, TlsVersions},
};
#[derive(Debug)]
@@ -21,7 +23,26 @@ pub struct ExtensionStack {
pub extensions: Vec<Extension>,
}
impl ExtensionStack {
pub fn find_by_type(&self, etype: u16) -> Option<Bytes> {
self.extensions
.iter()
.find(|e| e.etype == etype)
.map(|e| e.data.clone())
}
}
impl Extension {
/// Creates a new Extension from the given parameters.
///
/// # Arguments
///
/// * `etype`: The type of extension (e.g., EXT_SUPPORTED_VERSIONS).
/// * `data`: The actual data being transported (e.g., a serialized list of supported versions).
///
/// # Returns
///
/// A new Extension structure with the given parameters.
pub fn new(etype: u16, data: Bytes) -> Self {
Self {
etype,
@@ -29,6 +50,16 @@ impl Extension {
data,
}
}
/// Packs an extension into a single byte array.
///
/// # Arguments
///
/// * `etype`: The type of extension (e.g., EXT_SUPPORTED_VERSIONS).
/// * `data`: The actual data being transported (e.g., a serialized list of supported versions).
///
/// # Returns
///
/// A single byte array containing the type and length of the extension, followed by the actual extension data.
pub fn pack(etype: u16, data: &[u8]) -> Bytes {
let mut ext = BytesMut::with_capacity(4 + data.len());
ext.put_u16(etype);
@@ -49,26 +80,18 @@ impl ExtensionBuilder {
}
}
/// Internal helper to pack and append an extension.
fn add_extension(&mut self, etype: u16, data: &[u8]) {
let ext = Extension::pack(etype, data);
self.payload.put_slice(&ext);
}
/// 0x?a?a - Randomized GREASE
pub fn grease(&mut self) {
let mut rng = rand::rng();
let rnd = Rng::next_u32(&mut rng) % 16;
let etype = GREASE_IDENTIFIERS[rnd as usize];
let rnd = rng.random_range(0..GREASE_IDENTIFIERS.len());
let etype = GREASE_IDENTIFIERS[rnd];
self.add_extension(etype, &[]);
}
/// Used for exact hex-matching of GREASE values
pub fn grease_fixed(&mut self, etype: u16) {
self.add_extension(etype, &[]);
}
/// 0x0000 - SNI
pub fn server_name(&mut self, host: &str) {
let host_bytes = host.as_bytes();
let host_len = host_bytes.len() as u16;
@@ -80,36 +103,49 @@ impl ExtensionBuilder {
data.put_u16(host_len);
data.put_slice(host_bytes);
self.add_extension(EXT_TYPE_SNI, &data);
self.add_extension(TlsExtensions::SNI, &data);
}
/// 0x0017 - Extended Master Secret
pub fn extended_main_secret(&mut self) {
self.add_extension(EXT_EXTENDED_MASTER_SECRET, &[]);
self.add_extension(TlsExtensions::EMS, &[]);
}
/// 0x000a - Supported Groups
pub fn supported_groups(&mut self, groups: TlsGroups) {
let mut data = BytesMut::with_capacity(2 + groups.0.len() * 2);
data.put_u16((groups.0.len() * 2) as u16);
for &g in groups.0 {
data.put_u16(g);
}
self.add_extension(EXT_SUPPORTED_GROUPS, &data);
self.add_extension(TlsExtensions::SUPPORTED_GROUPS, &data);
}
/// 0x000d - Signature Algorithms
pub fn signature_algorithms(&mut self, algs: TlsSignatures) {
let mut data = BytesMut::with_capacity(2 + algs.0.len() * 2);
data.put_u16((algs.0.len() * 2) as u16);
for &a in algs.0 {
data.put_u16(a);
}
self.add_extension(EXT_SIGNATURE_ALGORITHMS, &data);
self.add_extension(TlsExtensions::SIGNATURE_ALGORITHMS, &data);
}
pub fn supported_versions(&mut self, versions: TlsVersions) {
let mut data = BytesMut::with_capacity(1 + versions.0.len() * 2);
data.put_u8((versions.0.len() * 2) as u8);
for &v in versions.0 {
data.put_u16(v);
}
self.add_extension(TlsExtensions::SUPPORTED_VERSIONS, &data);
}
pub fn key_share(&mut self, pub_key: &[u8]) {
let mut data = BytesMut::with_capacity(38);
data.put_u16(34); // Total Key Share List Length
data.put_u16(TlsGroups::X25519);
data.put_u16(32); // Public Key length
data.put_slice(pub_key);
self.add_extension(TlsExtensions::KEY_SHARE, &data);
}
/// 0x44cd - ALPS (Application Settings)
/// Updated to support specific protocols
pub fn application_settings(&mut self, protocols: &[&str]) {
let mut data = BytesMut::new();
for proto in protocols {
@@ -118,81 +154,9 @@ impl ExtensionBuilder {
data.put_slice(p_bytes);
data.put_u16(0); // Empty settings per-protocol
}
self.add_extension(EXT_ALPS, &data);
self.add_extension(TlsExtensions::ALPS, &data);
}
/// 0x002b - Supported Versions
pub fn supported_versions(&mut self, versions: TlsVersions) {
let mut data = BytesMut::with_capacity(1 + versions.0.len() * 2);
data.put_u8((versions.0.len() * 2) as u8);
for &v in versions.0 {
data.put_u16(v);
}
self.add_extension(EXT_SUPPORTED_VERSIONS, &data);
}
/// 0x002d - PSK Key Exchange Modes
pub fn psk_key_exchange_modes(&mut self) {
let mut data = BytesMut::with_capacity(2);
data.put_u8(1);
data.put_u8(PSK_DHE_KE_MODE);
self.add_extension(EXT_PSK_KEY_EXCHANGE_MODES, &data);
}
/// 0x001b - Certificate Compression
pub fn compress_certificate(&mut self, algorithms: &[u16]) {
let mut data = BytesMut::with_capacity(1 + algorithms.len() * 2);
data.put_u8((algorithms.len() * 2) as u8);
for &alg in algorithms {
data.put_u16(alg);
}
self.add_extension(EXT_COMPRESS_CERTIFICATE, &data);
}
/// 0x0005 - Status Request
pub fn status_request(&mut self) {
let mut data = BytesMut::with_capacity(5);
data.put_u8(OCSP_STATUS_TYPE);
data.put_u16(0); // responder_id_list
data.put_u16(0); // request_extensions
self.add_extension(EXT_STATUS_REQUEST, &data);
}
/// 0x0033 - Key Share
/// Corrected: ClientHello KeyShare has a list length AND a group/key length
pub fn key_share(&mut self, public_key: &[u8]) {
let mut data = BytesMut::with_capacity(38);
data.put_u16(34); // Total Key Share List Length
data.put_u16(GROUP_X25519);
data.put_u16(32); // Public Key length
data.put_slice(public_key);
self.add_extension(EXT_KEY_SHARE, &data);
}
/// 0x000b - EC Point Formats
pub fn ec_point_formats(&mut self) {
let mut data = BytesMut::with_capacity(2);
data.put_u8(1);
data.put_u8(EC_POINT_FORMAT_UNCOMPRESSED);
self.add_extension(EXT_EC_POINT_FORMATS, &data);
}
/// 0x0012 - SCT
pub fn signed_certificate_timestamp(&mut self) {
self.add_extension(EXT_SIGNED_CERT_TIMESTAMP, &[]);
}
/// 0x0022 - Delegated Credentials
pub fn delegated_credential(&mut self, algs: TlsSignatures) {
let mut data = BytesMut::with_capacity(2 + algs.0.len() * 2);
data.put_u16((algs.0.len() * 2) as u16);
for &a in algs.0 {
data.put_u16(a);
}
self.add_extension(EXT_DELEGATED_CREDENTIAL, &data);
}
/// 0x0010 - ALPN
pub fn alpn(&mut self, protocols: &[&str]) {
let mut list_data = BytesMut::new();
for proto in protocols {
@@ -203,70 +167,110 @@ impl ExtensionBuilder {
let mut extension_data = BytesMut::new();
extension_data.put_u16(list_data.len() as u16);
extension_data.put_slice(&list_data);
self.add_extension(EXT_ALPN, &extension_data);
self.add_extension(TlsExtensions::ALPN, &extension_data);
}
pub fn psk_key_exchange_modes(&mut self) {
let mut data = BytesMut::with_capacity(2);
data.put_u8(1);
data.put_u8(PSK_DHE_KE_MODE);
self.add_extension(TlsExtensions::PSK_MODES, &data);
}
pub fn compress_certificate(&mut self, algorithms: &[u16]) {
let mut data = BytesMut::with_capacity(1 + algorithms.len() * 2);
data.put_u8((algorithms.len() * 2) as u8);
for &alg in algorithms {
data.put_u16(alg);
}
self.add_extension(TlsExtensions::COMPRESS_CERT, &data);
}
pub fn status_request(&mut self) {
let mut data = BytesMut::with_capacity(5);
data.put_u8(OCSP_STATUS_TYPE);
data.put_u16(0); // responder_id_list
data.put_u16(0); // request_extensions
self.add_extension(TlsExtensions::STATUS_REQUEST, &data);
}
pub fn ec_point_formats(&mut self) {
let mut data = BytesMut::with_capacity(2);
data.put_u8(1);
data.put_u8(0x00); // Uncompressed
self.add_extension(TlsExtensions::EC_POINT_FORMATS, &data);
}
pub fn signed_certificate_timestamp(&mut self) {
self.add_extension(TlsExtensions::SCT, &[]);
}
pub fn delegated_credential(&mut self, algs: TlsSignatures) {
let mut data = BytesMut::with_capacity(2 + algs.0.len() * 2);
data.put_u16((algs.0.len() * 2) as u16);
for &a in algs.0 {
data.put_u16(a);
}
self.add_extension(TlsExtensions::DELEGATED_CREDENTIAL, &data);
}
/// 0x0023 - Session Ticket
pub fn session_ticket(&mut self) {
self.add_extension(0x0023, &[]);
self.add_extension(TlsExtensions::SESSION_TICKET, &[]);
}
pub fn renegotiation_info(&mut self) {
self.add_extension(TlsExtensions::RENEGOTIATION_INFO, &[0x00]);
}
pub fn padding(&mut self, target_size: usize) {
// Текущий размер накопленной нагрузки
let current_size = self.payload.len();
// 4 байта резервируем под заголовок самого расширения (Type + Length)
if target_size > current_size + 4 {
let pad_len = target_size - current_size - 4;
let data = vec![0u8; pad_len];
// Используем pack, как и в других методах
let ext = Extension::pack(EXT_PADDING, &data);
self.payload.put_slice(&ext);
self.add_extension(TlsExtensions::PADDING, &data);
}
}
/// 0xff01 - Renegotiation Info
pub fn renegotiation_info(&mut self) {
self.add_extension(EXT_RENEGOTIATION_INFO, &[0x00]);
}
pub fn build(&mut self) -> Bytes {
self.payload.split().freeze()
}
pub fn apply_profile(&mut self, profile: &BrowserProfile, host: &str, pub_key: &[u8]) {
for &ext_id in profile.extension_order {
for &ext_id in &profile.extension_order {
match ext_id {
0x0000 => self.server_name(host),
0x000a => self.supported_groups(profile.groups),
0x000d => self.signature_algorithms(profile.signatures),
0x0010 => self.alpn(&["h2", "http/1.1"]),
0x0012 => self.signed_certificate_timestamp(),
0x0017 => self.extended_main_secret(),
0x001b => self.compress_certificate(&[CERT_COMPRESSION_BROTLI]),
0x0022 => self.delegated_credential(profile.delegated_signatures),
0x0023 => self.session_ticket(),
0x002b => self.supported_versions(profile.versions),
0x002d => self.psk_key_exchange_modes(),
0x0033 => self.key_share(pub_key),
0x44cd => self.application_settings(&["h2"]),
0x0005 => self.status_request(),
0x000b => self.ec_point_formats(),
0xff01 => self.renegotiation_info(),
// Padding logic
0x0015 => {
TlsExtensions::SNI => self.server_name(host),
TlsExtensions::SUPPORTED_GROUPS => self.supported_groups(profile.groups),
TlsExtensions::SIGNATURE_ALGORITHMS => {
self.signature_algorithms(profile.signatures)
}
TlsExtensions::ALPN => self.alpn(profile.alpn),
TlsExtensions::SCT => self.signed_certificate_timestamp(),
TlsExtensions::EMS => self.extended_main_secret(),
TlsExtensions::COMPRESS_CERT => {
self.compress_certificate(&[CERT_COMPRESSION_BROTLI])
}
TlsExtensions::DELEGATED_CREDENTIAL => {
self.delegated_credential(profile.delegated_signatures)
}
TlsExtensions::SESSION_TICKET => self.session_ticket(),
TlsExtensions::SUPPORTED_VERSIONS => self.supported_versions(profile.versions),
TlsExtensions::PSK_MODES => self.psk_key_exchange_modes(),
TlsExtensions::KEY_SHARE => self.key_share(pub_key),
TlsExtensions::ALPS => self.application_settings(&["h2"]),
TlsExtensions::STATUS_REQUEST => self.status_request(),
TlsExtensions::EC_POINT_FORMATS => self.ec_point_formats(),
TlsExtensions::RENEGOTIATION_INFO => self.renegotiation_info(),
TlsExtensions::PADDING => {
if profile.is_chromium {
// Standard Chromium behavior: pad to 512 bytes
self.padding(512);
} else {
// Non-chromium might use different logic or no padding
self.add_extension(0x0015, &[]);
self.add_extension(TlsExtensions::PADDING, &[]);
}
}
// Обработка GREASE по маске
id if (id & 0x0f0f) == 0x0a0a => self.grease(),
_ => {}
}
}
}
pub fn build(&mut self) -> Bytes {
self.payload.split().freeze()
}
}
+250
View File
@@ -0,0 +1,250 @@
use aead::rand_core::RngCore;
use bytes::{BufMut, Bytes, BytesMut};
use crate::{
tlseng::{
consts::{HANDSHAKE_TYPE_CLIENT_HELLO, HANDSHAKE_TYPE_SERVER_HELLO},
extension::ExtensionBuilder,
profile::BrowserProfile,
tls_record::TlsRecord,
types::{ContentType, HelloType, ProtocolVersion},
},
utils::u24::U24,
};
pub struct HelloHeader {
pub header_type: HelloType,
pub len: U24,
}
pub struct ClientHello {
/// The maximum version supported (legacy field in TLS 1.3)
pub version: ProtocolVersion,
/// 32 bytes of client-generated entropy
pub random: [u8; 32],
/// Legacy session ID (used in TLS 1.3 for middlebox compatibility)
pub session_id: Bytes,
/// List of cryptographic ciphers the client supports
pub cipher_suites: Vec<u16>,
/// Opaque block of extensions generated by ExtensionBuilder
pub extensions: Bytes,
}
impl ClientHello {
/// Serializes the ClientHello message into its wire format.
/// Includes the 4-byte Handshake header (Type + Length).
pub fn serialize(&self) -> Bytes {
let mut buf = BytesMut::with_capacity(512 + self.extensions.len());
// Handshake Type: 0x01 (ClientHello)
buf.put_u8(HANDSHAKE_TYPE_CLIENT_HELLO);
// Handshake Length Placeholder:
// Handshake messages use a 24-bit (3 byte) length field.
let length_pos = buf.len();
buf.put_bytes(0, 3);
// Protocol Version:
// For TLS 1.3, this is traditionally pinned to 0x0303 (TLS 1.2)
// to prevent middleboxes from dropping the packet.
buf.put_u16(0x0303);
buf.put_slice(&self.random);
// Legacy Session ID:
// Formatted as Length (1 byte) + ID bytes.
buf.put_u8(self.session_id.len() as u8);
buf.put_slice(&self.session_id);
// Cipher Suites:
// Formatted as Total Length (2 bytes) + Suite IDs (2 bytes each).
buf.put_u16((self.cipher_suites.len() * 2) as u16);
for &suite in &self.cipher_suites {
buf.put_u16(suite);
}
// Legacy Compression Methods:
// Always 1 byte of length (1) followed by the 'Null' method (0x00).
buf.put_u8(1);
buf.put_u8(0x00);
// Extensions Block:
// Formatted as Total Length (2 bytes) + Extension Data.
buf.put_u16(self.extensions.len() as u16);
buf.put_slice(&self.extensions);
// Patch the Handshake Length:
// We calculate the length of everything after the 3-byte placeholder.
let total_len = (buf.len() - length_pos - 3) as u32;
let len_bytes = total_len.to_be_bytes();
// Copy the last 3 bytes of the big-endian u32 into the placeholder.
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
let ext_len = self.extensions.len();
let total_handshake_len = (buf.len() - length_pos - 3) as u32;
tracing::debug!(
handshake_type = "ClientHello",
body_len = total_handshake_len,
extensions_len = ext_len,
total_bytes = buf.len(),
"Serialized Handshake message"
);
buf.freeze()
}
pub fn make_client_hello(
profile: &BrowserProfile,
host: &str,
public_key: &[u8; 32],
salt: [u8; 32],
) -> Bytes {
// 1. Key Exchange: Generate ECDH pair and get public key
// 2. Authentication: Generate 32 bytes for TLS Random
// [16 bytes entropy] + [16 bytes HMAC(timestamp)]
let mut tls_random = salt;
//todo
let auth_token = [0; 16]; //generate_auth_tag(&[]);
// tls_random[16..32].copy_from_slice(&auth_token);
// 3. Extensions: Build the extensions block using the profile
let mut ext_builder = ExtensionBuilder::new();
// Pass the public key into the KeyShare extension via apply_profile
ext_builder.apply_profile(profile, host, public_key);
let extensions_bytes = ext_builder.build();
let mut session_id = BytesMut::with_capacity(32);
session_id.put_slice(&[0u8; 32]);
// 4. Assemble ClientHello Handshake message
let client_hello = ClientHello {
version: ProtocolVersion::Tls12, // Legacy version for compatibility
random: tls_random,
session_id: session_id.freeze(), // Standard 32-byte session ID
cipher_suites: vec![0x1301, 0x1302, 0x1303], // TLS 1.3 suites
extensions: extensions_bytes,
};
// 5. Wrap ClientHello into a TLS Record
let record = TlsRecord::new(
ContentType::Handshake,
ProtocolVersion::Tls10,
client_hello.serialize(),
);
// Final result: Byte buffer ready for the wire
record.serialize()
}
}
pub struct ServerHello {
pub version: ProtocolVersion,
pub random: [u8; 32],
pub session_id: Bytes,
pub cipher_suite: u16,
pub extensions: BytesMut,
}
impl ServerHello {
/// Динамически создает ServerHello на основе данных из ClientHello
pub fn from_client_hello(
client_hello: &ClientHello,
server_public_key: &[u8],
salt: [u8; 32],
) -> Self {
let mut server_random = salt;
let selected_suite = client_hello
.cipher_suites
.first()
.cloned()
.unwrap_or(0x1301);
let mut extensions = BytesMut::new();
// --- Extension: Supported Versions (0x002b) ---
extensions.put_u16(0x002b);
extensions.put_u16(2);
extensions.put_u16(0x0304); // TLS 1.3
// --- Extension: Key Share (0x0033) ---
// Структура: Type(2) + Length(2) + Group(2) + KeyLength(2) + Key(N)
extensions.put_u16(0x0033);
extensions.put_u16(36); // Общая длина данных расширения (2+2+32)
extensions.put_u16(0x001d); // Named Group: x25519
extensions.put_u16(32); // Key Length
extensions.put_slice(server_public_key);
Self {
version: ProtocolVersion::Tls12,
random: server_random,
session_id: client_hello.session_id.clone(),
cipher_suite: selected_suite,
extensions,
}
}
/// Оборачивает в TLS Record, принимая ключ
pub fn make_server_hello(
client_hello: &ClientHello,
server_public_key: &[u8],
salt: [u8; 32],
) -> Bytes {
let server_hello = Self::from_client_hello(client_hello, server_public_key, salt);
let handshake_payload = server_hello.serialize();
let record = TlsRecord::new(
ContentType::Handshake,
ProtocolVersion::Tls12,
handshake_payload,
);
record.serialize()
}
/// Сериализация самого сообщения Handshake (Type + Len gth + Body)
pub fn serialize(&self) -> Bytes {
let mut buf = BytesMut::with_capacity(256 + self.extensions.len());
// 1. Handshake Type: 0x02 (ServerHello)
buf.put_u8(HANDSHAKE_TYPE_SERVER_HELLO);
// 2. Placeholder для длины (u24)
let length_pos = buf.len();
buf.put_bytes(0, 3);
// 3. Тело ServerHello
buf.put_u16(0x0303); // Legacy Version
buf.put_slice(&self.random);
// Session ID: Length (1 byte) + Data
buf.put_u8(self.session_id.len() as u8);
buf.put_slice(&self.session_id);
// Selected Cipher Suite
buf.put_u16(self.cipher_suite);
// Compression: всегда 0x00
buf.put_u8(0x00);
// Extensions: Length (2 bytes) + Data
buf.put_u16(self.extensions.len() as u16);
buf.put_slice(&self.extensions);
// 4. Патчим длину Handshake сообщения (u24)
let total_len = (buf.len() - length_pos - 3) as u32;
let len_bytes = total_len.to_be_bytes();
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
let total_handshake_len = (buf.len() - length_pos - 3) as u32;
let ext_len = self.extensions.len();
// ИНФОРМАТИВНЫЙ ЛОГ
tracing::debug!(
handshake_type = "ServerHello",
body_len = total_handshake_len,
extensions_len = ext_len,
total_bytes = buf.len(),
"Serialized Handshake message"
);
buf.freeze() // Превращаем BytesMut в Bytes
}
}
-117
View File
@@ -1,117 +0,0 @@
use bytes::{BufMut, Bytes, BytesMut};
use crate::{
crypto::hmac::generate_auth_tag,
tlseng::{
consts::HANDSHAKE_TYPE_CLIENT_HELLO,
extension::ExtensionBuilder,
profile::profile::BrowserProfile,
tls_record::TlsRecord,
types::{ContentType, ProtocolVersion},
},
};
pub struct ClientHello {
/// The maximum version supported (legacy field in TLS 1.3)
pub version: ProtocolVersion,
/// 32 bytes of client-generated entropy
pub random: [u8; 32],
/// Legacy session ID (used in TLS 1.3 for middlebox compatibility)
pub session_id: Bytes,
/// List of cryptographic ciphers the client supports
pub cipher_suites: Vec<u16>,
/// Opaque block of extensions generated by ExtensionBuilder
pub extensions: Bytes,
}
impl ClientHello {
/// Serializes the ClientHello message into its wire format.
/// Includes the 4-byte Handshake header (Type + Length).
pub fn serialize(&self) -> Bytes {
let mut buf = BytesMut::with_capacity(512 + self.extensions.len());
// Handshake Type: 0x01 (ClientHello)
buf.put_u8(HANDSHAKE_TYPE_CLIENT_HELLO);
// Handshake Length Placeholder:
// Handshake messages use a 24-bit (3 byte) length field.
let length_pos = buf.len();
buf.put_bytes(0, 3);
// Protocol Version:
// For TLS 1.3, this is traditionally pinned to 0x0303 (TLS 1.2)
// to prevent middleboxes from dropping the packet.
buf.put_u16(0x0303);
buf.put_slice(&self.random);
// Legacy Session ID:
// Formatted as Length (1 byte) + ID bytes.
buf.put_u8(self.session_id.len() as u8);
buf.put_slice(&self.session_id);
// Cipher Suites:
// Formatted as Total Length (2 bytes) + Suite IDs (2 bytes each).
buf.put_u16((self.cipher_suites.len() * 2) as u16);
for &suite in &self.cipher_suites {
buf.put_u16(suite);
}
// Legacy Compression Methods:
// Always 1 byte of length (1) followed by the 'Null' method (0x00).
buf.put_u8(1);
buf.put_u8(0x00);
// Extensions Block:
// Formatted as Total Length (2 bytes) + Extension Data.
buf.put_u16(self.extensions.len() as u16);
buf.put_slice(&self.extensions);
// Patch the Handshake Length:
// We calculate the length of everything after the 3-byte placeholder.
let total_len = (buf.len() - length_pos - 3) as u32;
let len_bytes = total_len.to_be_bytes();
// Copy the last 3 bytes of the big-endian u32 into the placeholder.
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
buf.freeze()
}
pub fn make_client_hello(profile: &BrowserProfile, host: &str) -> Bytes {
// 1. Key Exchange: Generate ECDH pair and get public key
// 2. Authentication: Generate 32 bytes for TLS Random
// [16 bytes entropy] + [16 bytes HMAC(timestamp)]
let mut tls_random = [0; 32];
let auth_token = generate_auth_tag(&[]);
tls_random[16..32].copy_from_slice(&auth_token);
// 3. Extensions: Build the extensions block using the profile
let mut ext_builder = ExtensionBuilder::new();
// Pass the public key into the KeyShare extension via apply_profile
ext_builder.apply_profile(profile, host, &[0; 32]);
let extensions_bytes = ext_builder.build();
let mut session_id = BytesMut::with_capacity(32);
session_id.put_slice(&[0u8; 32]);
// 4. Assemble ClientHello Handshake message
let client_hello = ClientHello {
version: ProtocolVersion::Tls12, // Legacy version for compatibility
random: tls_random,
session_id: session_id.freeze(), // Standard 32-byte session ID
cipher_suites: vec![0x1301, 0x1302, 0x1303], // TLS 1.3 suites
extensions: extensions_bytes,
};
// 5. Wrap ClientHello into a TLS Record
let record = TlsRecord::new(
ContentType::Handshake,
ProtocolVersion::Tls10,
client_hello.serialize(),
);
// Final result: Byte buffer ready for the wire
record.serialize()
}
}
@@ -1,6 +0,0 @@
use crate::{tlseng::types::HelloType, utils::u24::U24};
pub struct HelloHeader {
pub header_type: HelloType,
pub len: U24,
}
-3
View File
@@ -1,3 +0,0 @@
pub mod client_hello;
pub mod hello_header;
pub mod server_hello;
@@ -1,95 +0,0 @@
use bytes::{BufMut, Bytes, BytesMut};
use crate::tlseng::{
consts::HANDSHAKE_TYPE_SERVER_HELLO,
tls_record::TlsRecord,
types::{ContentType, ProtocolVersion},
};
pub struct ServerHello {
pub version: ProtocolVersion,
pub random: [u8; 32],
pub session_id: Bytes,
pub cipher_suite: u16,
pub extensions: BytesMut,
}
impl ServerHello {
pub fn make_mock_server_hello() -> Bytes {
// 1. Генерируем "рандом" (в реальном Nginx здесь случайные байты)
let mut mock_random = [0u8; 32];
mock_random[0..4].copy_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]); // Просто метка
// 2. Имитируем Session ID (в TLS 1.3 сервер часто эхоит ID клиента)
let mut session_id = BytesMut::with_capacity(32);
session_id.put_slice(&[0u8; 32]);
// 3. Подготавливаем минимальные расширения (пустые или базовые)
// Для TLS 1.3 тут обязательно должны быть Supported Versions (0x002b)
let mut mock_extensions = BytesMut::new();
// Extension: Supported Versions (TLS 1.3)
mock_extensions.put_u16(0x002b); // Type
mock_extensions.put_u16(2); // Length
mock_extensions.put_u16(0x0304); // Value: TLS 1.3
let server_hello = ServerHello {
version: ProtocolVersion::Tls12, // Legacy 0x0303
random: mock_random,
session_id: session_id.freeze(),
cipher_suite: 0x1301, // TLS_AES_128_GCM_SHA256
extensions: mock_extensions,
};
// 4. Сериализуем Handshake сообщение
let handshake_payload = server_hello.serialize();
// 5. Оборачиваем в TLS Record
// ContentType: Handshake (22)
// Version: TLS 1.0 (0x0301) для совместимости
let record = TlsRecord::new(
ContentType::Handshake,
ProtocolVersion::Tls10,
handshake_payload.freeze(), // Теперь payload — это Bytes
);
// Финальный результат: [Header(5 bytes)][Handshake(N bytes)]
record.serialize()
}
pub fn serialize(&self) -> BytesMut {
let mut buf = BytesMut::with_capacity(256 + self.extensions.len());
// 1. Handshake Type: 0x02 (ServerHello)
buf.put_u8(HANDSHAKE_TYPE_SERVER_HELLO);
// 2. Placeholder for u24 length
let length_pos = buf.len();
buf.put_bytes(0, 3);
// 3. body of ServerHello
buf.put_u16(ProtocolVersion::Tls12 as u16); // Legacy 0x0303
buf.put_slice(&self.random);
// Session ID
buf.put_u8(self.session_id.len() as u8);
buf.put_slice(&self.session_id);
// Selected Cipher Suite (only one)
buf.put_u16(self.cipher_suite);
// Compression: always 0x00
buf.put_u8(0x00);
// Extensions
buf.put_u16(self.extensions.len() as u16);
buf.put_slice(&self.extensions);
// 4. Patch length
let total_len = (buf.len() - length_pos - 3) as u32;
let len_bytes = total_len.to_be_bytes();
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
buf
}
}
+8 -4
View File
@@ -1,9 +1,13 @@
pub mod application_data;
pub mod consts;
use bytes::Bytes;
pub struct ApplicationData {
pub len: usize,
pub payload: Bytes,
}
mod consts;
pub mod extension;
pub mod handshake;
mod params;
pub mod profile;
pub mod tls_record;
pub mod types;
mod values;
-8
View File
@@ -1,8 +0,0 @@
#[derive(Clone, Copy)]
pub struct TlsGroups(pub &'static [u16]);
#[derive(Clone, Copy)]
pub struct TlsSignatures(pub &'static [u16]);
#[derive(Clone, Copy)]
pub struct TlsVersions(pub &'static [u16]);
+103
View File
@@ -0,0 +1,103 @@
use crate::tlseng::types::{ExtensionOrder, TlsGroups, TlsSignatures, TlsVersions};
/// Represents a complete TLS fingerprint profile for a specific browser.
///
/// This struct contains all the necessary information to generate a TLS
/// fingerprint for a specific browser, including the groups, signatures,
/// delegated signatures, versions, ALPN, and extension order.
pub struct BrowserProfile {
/// The name of the browser profile.
pub name: &'static str,
/// The groups supported by the browser.
pub groups: TlsGroups,
/// The signatures supported by the browser.
pub signatures: TlsSignatures,
/// The delegated signatures supported by the browser.
pub delegated_signatures: TlsSignatures,
/// The versions of TLS supported by the browser.
pub versions: TlsVersions,
/// The ALPN protocols supported by the browser.
pub alpn: &'static [&'static str],
/// The specific order of Extension IDs (e.g., [0x0000, 0x0017, ...])
pub extension_order: ExtensionOrder,
/// Whether the browser is based on Chromium.
pub is_chromium: bool,
}
impl BrowserProfile {
pub const CHROME_131: Self = Self {
name: "Chrome 131 (Windows)",
groups: TlsGroups::CHROMIUM,
signatures: TlsSignatures::BROWSER_STANDARD,
delegated_signatures: TlsSignatures::BROWSER_STANDARD,
versions: TlsVersions::TLS_13_ONLY,
alpn: &["h2", "http/1.1"],
extension_order: ExtensionOrder::CHROMIUM_131,
is_chromium: true,
};
pub const EDGE: Self = Self {
name: "Edge",
groups: TlsGroups::CHROMIUM, // Edge использует тот же набор, что и Chrome
signatures: TlsSignatures::BROWSER_STANDARD,
delegated_signatures: TlsSignatures::BROWSER_STANDARD,
versions: TlsVersions::MODERN,
alpn: &["h2", "http/1.1"],
extension_order: ExtensionOrder::EDGE_130,
is_chromium: true,
};
pub const DEFAULT: Self = Self::CHROME_131;
}
/// Represents a TLS configuration profile for the server side.
pub struct ServerProfile {
/// Имя профиля (например, "Modern-TLS-1.3-Only" или "Compatible-Nginx-Style")
pub name: &'static str,
/// Поддерживаемые версии TLS. Сервер выберет высшую общую с клиентом.
pub versions: TlsVersions,
/// Приоритетный список шифров (Cipher Suites).
/// В TLS 1.3 это обычно [0x1301, 0x1302, 0x1303].
pub cipher_suites: &'static [u16],
/// Группы для обмена ключами (Key Exchange Groups).
pub groups: TlsGroups,
/// Поддерживаемые алгоритмы подписи для аутентификации сервера.
pub signatures: TlsSignatures,
/// Протоколы ALPN, которые сервер готов подтвердить (h2, http/1.1).
pub alpn: &'static [&'static str],
/// Настройки сессий
pub session_tickets: bool,
/// Нужно ли форсировать порядок шифров сервера (Server Preference),
/// игнорируя порядок предпочтений клиента.
pub honor_cipher_order: bool,
}
impl ServerProfile {
pub const MODERN: Self = Self {
name: "Modern-Secure",
versions: TlsVersions::MODERN, // Допустим, у тебя есть такой хелпер
cipher_suites: &[
0x1301, // TLS_AES_128_GCM_SHA256
0x1302, // TLS_AES_256_GCM_SHA384
0x1303, // TLS_CHACHA20_POLY1305_SHA256
],
groups: TlsGroups::MODERN, // X25519, P-256
signatures: TlsSignatures::BROWSER_STANDARD,
alpn: &["h2", "http/1.1"],
session_tickets: true,
honor_cipher_order: true,
};
}
@@ -1,51 +0,0 @@
use crate::tlseng::consts::*;
use crate::tlseng::params::{TlsGroups, TlsSignatures};
use crate::tlseng::values::{
GROUP_SECP256R1, GROUP_SECP384R1, GROUP_X25519, SIG_ECDSA_SECP256R1_SHA256,
SIG_ECDSA_SECP384R1_SHA384, SIG_RSA_PKCS1_SHA256, SIG_RSA_PKCS1_SHA384, SIG_RSA_PKCS1_SHA512,
SIG_RSA_PSS_RSAE_SHA256, SIG_RSA_PSS_RSAE_SHA384, SIG_RSA_PSS_RSAE_SHA512,
};
pub const CHROME_GROUPS: TlsGroups =
TlsGroups(&[0xaaaa, GROUP_X25519, GROUP_SECP256R1, GROUP_SECP384R1]);
pub const CHROME_SIGNATURES: TlsSignatures = TlsSignatures(&[
SIG_ECDSA_SECP256R1_SHA256,
SIG_RSA_PSS_RSAE_SHA256,
SIG_RSA_PKCS1_SHA256,
SIG_ECDSA_SECP384R1_SHA384,
SIG_RSA_PSS_RSAE_SHA384,
SIG_RSA_PKCS1_SHA384,
SIG_RSA_PSS_RSAE_SHA512,
SIG_RSA_PKCS1_SHA512,
]);
pub const CHROME_DELEGATED_ALGS: TlsSignatures = TlsSignatures(&[
SIG_ECDSA_SECP256R1_SHA256,
SIG_RSA_PSS_RSAE_SHA256,
SIG_RSA_PKCS1_SHA256,
SIG_ECDSA_SECP384R1_SHA384,
SIG_RSA_PSS_RSAE_SHA384,
SIG_RSA_PKCS1_SHA384,
]);
pub const CHROME_ALPN_PROTOCOLS: &[&str] = &["h2", "http/1.1"];
pub const CHROMIUM_EXT_ORDER: &[u16] = &[
0xaaaa, // GREASE
EXT_TYPE_SNI, // 0x0000
EXT_EXTENDED_MASTER_SECRET, // 0x0017
EXT_SESSION_TICKET, // SessionTicket
EXT_SUPPORTED_GROUPS, // 0x000a
EXT_EC_POINT_FORMATS, // 0x000b
EXT_SIGNATURE_ALGORITHMS, // 0x000d
EXT_ALPN, // 0x0010
EXT_ALPS, // 0x44cd
EXT_STATUS_REQUEST, // 0x0005
EXT_KEY_SHARE, // 0x0033
EXT_SUPPORTED_VERSIONS, // 0x002b
EXT_PSK_KEY_EXCHANGE_MODES, // 0x002d
EXT_COMPRESS_CERTIFICATE, // 0x001b
EXT_SIGNED_CERT_TIMESTAMP, // 0x0012
EXT_DELEGATED_CREDENTIAL, // 0x0022
];
@@ -1,61 +0,0 @@
use crate::tlseng::consts::*;
use crate::tlseng::params::{TlsGroups, TlsSignatures};
use crate::tlseng::values::{
GROUP_SECP256R1, GROUP_SECP384R1, GROUP_X25519, SIG_ECDSA_SECP256R1_SHA256,
SIG_ECDSA_SECP384R1_SHA384, SIG_RSA_PKCS1_SHA256, SIG_RSA_PKCS1_SHA384, SIG_RSA_PKCS1_SHA512,
SIG_RSA_PSS_RSAE_SHA256, SIG_RSA_PSS_RSAE_SHA384, SIG_RSA_PSS_RSAE_SHA512,
};
// --- MICROSOFT EDGE ---
// Edge often mirrors Chrome exactly but sometimes removes specific
// experimental GREASE values or adds Windows-specific signature prefs.
pub const EDGE_GROUPS: TlsGroups = TlsGroups(&[
0x0a0a, // GREASE
GROUP_X25519,
GROUP_SECP256R1,
GROUP_SECP384R1,
]);
pub const EDGE_SIGNATURES: TlsSignatures = TlsSignatures(&[
SIG_ECDSA_SECP256R1_SHA256,
SIG_RSA_PSS_RSAE_SHA256,
SIG_RSA_PKCS1_SHA256,
SIG_ECDSA_SECP384R1_SHA384,
SIG_RSA_PSS_RSAE_SHA384,
SIG_RSA_PKCS1_SHA384,
SIG_RSA_PSS_RSAE_SHA512,
SIG_RSA_PKCS1_SHA512,
]);
pub const EDGE_DELEGATED_ALGS: TlsSignatures = TlsSignatures(&[
SIG_ECDSA_SECP256R1_SHA256,
SIG_RSA_PSS_RSAE_SHA256,
SIG_RSA_PKCS1_SHA256,
SIG_ECDSA_SECP384R1_SHA384,
SIG_RSA_PSS_RSAE_SHA384,
SIG_RSA_PKCS1_SHA384,
]);
pub const EDGE_ALPN_PROTOCOLS: &[&str] = &["h2", "http/1.1"];
// Microsoft Edge Extension Order (Chromium v130+)
pub const EDGE_EXT_ORDER: &[u16] = &[
0x1a1a, // GREASE
EXT_TYPE_SNI, // 0x0000
EXT_EXTENDED_MASTER_SECRET, // 0x0017
EXT_SESSION_TICKET, // 0x0023
EXT_SUPPORTED_GROUPS, // 0x000a
EXT_EC_POINT_FORMATS, // 0x000b
EXT_SIGNATURE_ALGORITHMS, // 0x000d
EXT_ALPN, // 0x0010
EXT_ALPS, // 0x44cd
EXT_STATUS_REQUEST, // 0x0005
EXT_KEY_SHARE, // 0x0033
EXT_SUPPORTED_VERSIONS, // 0x002b
EXT_PSK_KEY_EXCHANGE_MODES, // 0x002d
EXT_COMPRESS_CERTIFICATE, // 0x001b
EXT_SIGNED_CERT_TIMESTAMP, // 0x0012
EXT_DELEGATED_CREDENTIAL, // 0x0022
EXT_PADDING, // 0x0015
0x3a3a, // GREASE
];
-3
View File
@@ -1,3 +0,0 @@
pub mod chrome_groups;
pub mod edge_groups;
pub mod shared;
@@ -1,3 +0,0 @@
use crate::tlseng::params::TlsVersions;
pub const MODERN_VERSIONS: TlsVersions = TlsVersions(&[0x0304, 0x0303]);
-3
View File
@@ -1,3 +0,0 @@
mod groups;
pub mod profile;
pub mod versions;
-14
View File
@@ -1,14 +0,0 @@
use crate::tlseng::params::{TlsGroups, TlsSignatures, TlsVersions};
/// Represents a complete TLS fingerprint profile for a specific browser.
pub struct BrowserProfile {
pub name: &'static str,
pub groups: TlsGroups,
pub signatures: TlsSignatures,
pub delegated_signatures: TlsSignatures,
pub versions: TlsVersions,
pub alpn: &'static [&'static str],
/// The specific order of Extension IDs (e.g., [0x0000, 0x0017, ...])
pub extension_order: &'static [u16],
pub is_chromium: bool,
}
-45
View File
@@ -1,45 +0,0 @@
use crate::tlseng::{
params::TlsVersions,
profile::{
groups::{
chrome_groups::{
CHROME_ALPN_PROTOCOLS, CHROME_DELEGATED_ALGS, CHROME_GROUPS, CHROME_SIGNATURES,
CHROMIUM_EXT_ORDER,
},
edge_groups::{
EDGE_ALPN_PROTOCOLS, EDGE_DELEGATED_ALGS, EDGE_EXT_ORDER, EDGE_GROUPS,
EDGE_SIGNATURES,
},
shared::MODERN_VERSIONS,
},
profile::BrowserProfile,
},
};
// --- Versions ---
pub const TLS_13_ONLY: TlsVersions = TlsVersions(&[0x0304]);
// --- CHROME 131 PROFILE ---
pub const CHROME_131: BrowserProfile = BrowserProfile {
name: "Chrome 131 (Windows)",
groups: CHROME_GROUPS,
signatures: CHROME_SIGNATURES,
alpn: CHROME_ALPN_PROTOCOLS,
delegated_signatures: CHROME_DELEGATED_ALGS,
versions: TLS_13_ONLY,
extension_order: CHROMIUM_EXT_ORDER,
is_chromium: true,
};
// --- FIREFOX 133 PROFILE (Example) ---
// Note: Firefox uses different groups and no ALPS
pub const EDGE_PROFILE: BrowserProfile = BrowserProfile {
name: "Edge",
groups: EDGE_GROUPS,
signatures: EDGE_SIGNATURES, // Usually identical to Chrome
alpn: EDGE_ALPN_PROTOCOLS,
delegated_signatures: EDGE_DELEGATED_ALGS,
versions: MODERN_VERSIONS,
extension_order: EDGE_EXT_ORDER,
is_chromium: true,
};
+22
View File
@@ -17,6 +17,17 @@ pub struct TlsRecord {
}
impl TlsRecord {
/// Creates a new TLS Record Layer from the given parameters.
///
/// # Arguments
///
/// * `content_type`: The type of data contained (Handshake, ApplicationData, etc.).
/// * `version`: The record layer version (usually 0x0301 for legacy support).
/// * `payload`: The actual data being transported (e.g., a serialized ClientHello).
///
/// # Returns
///
/// A new TLS Record Layer structure with the given parameters.
pub fn new(content_type: ContentType, version: ProtocolVersion, payload: Bytes) -> Self {
Self {
content_type,
@@ -38,4 +49,15 @@ impl TlsRecord {
buf.freeze()
}
pub fn build_application_data(payload: Bytes) -> Bytes {
tracing::trace!(payload_len = payload.len(), "Building TlsRecord from Bytes");
let record = Self::new(
ContentType::ApplicationData,
ProtocolVersion::Tls12,
payload,
);
record.serialize()
}
}
+200 -3
View File
@@ -1,7 +1,7 @@
/// TLS Content Types as defined in the TLS Record Protocol.
/// These identify what is contained within the TLS Record payload.
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContentType {
/// Handshake messages (e.g., ClientHello, ServerHello)
Handshake = 0x16,
@@ -13,6 +13,14 @@ pub enum ContentType {
impl TryFrom<u8> for ContentType {
type Error = &'static str;
/// Attempts to convert a given `u8` value into a `ContentType`.
///
/// Returns `Ok(ContentType)` if the conversion is successful, and `Err(&str)` if not.
///
/// # Examples
///
///
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x16 => Ok(ContentType::Handshake),
@@ -23,8 +31,14 @@ impl TryFrom<u8> for ContentType {
}
}
/// Known TLS protocol versions.
/// Note: TLS 1.3 often uses legacy versions in headers for compatibility.
///
/// Represents known TLS protocol versions.
///
/// Note that TLS 1.3 often uses legacy versions in headers for compatibility.
///
/// # Examples
///
///
#[repr(u16)]
#[derive(Copy, Clone, Debug)]
pub enum ProtocolVersion {
@@ -35,23 +49,67 @@ pub enum ProtocolVersion {
impl TryFrom<u16> for ProtocolVersion {
type Error = &'static str;
/// Attempts to convert a given `u16` value into a `ProtocolVersion`.
///
/// Returns `Ok(ProtocolVersion)` if the conversion is successful, and `Err(&str)` if not.
///
/// # Examples
///
///
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
// TLS 1.0 (RFC 2246)
0x0301 => Ok(ProtocolVersion::Tls10),
// TLS 1.2 (RFC 4346)
0x0303 => Ok(ProtocolVersion::Tls12),
// TLS 1.3 (draft-ietf-tls-tls13-28)
0x0304 => Ok(ProtocolVersion::Tls13),
_ => Err("This is not Protocol Version"),
}
}
}
/// Hello types as defined in the TLS Handshake Protocol.
///
/// These identify the type of the message in the TLS Handshake protocol.
///
/// # Examples
///
///
/// # Notes
///
/// The values of these enum variants are used as the first byte of the TLS
/// Handshake protocol message.
///
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum HelloType {
/// Client hello message type
Client = 0x01,
/// Server hello message type
Server = 0x02,
}
/// Attempts to convert a given `u8` value into a `HelloType`.
///
/// Returns `Ok(HelloType)` if the conversion is successful, and `Err(&str)` if not.
///
/// # Examples
///
///
/// # Notes
///
/// This function is used to convert raw bytes into a `HelloType`.
/// It is used in the `HelloHeader` parsing process.
impl TryFrom<u8> for HelloType {
type Error = &'static str;
/// Attempts to convert a given `u8` value into a `HelloType`.
///
/// Returns `Ok(HelloType)` if the conversion is successful, and `Err(&str)` if not.
///
/// # Examples
///
///
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(HelloType::Client),
@@ -60,3 +118,142 @@ impl TryFrom<u8> for HelloType {
}
}
}
/// A collection of supported TLS groups.
///
/// This is a list of 16-bit group identifiers that the client supports.
/// The server will select one of these groups to use for the key exchange.
#[derive(Clone, Copy)]
pub struct TlsGroups(pub &'static [u16]);
impl TlsGroups {
pub const X25519: u16 = 0x001d;
pub const SECP256R1: u16 = 0x0017;
pub const SECP384R1: u16 = 0x0018;
/// Стандартный набор для Chrome/Edge (X25519 + P-256)
pub const CHROMIUM: Self = Self(&[Self::X25519, Self::SECP256R1, Self::SECP384R1]);
/// Набор "только современные кривые"
pub const MODERN: Self = Self(&[Self::X25519, Self::SECP256R1]);
}
/// A collection of supported TLS signature algorithms.
///
/// This is a list of 16-bit signature algorithm identifiers that the client supports.
/// The server will select one of these algorithms to use for the digital signature.
#[derive(Clone, Copy)]
pub struct TlsSignatures(pub &'static [u16]);
impl TlsSignatures {
pub const ECDSA_SECP256R1_SHA256: u16 = 0x0403;
pub const RSA_PSS_RSAE_SHA256: u16 = 0x0804;
pub const RSA_PKCS1_SHA256: u16 = 0x0401;
pub const ECDSA_SECP384R1_SHA384: u16 = 0x0503;
pub const RSA_PSS_RSAE_SHA384: u16 = 0x0805;
pub const RSA_PKCS1_SHA384: u16 = 0x0501;
pub const RSA_PSS_RSAE_SHA512: u16 = 0x0806;
pub const RSA_PKCS1_SHA512: u16 = 0x0601;
/// Типичный набор для современных браузеров
pub const BROWSER_STANDARD: Self = Self(&[
Self::ECDSA_SECP256R1_SHA256,
Self::RSA_PSS_RSAE_SHA256,
Self::RSA_PKCS1_SHA256,
Self::ECDSA_SECP384R1_SHA384,
Self::RSA_PSS_RSAE_SHA384,
Self::RSA_PKCS1_SHA384,
Self::RSA_PSS_RSAE_SHA512,
]);
}
/// A collection of supported TLS protocol versions.
///
/// This is a list of 16-bit protocol version identifiers that the client supports.
/// The server will select one of these versions to use for the TLS connection.
#[derive(Clone, Copy)]
pub struct TlsVersions(pub &'static [u16]);
impl TlsVersions {
pub const TLS_1_3: u16 = 0x0304;
pub const TLS_1_2: u16 = 0x0303;
pub const TLS_13_ONLY: Self = Self(&[Self::TLS_1_3]);
pub const MODERN: Self = Self(&[Self::TLS_1_3, Self::TLS_1_2]);
}
pub struct TlsExtensions;
impl TlsExtensions {
pub const SNI: u16 = 0x0000;
pub const STATUS_REQUEST: u16 = 0x0005;
pub const SUPPORTED_GROUPS: u16 = 0x000a;
pub const EC_POINT_FORMATS: u16 = 0x000b;
pub const SIGNATURE_ALGORITHMS: u16 = 0x000d;
pub const ALPN: u16 = 0x0010;
pub const SCT: u16 = 0x0012;
pub const PADDING: u16 = 0x0015;
pub const EMS: u16 = 0x0017;
pub const COMPRESS_CERT: u16 = 0x001b;
pub const DELEGATED_CREDENTIAL: u16 = 0x0022;
pub const SESSION_TICKET: u16 = 0x0023;
pub const SUPPORTED_VERSIONS: u16 = 0x002b;
pub const PSK_MODES: u16 = 0x002d;
pub const KEY_SHARE: u16 = 0x0033;
pub const ALPS: u16 = 0x44cd;
pub const RENEGOTIATION_INFO: u16 = 0xff01;
}
#[derive(Clone, Copy)]
pub struct ExtensionOrder(pub &'static [u16]);
impl<'a> IntoIterator for &'a ExtensionOrder {
type Item = &'a u16;
type IntoIter = std::slice::Iter<'a, u16>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl ExtensionOrder {
pub const CHROMIUM_131: Self = Self(&[
0xaaaa, // GREASE
TlsExtensions::SNI,
TlsExtensions::EMS,
TlsExtensions::SESSION_TICKET,
TlsExtensions::SUPPORTED_GROUPS,
TlsExtensions::EC_POINT_FORMATS,
TlsExtensions::SIGNATURE_ALGORITHMS,
TlsExtensions::ALPN,
TlsExtensions::ALPS,
TlsExtensions::STATUS_REQUEST,
TlsExtensions::KEY_SHARE,
TlsExtensions::SUPPORTED_VERSIONS,
TlsExtensions::PSK_MODES,
TlsExtensions::COMPRESS_CERT,
TlsExtensions::SCT,
TlsExtensions::DELEGATED_CREDENTIAL,
]);
pub const EDGE_130: Self = Self(&[
0x1a1a, // GREASE
TlsExtensions::SNI,
TlsExtensions::EMS,
TlsExtensions::SESSION_TICKET,
TlsExtensions::SUPPORTED_GROUPS,
TlsExtensions::EC_POINT_FORMATS,
TlsExtensions::SIGNATURE_ALGORITHMS,
TlsExtensions::ALPN,
TlsExtensions::ALPS,
TlsExtensions::STATUS_REQUEST,
TlsExtensions::KEY_SHARE,
TlsExtensions::SUPPORTED_VERSIONS,
TlsExtensions::PSK_MODES,
TlsExtensions::COMPRESS_CERT,
TlsExtensions::SCT,
TlsExtensions::DELEGATED_CREDENTIAL,
TlsExtensions::PADDING,
0x3a3a, // GREASE
]);
}
-21
View File
@@ -1,21 +0,0 @@
pub const TYPE_HOST_NAME: u8 = 0x00;
pub const GROUP_X25519: u16 = 0x001d;
pub const GROUP_SECP256R1: u16 = 0x0017;
pub const GROUP_SECP384R1: u16 = 0x0018;
// Signature Algorithms (Signature Schemes)
pub const SIG_ECDSA_SECP256R1_SHA256: u16 = 0x0403;
pub const SIG_RSA_PSS_RSAE_SHA256: u16 = 0x0804;
pub const SIG_RSA_PKCS1_SHA256: u16 = 0x0401;
pub const SIG_ECDSA_SECP384R1_SHA384: u16 = 0x0503;
pub const SIG_RSA_PSS_RSAE_SHA384: u16 = 0x0805;
pub const SIG_RSA_PKCS1_SHA384: u16 = 0x0501;
pub const SIG_RSA_PSS_RSAE_SHA512: u16 = 0x0806;
pub const SIG_RSA_PKCS1_SHA512: u16 = 0x0601;
// Versions & Modes
pub const PSK_DHE_KE_MODE: u8 = 0x01;
pub const CERT_COMPRESSION_BROTLI: u16 = 0x0002;
pub const OCSP_STATUS_TYPE: u8 = 0x01;
pub const EC_POINT_FORMAT_UNCOMPRESSED: u8 = 0x00;
+33 -1
View File
@@ -4,17 +4,50 @@ use bytes::Buf;
pub struct U24([u8; 3]);
impl U24 {
/// Creates a new `U24` from a given `u32` value.
///
/// The `u32` value is converted to big-endian byte order and then split into
/// three bytes, which are used to initialize the `U24`.
///
/// # Examples
///
///
pub fn from_u32(value: u32) -> Self {
let b = value.to_be_bytes();
U24([b[1], b[2], b[3]])
}
/// Converts the `U24` into a `u32` value.
///
/// The `U24` is converted from big-endian byte order to a `u32` value.
///
/// # Examples
///
///
pub fn to_u32(&self) -> u32 {
u32::from_be_bytes([0, self.0[0], self.0[1], self.0[2]])
}
/// Converts a slice of three bytes into a `u32` value.
///
/// The slice is interpreted as a big-endian byte order, and the resulting `u32` value is
/// computed by combining the three bytes into a single 32-bit value.
pub fn from_slice(slice: &[u8]) -> u32 {
u32::from_be_bytes([0, slice[0], slice[1], slice[2]])
}
}
pub trait BufExt: Buf {
/// Reads three bytes from the buffer and returns a `u32` value.
///
/// The bytes are read in big-endian byte order and then combined into a single `u32` value.
///
/// # Examples
///
///
/// let mut buf = BytesMut::from(&[0x12, 0x34, 0x56][..]);
/// let val = buf.get_u24();
/// assert_eq!(val, 0x123456);
fn get_u24(&mut self) -> u32 {
let b1 = self.get_u8() as u32;
let b2 = self.get_u8() as u32;
@@ -23,5 +56,4 @@ pub trait BufExt: Buf {
}
}
// Реализуем этот трейт для всего, что поддерживает Buf (включая BytesMut)
impl<T: Buf> BufExt for T {}