project strucute refactoring

This commit is contained in:
2026-03-25 20:08:13 +07:00
parent a213c01158
commit 037edaa7cd
18 changed files with 694 additions and 565 deletions
+2 -3
View File
@@ -1,12 +1,11 @@
use crate::crypto::session::SessionKeys;
use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError};
use crate::nrxp::parser::parser::Parser;
use crate::parser::Parser;
use crate::tlseng::extension::ExtensionStack;
use crate::tlseng::handshake::{ClientHello, HelloHeader, ServerHello};
use crate::tlseng::profile::{BrowserProfile, ServerProfile};
use crate::tlseng::tls_record::TlsRecord;
use crate::tlseng::tls_record::{ApplicationData, TlsRecord};
use crate::tlseng::types::{ContentType, HelloType};
use crate::tlseng::ApplicationData;
use bytes::{Bytes, BytesMut};
pub trait TlsInterceptor {
+1 -1
View File
@@ -6,7 +6,7 @@ use crate::crypto::session::SessionKeys;
use crate::nrxp::codec::bridge::TlsBridge;
use crate::nrxp::codec::frame::{Frame, FrameHeader, FrameType, Padding};
use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError};
use crate::nrxp::parser::parser::Parser;
use crate::parser::Parser;
use crate::tlseng::profile::{BrowserProfile, ServerProfile};
pub struct Codec {
+90 -2
View File
@@ -1,8 +1,8 @@
use bytes::{BufMut, Bytes, BytesMut};
use bytes::{Buf, BufMut, Bytes, BytesMut};
use rand::Rng;
use crate::nrxp::codec::MAX_PADDING_SIZE;
use crate::{nrxp::codec::MAX_PADDING_SIZE, parser::Parser};
pub struct Padding {
pub len: u16,
@@ -75,3 +75,91 @@ impl Frame {
buf
}
}
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 {
if bytes.len() < FRAME_HEADER_SIZE as usize {
return false;
}
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;
netrunner_logger::debug!(
"CAN_PARSE: p_len={}, pad_len={}, total_needed={}, have={}",
p_len,
pad_len,
25 + p_len + pad_len,
bytes.len()
);
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);
}
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;
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,
}))
}
}
+118 -2
View File
@@ -1,8 +1,8 @@
use std::fmt;
use bytes::{BufMut, BytesMut};
use bytes::{Buf, BufMut, BytesMut};
use crate::nrxp::parser::parser::Parser;
use crate::parser::Parser;
pub const SOCKS5_VERSION: u8 = 0x05;
pub const REPLY_SUCCESS: u8 = 0x00;
@@ -227,3 +227,119 @@ impl SocksTarget {
}
}
}
impl Parser for SocksTarget {
type Error = String;
fn can_parse(bytes: &BytesMut) -> bool {
if bytes.len() < 4 {
return false;
}
let atyp = bytes[3];
match atyp {
ATYP_IPV4 => bytes.len() >= 4 + 4 + 2,
ATYP_IPV6 => bytes.len() >= 4 + 16 + 2,
ATYP_DOMAIN => {
if bytes.len() < 5 {
return false;
}
let domain_len = bytes[4] as usize;
bytes.len() >= 4 + 1 + domain_len + 2
}
_ => false,
}
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if !Self::can_parse(bytes) {
return Ok(None);
}
let atyp = bytes[3];
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(4);
let addr = match atyp {
ATYP_IPV4 => {
let octets: [u8; 4] = packet.split_to(4)[..].try_into().unwrap();
let port = packet.get_u16();
TargetAddress::Ipv4(octets.into(), port)
}
ATYP_DOMAIN => {
let len = packet.get_u8() as usize;
let domain_bytes = packet.split_to(len);
let domain = String::from_utf8(domain_bytes.to_vec())
.map_err(|_| "Invalid UTF-8 domain".to_string())?;
let port = packet.get_u16();
TargetAddress::Domain(domain, port)
}
ATYP_IPV6 => {
let octets: [u8; 16] = packet.split_to(16)[..].try_into().unwrap();
let port = packet.get_u16();
TargetAddress::Ipv6(octets.into(), port)
}
_ => unreachable!(),
};
Ok(Some(SocksTarget { addr }))
}
}
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 {
if bytes.len() >= SOCKS5_MIN_HEADER && SocksTarget::can_parse(bytes) {
return true;
}
return true;
}
false
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if bytes.len() < 2 || bytes[0] != SOCKS5_VERSION {
return Ok(None);
}
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 }));
}
}
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)
}
}
-1
View File
@@ -1,3 +1,2 @@
pub mod codec;
pub mod errors;
pub mod parser;
-6
View File
@@ -1,6 +0,0 @@
mod netr;
pub mod parser;
mod socks;
mod tls;
use parser::Parser;
-94
View File
@@ -1,94 +0,0 @@
use bytes::{Buf, BytesMut};
use crate::nrxp::{
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 {
if bytes.len() < FRAME_HEADER_SIZE as usize {
return false;
}
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;
netrunner_logger::debug!(
"CAN_PARSE: p_len={}, pad_len={}, total_needed={}, have={}",
p_len,
pad_len,
25 + p_len + pad_len,
bytes.len()
);
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);
}
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;
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,
}))
}
}
-9
View File
@@ -1,9 +0,0 @@
use bytes::BytesMut;
pub trait Parser {
type Error;
fn can_parse(bytes: &BytesMut) -> bool;
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error>
where
Self: Sized;
}
-119
View File
@@ -1,119 +0,0 @@
use bytes::{Buf, BytesMut};
use crate::nrxp::{codec::socks::*, parser::parser::Parser};
impl Parser for SocksTarget {
type Error = String;
fn can_parse(bytes: &BytesMut) -> bool {
if bytes.len() < 4 {
return false;
}
let atyp = bytes[3];
match atyp {
ATYP_IPV4 => bytes.len() >= 4 + 4 + 2,
ATYP_IPV6 => bytes.len() >= 4 + 16 + 2,
ATYP_DOMAIN => {
if bytes.len() < 5 {
return false;
}
let domain_len = bytes[4] as usize;
bytes.len() >= 4 + 1 + domain_len + 2
}
_ => false,
}
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if !Self::can_parse(bytes) {
return Ok(None);
}
let atyp = bytes[3];
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(4);
let addr = match atyp {
ATYP_IPV4 => {
let octets: [u8; 4] = packet.split_to(4)[..].try_into().unwrap();
let port = packet.get_u16();
TargetAddress::Ipv4(octets.into(), port)
}
ATYP_DOMAIN => {
let len = packet.get_u8() as usize;
let domain_bytes = packet.split_to(len);
let domain = String::from_utf8(domain_bytes.to_vec())
.map_err(|_| "Invalid UTF-8 domain".to_string())?;
let port = packet.get_u16();
TargetAddress::Domain(domain, port)
}
ATYP_IPV6 => {
let octets: [u8; 16] = packet.split_to(16)[..].try_into().unwrap();
let port = packet.get_u16();
TargetAddress::Ipv6(octets.into(), port)
}
_ => unreachable!(),
};
Ok(Some(SocksTarget { addr }))
}
}
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 {
if bytes.len() >= SOCKS5_MIN_HEADER && SocksTarget::can_parse(bytes) {
return true;
}
return true;
}
false
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if bytes.len() < 2 || bytes[0] != SOCKS5_VERSION {
return Ok(None);
}
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 }));
}
}
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)
}
}
-313
View File
@@ -1,313 +0,0 @@
use crate::{
nrxp::{
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};
impl Parser for TlsRecord {
type Error = TlsError;
fn can_parse(bytes: &BytesMut) -> bool {
if bytes.len() < 5 {
return false;
}
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;
}
let record_len = u16::from_be_bytes([bytes[3], bytes[4]]) as usize;
if content_type == ContentType::ApplicationData as u8 {
if record_len < 17 {
return false;
}
}
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)))
}
}
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),
}))
}
}
impl Parser for ClientHello {
type Error = TlsError;
fn can_parse(bytes: &BytesMut) -> bool {
let mut reader = &bytes[..];
if reader.len() < 35 {
return false;
}
reader.advance(34);
let sid_len = reader[0] as usize;
reader.advance(1);
if reader.len() < sid_len + 2 {
return false;
}
reader.advance(sid_len);
let ciphers_len = u16::from_be_bytes([reader[0], reader[1]]) as usize;
reader.advance(2);
if reader.len() < ciphers_len + 1 {
return false;
}
reader.advance(ciphers_len);
let comp_len = reader[0] as usize;
reader.advance(1);
if reader.len() < comp_len {
return false;
}
reader.advance(comp_len);
if reader.len() >= 2 {
let ext_len = u16::from_be_bytes([reader[0], reader[1]]) as usize;
reader.advance(2);
if reader.len() < ext_len {
return false;
}
}
true
}
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
if !Self::can_parse(bytes) {
return Ok(None);
}
let _version = ProtocolVersion::try_from(bytes.get_u16())
.map_err(|e| TlsError::new(ErrorStage::Tls(e), ErrorAction::Drop, Bytes::new()))?;
let mut random = [0u8; 32];
bytes.copy_to_slice(&mut random);
let sid_len = bytes.get_u8() as usize;
let session_id = bytes.split_to(sid_len).freeze();
let c_len = bytes.get_u16() as usize;
let mut cipher_suites = Vec::with_capacity(c_len / 2);
let mut ciphers_data = bytes.split_to(c_len);
while ciphers_data.has_remaining() {
cipher_suites.push(ciphers_data.get_u16());
}
let cmp_len = bytes.get_u8() as usize;
bytes.advance(cmp_len);
let extensions = if bytes.remaining() >= 2 {
let ext_len = bytes.get_u16() as usize;
bytes.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;
if bytes.len() < offset + 1 {
return false;
}
let session_id_len = bytes[offset] as usize;
offset += 1 + session_id_len;
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> {
let mut offset = 34;
if bytes.len() < offset + 1 {
return Ok(None);
}
let session_id_len = bytes[offset] as usize;
offset += 1 + session_id_len;
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;
}
if bytes.len() < offset {
return Ok(None);
}
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);
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,
}))
}
}
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;
}
if offset > data_len {
return Ok(None);
}
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 }))
}
}