@ -23,15 +23,16 @@ use crate::common::nom7::take_until_and_consume;
use nom7 ::branch ::alt ;
use nom7 ::bytes ::streaming ::{ tag , take , take_until , take_until1 } ;
use nom7 ::character ::streaming ::{ alphanumeric1 , char } ;
use nom7 ::combinator ::{ all_consuming , co nd, eof , map_parser , opt , peek , verify } ;
use nom7 ::combinator ::{ all_consuming , co mplete, co nd, eof , map_parser , opt , peek , verify } ;
use nom7 ::error ::{ make_error , ErrorKind , ParseError } ;
use nom7 ::multi ::{ many1, many_m_n , many_till } ;
use nom7 ::multi ::{ count, many1 , many_till } ;
use nom7 ::number ::streaming ::{ be_i16 , be_i32 } ;
use nom7 ::number ::streaming ::{ be_u16 , be_u32 , be_u8 } ;
use nom7 ::sequence ::terminated ;
use nom7 ::{ Err , IResult , Parser } ;
const PGSQL_LENGTH_FIELD : u32 = 4 ;
const PGSQL_ROW_HEADER_LENGTH : u32 = 6 ;
const PGSQL_DUMMY_PROTO_MAJOR : u16 = 1234 ; // 0x04d2
const PGSQL_DUMMY_PROTO_CANCEL_REQUEST : u16 = 5678 ; // 0x162e
@ -249,6 +250,8 @@ pub(crate) struct BackendKeyDataMessage {
#[ derive(Debug, PartialEq, Eq) ]
pub ( crate ) struct ConsolidatedDataRowPacket {
// also built in pgsql.rs, as a per-transaction summary
pub is_malformed : bool ,
pub identifier : u8 ,
pub row_cnt : u64 , // row or msg cnt
pub data_size : u64 ,
@ -361,6 +364,8 @@ impl PgsqlBEMessage {
PgsqlBEMessage ::CopyInResponse ( m ) | PgsqlBEMessage ::CopyOutResponse ( m ) = > {
m . is_malformed
}
PgsqlBEMessage ::RowDescription ( r ) = > r . is_malformed ,
PgsqlBEMessage ::ConsolidatedDataRow ( d ) = > d . is_malformed ,
_ = > false ,
}
}
@ -466,6 +471,7 @@ pub(crate) struct RowField {
#[ derive(Debug, PartialEq, Eq) ]
pub ( crate ) struct RowDescriptionMessage {
is_malformed : bool ,
pub identifier : u8 ,
pub length : u32 ,
pub field_count : u16 ,
@ -1009,16 +1015,30 @@ fn parse_row_field(i: &[u8]) -> IResult<&[u8], RowField, PgsqlParseError<&[u8]>>
}
fn parse_row_description ( i : & [ u8 ] ) -> IResult < & [ u8 ] , PgsqlBEMessage , PgsqlParseError < & [ u8 ] > > {
let ( i , identifier ) = verify ( be_u8 , | & x | x = = b'T' ) ( i ) ? ;
let ( i , length ) = parse_gte_length ( i , 7 ) ? ;
let ( i , field_count ) = be_u16 ( i ) ? ;
let ( i , fields ) = map_parser (
take ( length - 6 ) ,
many_m_n ( 0 , field_count . into ( ) , parse_row_field ) ,
) ( i ) ? ;
let ( i , identifier ) = verify ( be_u8 , | & x | x = = b'T' ) . parse ( i ) ? ;
let ( i , length ) = parse_gte_length ( i , PGSQL_ROW_HEADER_LENGTH + 1 ) ? ;
let ( i , field_count ) = be_u16 . parse ( i ) ? ;
let ( i , body ) = take ( length - PGSQL_ROW_HEADER_LENGTH ) . parse ( i ) ? ;
let fields =
match all_consuming ( count ( complete ( parse_row_field ) , field_count . into ( ) ) ) . parse ( body ) {
Ok ( ( _ , fields ) ) = > fields ,
Err ( _ ) = > {
return Ok ( (
i ,
PgsqlBEMessage ::RowDescription ( RowDescriptionMessage {
is_malformed : true ,
identifier ,
length ,
field_count ,
fields : Vec ::new ( ) ,
} ) ,
) ) ;
}
} ;
Ok ( (
i ,
PgsqlBEMessage ::RowDescription ( RowDescriptionMessage {
is_malformed : false ,
identifier ,
length ,
field_count ,
@ -1127,7 +1147,9 @@ fn parse_consolidated_copy_data_out(i: &[u8]) -> IResult<&[u8], PgsqlBEMessage,
let ( i , _data ) = take ( length - PGSQL_LENGTH_FIELD ) ( i ) ? ;
SCLogDebug ! ( "data_size is {:?}" , _data ) ;
Ok ( (
i , PgsqlBEMessage ::ConsolidatedCopyDataOut ( ConsolidatedDataRowPacket {
i ,
PgsqlBEMessage ::ConsolidatedCopyDataOut ( ConsolidatedDataRowPacket {
is_malformed : false ,
identifier ,
row_cnt : 1 ,
data_size : ( length - PGSQL_LENGTH_FIELD ) as u64 } )
@ -1140,7 +1162,9 @@ fn parse_consolidated_copy_data_in(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, P
let ( i , _data ) = take ( length - PGSQL_LENGTH_FIELD ) ( i ) ? ;
SCLogDebug ! ( "data size is {:?}" , _data ) ;
Ok ( (
i , PgsqlFEMessage ::ConsolidatedCopyDataIn ( ConsolidatedDataRowPacket {
i ,
PgsqlFEMessage ::ConsolidatedCopyDataIn ( ConsolidatedDataRowPacket {
is_malformed : false ,
identifier ,
row_cnt : 1 ,
data_size : ( length - PGSQL_LENGTH_FIELD ) as u64 } )
@ -1185,20 +1209,31 @@ fn parse_copy_fail(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage, PgsqlParseError<&
// Currently, we don't store the actual DataRow messages, as those could easily become a burden, memory-wise
// We use ConsolidatedDataRow to store info we still want to log: message size.
// Later on, we calculate the number of lines the command actually returned by counting ConsolidatedDataRow messages
fn parse_consolidated_data_row (
i : & [ u8 ] ,
) -> IResult < & [ u8 ] , PgsqlBEMessage , PgsqlParseError < & [ u8 ] > > {
let ( i , identifier ) = verify ( be_u8 , | & x | x = = b'D' ) ( i ) ? ;
let ( i , length ) = parse_gte_length ( i , 7 ) ? ;
let ( i , field_count ) = be_u16 ( i ) ? ;
// 6 here is for skipping length + field_count
let ( i , rows ) = map_parser (
take ( length - 6 ) ,
many_m_n ( 0 , field_count . into ( ) , parse_data_row_value ) ,
) ( i ) ? ;
fn parse_consolidated_data_row ( i : & [ u8 ] ) -> IResult < & [ u8 ] , PgsqlBEMessage , PgsqlParseError < & [ u8 ] > > {
let ( i , identifier ) = verify ( be_u8 , | & x | x = = b'D' ) . parse ( i ) ? ;
let ( i , length ) = parse_gte_length ( i , PGSQL_ROW_HEADER_LENGTH + 1 ) ? ;
let ( i , field_count ) = be_u16 . parse ( i ) ? ;
let ( i , body ) = take ( length - PGSQL_ROW_HEADER_LENGTH ) . parse ( i ) ? ;
let rows = match all_consuming ( count ( complete ( parse_data_row_value ) , field_count . into ( ) ) )
. parse ( body )
{
Ok ( ( _ , rows ) ) = > rows ,
Err ( _ ) = > {
return Ok ( (
i ,
PgsqlBEMessage ::ConsolidatedDataRow ( ConsolidatedDataRowPacket {
is_malformed : true ,
identifier ,
row_cnt : 1 ,
data_size : 0 ,
} ) ,
) ) ;
}
} ;
Ok ( (
i ,
PgsqlBEMessage ::ConsolidatedDataRow ( ConsolidatedDataRowPacket {
is_malformed : false ,
identifier ,
row_cnt : 1 ,
data_size : add_up_data_size ( rows ) ,
@ -2471,12 +2506,12 @@ mod tests {
// T ..
// source @ . ....... version @ . ....... sid @ . . .....
let buffer : & [ u8 ] = & [
0x54 , 0x00 , 0x00 , 0x00 , 0x 50 , 0x00 , 0x03 , 0x73 , 0x6f , 0x75 , 0x72 , 0x63 , 0x65 , 0x00 ,
0x54 , 0x00 , 0x00 , 0x00 , 0x 4f , 0x00 , 0x03 , 0x73 , 0x6f , 0x75 , 0x72 , 0x63 , 0x65 , 0x00 ,
0x00 , 0x00 , 0x40 , 0x09 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x19 , 0xff , 0xff , 0xff , 0xff ,
0xff , 0xff , 0x00 , 0x00 , 0x76 , 0x65 , 0x72 , 0x73 , 0x69 , 0x6f , 0x6e , 0x00 , 0x00 , 0x00 ,
0x40 , 0x09 , 0x00 , 0x02 , 0x00 , 0x00 , 0x00 , 0x19 , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
0x00 , 0x00 , 0x73 , 0x69 , 0x64 , 0x00 , 0x00 , 0x00 , 0x40 , 0x09 , 0x00 , 0x03 , 0x00 , 0x00 ,
0x00 , 0x14 , 0x00 , 0x08 , 0xff , 0xff , 0xff , 0xff , 0x00 , 0x00 , 0x00 ,
0x00 , 0x14 , 0x00 , 0x08 , 0xff , 0xff , 0xff , 0xff , 0x00 , 0x00 ,
] ;
let field1 = RowField {
@ -2512,8 +2547,9 @@ mod tests {
let fields_vec = vec! [ field1 , field2 , field3 ] ;
let ok_res = PgsqlBEMessage ::RowDescription ( RowDescriptionMessage {
is_malformed : false ,
identifier : b 'T' ,
length : 80 ,
length : 79 ,
field_count : 3 ,
fields : fields_vec ,
} ) ;
@ -2667,8 +2703,17 @@ mod tests {
0x07 , 0x32 , 0x30 , 0x32 , 0x31 , 0x37 , 0x30 , 0x31 ,
] ;
let result = parse_consolidated_data_row ( buffer ) ;
assert! ( result . is_ok ( ) ) ;
let ok_res = PgsqlBEMessage ::ConsolidatedDataRow ( ConsolidatedDataRowPacket {
is_malformed : false ,
identifier : b 'D' ,
row_cnt : 1 ,
data_size : 17 ,
} ) ;
assert_eq! (
parse_consolidated_data_row ( buffer ) ,
Ok ( ( & buffer [ 36 .. ] , ok_res ) )
) ;
}
#[ test ]