project strucute refactoring
This commit is contained in:
+3
-1
@@ -1,5 +1,7 @@
|
|||||||
mod crypto;
|
mod crypto;
|
||||||
pub mod nrxp;
|
|
||||||
pub mod net;
|
pub mod net;
|
||||||
|
pub mod nrxp;
|
||||||
|
pub mod parser;
|
||||||
|
pub mod rawcast;
|
||||||
mod tlseng;
|
mod tlseng;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
net::{
|
||||||
|
connection::{
|
||||||
|
bridge::run_tcp_bridge, engine::TunnelEngine, handler::StreamHandler, muxer::Muxer,
|
||||||
|
},
|
||||||
|
network::NetworkConfig,
|
||||||
|
},
|
||||||
nrxp::{
|
nrxp::{
|
||||||
codec::{
|
codec::{
|
||||||
codec::Codec,
|
codec::Codec,
|
||||||
@@ -6,14 +12,8 @@ use crate::{
|
|||||||
socks::{SocksReply, SocksRequest},
|
socks::{SocksReply, SocksRequest},
|
||||||
},
|
},
|
||||||
errors::ErrorAction,
|
errors::ErrorAction,
|
||||||
parser::parser::Parser,
|
|
||||||
},
|
|
||||||
net::{
|
|
||||||
connection::{
|
|
||||||
bridge::run_tcp_bridge, engine::TunnelEngine, handler::StreamHandler, muxer::Muxer,
|
|
||||||
},
|
|
||||||
network::NetworkConfig,
|
|
||||||
},
|
},
|
||||||
|
parser::Parser,
|
||||||
tlseng::profile::BrowserProfile,
|
tlseng::profile::BrowserProfile,
|
||||||
};
|
};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
use crate::crypto::session::SessionKeys;
|
use crate::crypto::session::SessionKeys;
|
||||||
use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError};
|
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::extension::ExtensionStack;
|
||||||
use crate::tlseng::handshake::{ClientHello, HelloHeader, ServerHello};
|
use crate::tlseng::handshake::{ClientHello, HelloHeader, ServerHello};
|
||||||
use crate::tlseng::profile::{BrowserProfile, ServerProfile};
|
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::types::{ContentType, HelloType};
|
||||||
use crate::tlseng::ApplicationData;
|
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
|
|
||||||
pub trait TlsInterceptor {
|
pub trait TlsInterceptor {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::crypto::session::SessionKeys;
|
|||||||
use crate::nrxp::codec::bridge::TlsBridge;
|
use crate::nrxp::codec::bridge::TlsBridge;
|
||||||
use crate::nrxp::codec::frame::{Frame, FrameHeader, FrameType, Padding};
|
use crate::nrxp::codec::frame::{Frame, FrameHeader, FrameType, Padding};
|
||||||
use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError};
|
use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError};
|
||||||
use crate::nrxp::parser::parser::Parser;
|
use crate::parser::Parser;
|
||||||
use crate::tlseng::profile::{BrowserProfile, ServerProfile};
|
use crate::tlseng::profile::{BrowserProfile, ServerProfile};
|
||||||
|
|
||||||
pub struct Codec {
|
pub struct Codec {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use crate::nrxp::codec::MAX_PADDING_SIZE;
|
use crate::{nrxp::codec::MAX_PADDING_SIZE, parser::Parser};
|
||||||
|
|
||||||
pub struct Padding {
|
pub struct Padding {
|
||||||
pub len: u16,
|
pub len: u16,
|
||||||
@@ -75,3 +75,91 @@ impl Frame {
|
|||||||
buf
|
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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::fmt;
|
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 SOCKS5_VERSION: u8 = 0x05;
|
||||||
pub const REPLY_SUCCESS: u8 = 0x00;
|
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,3 +1,2 @@
|
|||||||
pub mod codec;
|
pub mod codec;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod parser;
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
mod netr;
|
|
||||||
pub mod parser;
|
|
||||||
mod socks;
|
|
||||||
mod tls;
|
|
||||||
|
|
||||||
use parser::Parser;
|
|
||||||
@@ -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,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
use crate::parser::Parser;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum LocalProtocol {
|
||||||
|
Tcp = 0x01,
|
||||||
|
Udp = 0x02,
|
||||||
|
Icmp = 0x03,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum RawCastEvent {
|
||||||
|
Connect = 0x01,
|
||||||
|
Data = 0x02,
|
||||||
|
Close = 0x03,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RawCastFrame {
|
||||||
|
pub protocol: LocalProtocol,
|
||||||
|
pub event: RawCastEvent,
|
||||||
|
pub socket_id: u64,
|
||||||
|
pub dst_ip: Ipv4Addr,
|
||||||
|
pub dst_port: u16,
|
||||||
|
pub payload: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
const LOCAL_HEADER_SIZE: usize = 16;
|
||||||
|
|
||||||
|
impl RawCastFrame {
|
||||||
|
fn new(
|
||||||
|
protocol: LocalProtocol,
|
||||||
|
event: RawCastEvent,
|
||||||
|
socket_id: u64,
|
||||||
|
dst_ip: Ipv4Addr,
|
||||||
|
dst_port: u16,
|
||||||
|
payload: Bytes,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
protocol,
|
||||||
|
event,
|
||||||
|
socket_id,
|
||||||
|
dst_ip,
|
||||||
|
dst_port,
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect(protocol: LocalProtocol, id: u64, ip: Ipv4Addr, port: u16) -> Self {
|
||||||
|
Self::new(protocol, RawCastEvent::Connect, id, ip, port, Bytes::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(protocol: LocalProtocol, id: u64, ip: Ipv4Addr, port: u16, data: Vec<u8>) -> Self {
|
||||||
|
Self::new(
|
||||||
|
protocol,
|
||||||
|
RawCastEvent::Data,
|
||||||
|
id,
|
||||||
|
ip,
|
||||||
|
port,
|
||||||
|
Bytes::from(data),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(protocol: LocalProtocol, id: u64, ip: Ipv4Addr, port: u16) -> Self {
|
||||||
|
Self::new(protocol, RawCastEvent::Close, id, ip, port, Bytes::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_bytes(self) -> BytesMut {
|
||||||
|
let total_size = LOCAL_HEADER_SIZE + 2 + self.payload.len();
|
||||||
|
let mut buf = BytesMut::with_capacity(total_size);
|
||||||
|
|
||||||
|
buf.put_u8(self.protocol as u8);
|
||||||
|
buf.put_u8(self.event as u8);
|
||||||
|
buf.put_u64(self.socket_id);
|
||||||
|
buf.put_slice(&self.dst_ip.octets());
|
||||||
|
buf.put_u16(self.dst_port);
|
||||||
|
buf.put_u16(self.payload.len() as u16);
|
||||||
|
buf.put(self.payload);
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser for RawCastFrame {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn can_parse(bytes: &BytesMut) -> bool {
|
||||||
|
if bytes.len() < LOCAL_HEADER_SIZE + 2 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// читаем длину payload без сдвига буфера
|
||||||
|
let payload_len_pos = LOCAL_HEADER_SIZE;
|
||||||
|
let payload_len =
|
||||||
|
u16::from_be_bytes([bytes[payload_len_pos], bytes[payload_len_pos + 1]]) as usize;
|
||||||
|
|
||||||
|
bytes.len() >= LOCAL_HEADER_SIZE + 2 + payload_len
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(buf: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
|
||||||
|
if !Self::can_parse(buf) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut header = buf.split_to(LOCAL_HEADER_SIZE);
|
||||||
|
|
||||||
|
let protocol = match header.get_u8() {
|
||||||
|
0x01 => LocalProtocol::Tcp,
|
||||||
|
0x02 => LocalProtocol::Udp,
|
||||||
|
0x03 => LocalProtocol::Icmp,
|
||||||
|
p => return Err(format!("Unknown local protocol: {}", p)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let event = match header.get_u8() {
|
||||||
|
0x01 => RawCastEvent::Connect,
|
||||||
|
0x02 => RawCastEvent::Data,
|
||||||
|
0x03 => RawCastEvent::Close,
|
||||||
|
e => return Err(format!("Unknown local event: {}", e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let socket_id = header.get_u64();
|
||||||
|
|
||||||
|
let ip_bytes = [
|
||||||
|
header.get_u8(),
|
||||||
|
header.get_u8(),
|
||||||
|
header.get_u8(),
|
||||||
|
header.get_u8(),
|
||||||
|
];
|
||||||
|
let dst_ip = Ipv4Addr::from(ip_bytes);
|
||||||
|
|
||||||
|
let dst_port = header.get_u16();
|
||||||
|
|
||||||
|
let payload_len = buf.get_u16() as usize;
|
||||||
|
let payload = buf.split_to(payload_len).freeze();
|
||||||
|
|
||||||
|
Ok(Some(Self {
|
||||||
|
protocol,
|
||||||
|
event,
|
||||||
|
socket_id,
|
||||||
|
dst_ip,
|
||||||
|
dst_port,
|
||||||
|
payload,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
mod frame;
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
|
|
||||||
use crate::tlseng::{
|
use crate::{
|
||||||
consts::{CERT_COMPRESSION_BROTLI, OCSP_STATUS_TYPE, PSK_DHE_KE_MODE, TYPE_HOST_NAME},
|
nrxp::errors::{ErrorAction, ErrorStage, TlsError},
|
||||||
profile::BrowserProfile,
|
parser::Parser,
|
||||||
types::{TlsExtensions, TlsGroups, TlsSignatures, TlsVersions},
|
tlseng::{
|
||||||
|
consts::{CERT_COMPRESSION_BROTLI, OCSP_STATUS_TYPE, PSK_DHE_KE_MODE, TYPE_HOST_NAME},
|
||||||
|
profile::BrowserProfile,
|
||||||
|
types::{TlsExtensions, TlsGroups, TlsSignatures, TlsVersions},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -27,6 +31,54 @@ impl ExtensionStack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Extension {
|
impl Extension {
|
||||||
pub fn new(etype: u16, data: Bytes) -> Self {
|
pub fn new(etype: u16, data: Bytes) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use aead::{rand_core::RngCore, OsRng};
|
use aead::{rand_core::RngCore, OsRng};
|
||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
crypto::session::SessionKeys,
|
crypto::session::SessionKeys,
|
||||||
|
nrxp::errors::{ErrorAction, ErrorStage, TlsError},
|
||||||
|
parser::Parser,
|
||||||
tlseng::{
|
tlseng::{
|
||||||
consts::{HANDSHAKE_TYPE_CLIENT_HELLO, HANDSHAKE_TYPE_SERVER_HELLO},
|
consts::{HANDSHAKE_TYPE_CLIENT_HELLO, HANDSHAKE_TYPE_SERVER_HELLO},
|
||||||
extension::ExtensionBuilder,
|
extension::ExtensionBuilder,
|
||||||
@@ -10,7 +12,7 @@ use crate::{
|
|||||||
tls_record::TlsRecord,
|
tls_record::TlsRecord,
|
||||||
types::{ContentType, HelloType, ProtocolVersion},
|
types::{ContentType, HelloType, ProtocolVersion},
|
||||||
},
|
},
|
||||||
utils::u24::U24,
|
utils::u24::{BufExt, U24},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct HelloHeader {
|
pub struct HelloHeader {
|
||||||
@@ -18,6 +20,35 @@ pub struct HelloHeader {
|
|||||||
pub _len: U24,
|
pub _len: U24,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ClientHello {
|
pub struct ClientHello {
|
||||||
pub _version: ProtocolVersion,
|
pub _version: ProtocolVersion,
|
||||||
|
|
||||||
@@ -105,6 +136,90 @@ impl ClientHello {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ServerHello {
|
pub struct ServerHello {
|
||||||
pub version: ProtocolVersion,
|
pub version: ProtocolVersion,
|
||||||
pub random: [u8; 32],
|
pub random: [u8; 32],
|
||||||
@@ -211,3 +326,75 @@ impl ServerHello {
|
|||||||
buf.freeze()
|
buf.freeze()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
use bytes::Bytes;
|
|
||||||
|
|
||||||
pub struct ApplicationData {
|
|
||||||
pub _len: usize,
|
|
||||||
pub payload: Bytes,
|
|
||||||
}
|
|
||||||
|
|
||||||
mod consts;
|
mod consts;
|
||||||
pub mod extension;
|
pub mod extension;
|
||||||
pub mod handshake;
|
pub mod handshake;
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
|
|
||||||
use crate::tlseng::types::{ContentType, ProtocolVersion};
|
use crate::{
|
||||||
|
nrxp::errors::{ErrorAction, ErrorStage, TlsError},
|
||||||
|
parser::Parser,
|
||||||
|
tlseng::types::{ContentType, ProtocolVersion},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TlsRecord {
|
pub struct TlsRecord {
|
||||||
@@ -45,3 +49,74 @@ impl TlsRecord {
|
|||||||
record.serialize()
|
record.serialize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ApplicationData {
|
||||||
|
pub _len: usize,
|
||||||
|
pub payload: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user