@ -31,12 +31,14 @@ static mut ALPROTO_QUIC: AppProto = ALPROTO_UNKNOWN;
const DEFAULT_DCID_LEN : usize = 16 ;
const DEFAULT_DCID_LEN : usize = 16 ;
const PKT_NUM_BUF_MAX_LEN : usize = 4 ;
const PKT_NUM_BUF_MAX_LEN : usize = 4 ;
pub ( super ) const QUIC_MAX_CRYPTO_FRAG_LEN : u64 = 65535 ;
#[ derive(FromPrimitive, Debug, AppLayerEvent) ]
#[ derive(FromPrimitive, Debug, AppLayerEvent) ]
pub enum QuicEvent {
pub enum QuicEvent {
FailedDecrypt ,
FailedDecrypt ,
ErrorOnData ,
ErrorOnData ,
ErrorOnHeader ,
ErrorOnHeader ,
CryptoFragTooLong ,
}
}
#[ derive(Debug) ]
#[ derive(Debug) ]
@ -103,6 +105,14 @@ pub struct QuicState {
state_data : AppLayerStateData ,
state_data : AppLayerStateData ,
max_tx_id : u64 ,
max_tx_id : u64 ,
keys : Option < QuicKeys > ,
keys : Option < QuicKeys > ,
/// crypto fragment data already seen and reassembled to client
crypto_frag_tc : Vec < u8 > ,
/// number of bytes set in crypto fragment data to client
crypto_fraglen_tc : u32 ,
/// crypto fragment data already seen and reassembled to server
crypto_frag_ts : Vec < u8 > ,
/// number of bytes set in crypto fragment data to server
crypto_fraglen_ts : u32 ,
hello_tc : bool ,
hello_tc : bool ,
hello_ts : bool ,
hello_ts : bool ,
transactions : VecDeque < QuicTransaction > ,
transactions : VecDeque < QuicTransaction > ,
@ -114,6 +124,10 @@ impl Default for QuicState {
state_data : AppLayerStateData ::new ( ) ,
state_data : AppLayerStateData ::new ( ) ,
max_tx_id : 0 ,
max_tx_id : 0 ,
keys : None ,
keys : None ,
crypto_frag_tc : Vec ::new ( ) ,
crypto_frag_ts : Vec ::new ( ) ,
crypto_fraglen_tc : 0 ,
crypto_fraglen_ts : 0 ,
hello_tc : false ,
hello_tc : false ,
hello_ts : false ,
hello_ts : false ,
transactions : VecDeque ::new ( ) ,
transactions : VecDeque ::new ( ) ,
@ -144,10 +158,14 @@ impl QuicState {
fn new_tx (
fn new_tx (
& mut self , header : QuicHeader , data : QuicData , sni : Option < Vec < u8 > > , ua : Option < Vec < u8 > > ,
& mut self , header : QuicHeader , data : QuicData , sni : Option < Vec < u8 > > , ua : Option < Vec < u8 > > ,
extb : Vec < QuicTlsExtension > , ja3 : Option < String > , ja4 : Option < String > , client : bool ,
extb : Vec < QuicTlsExtension > , ja3 : Option < String > , ja4 : Option < String > , client : bool ,
frag_long : bool ,
) {
) {
let mut tx = QuicTransaction ::new ( header , data , sni , ua , extb , ja3 , ja4 , client ) ;
let mut tx = QuicTransaction ::new ( header , data , sni , ua , extb , ja3 , ja4 , client ) ;
self . max_tx_id + = 1 ;
self . max_tx_id + = 1 ;
tx . tx_id = self . max_tx_id ;
tx . tx_id = self . max_tx_id ;
if frag_long {
tx . tx_data . set_event ( QuicEvent ::CryptoFragTooLong as u8 ) ;
}
self . transactions . push_back ( tx ) ;
self . transactions . push_back ( tx ) ;
}
}
@ -225,6 +243,7 @@ impl QuicState {
let mut ja3 : Option < String > = None ;
let mut ja3 : Option < String > = None ;
let mut ja4 : Option < String > = None ;
let mut ja4 : Option < String > = None ;
let mut extv : Vec < QuicTlsExtension > = Vec ::new ( ) ;
let mut extv : Vec < QuicTlsExtension > = Vec ::new ( ) ;
let mut frag_long = false ;
for frame in & data . frames {
for frame in & data . frames {
match frame {
match frame {
Frame ::Stream ( s ) = > {
Frame ::Stream ( s ) = > {
@ -241,6 +260,24 @@ impl QuicState {
}
}
}
}
}
}
Frame ::CryptoFrag ( frag ) = > {
// means we had some fragments but not full TLS hello
// save it for a later packet
if to_server {
// use a hardcoded limit to not grow indefinitely
if frag . length < QUIC_MAX_CRYPTO_FRAG_LEN {
self . crypto_frag_ts . clone_from ( & frag . data ) ;
self . crypto_fraglen_ts = frag . offset as u32 ;
} else {
frag_long = true ;
}
} else if frag . length < QUIC_MAX_CRYPTO_FRAG_LEN {
self . crypto_frag_tc . clone_from ( & frag . data ) ;
self . crypto_fraglen_tc = frag . offset as u32 ;
} else {
frag_long = true ;
}
}
Frame ::Crypto ( c ) = > {
Frame ::Crypto ( c ) = > {
if let Some ( ja3str ) = & c . ja3 {
if let Some ( ja3str ) = & c . ja3 {
ja3 = Some ( ja3str . clone ( ) ) ;
ja3 = Some ( ja3str . clone ( ) ) ;
@ -268,7 +305,7 @@ impl QuicState {
_ = > { }
_ = > { }
}
}
}
}
self . new_tx ( header , data , sni , ua , extv , ja3 , ja4 , to_server );
self . new_tx ( header , data , sni , ua , extv , ja3 , ja4 , to_server , frag_long );
}
}
fn set_event_notx ( & mut self , event : QuicEvent , header : QuicHeader , client : bool ) {
fn set_event_notx ( & mut self , event : QuicEvent , header : QuicHeader , client : bool ) {
@ -327,11 +364,31 @@ impl QuicState {
None ,
None ,
None ,
None ,
to_server ,
to_server ,
false ,
) ;
) ;
continue ;
continue ;
}
}
match QuicData ::from_bytes ( framebuf ) {
let mut frag = Vec ::new ( ) ;
// take the current fragment and reset it in the state
let past_frag = if to_server {
std ::mem ::swap ( & mut self . crypto_frag_ts , & mut frag ) ;
& frag
} else {
std ::mem ::swap ( & mut self . crypto_frag_tc , & mut frag ) ;
& frag
} ;
let past_fraglen = if to_server {
self . crypto_fraglen_ts
} else {
self . crypto_fraglen_tc
} ;
if to_server {
self . crypto_fraglen_ts = 0
} else {
self . crypto_fraglen_tc = 0
}
match QuicData ::from_bytes ( framebuf , past_frag , past_fraglen ) {
Ok ( data ) = > {
Ok ( data ) = > {
self . handle_frames ( data , header , to_server ) ;
self . handle_frames ( data , header , to_server ) ;
}
}