Skip to content

Commit de09fcf

Browse files
catenacyberjasonish
authored andcommitted
detect/http2: use ThreadCtx for all headers keywords
Instead of custom inefficient "escaped" Vec Ticket: 8291
1 parent 0f4e396 commit de09fcf

6 files changed

Lines changed: 198 additions & 105 deletions

File tree

rust/src/http2/detect.rs

Lines changed: 150 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -416,12 +416,18 @@ pub fn http2_frames_get_header_value_vec(
416416
}
417417
}
418418

419+
#[derive(Debug, PartialEq)]
420+
enum Http2Header<'a> {
421+
Single(&'a [u8]),
422+
Multiple(Vec<u8>),
423+
}
424+
419425
fn http2_frames_get_header_value<'a>(
420-
tx: &'a mut HTTP2Transaction, direction: Direction, name: &str,
421-
) -> Result<&'a [u8], ()> {
426+
tx: &'a HTTP2Transaction, direction: Direction, name: &str,
427+
) -> Option<Http2Header<'a>> {
422428
let mut found = 0;
423429
let mut vec = Vec::new();
424-
let mut single: Result<&[u8], ()> = Err(());
430+
let mut single = None;
425431
let frames = if direction == Direction::ToServer {
426432
&tx.frames_ts
427433
} else {
@@ -432,10 +438,10 @@ fn http2_frames_get_header_value<'a>(
432438
for block in blocks.iter() {
433439
if block.name.as_ref() == name.as_bytes() {
434440
if found == 0 {
435-
single = Ok(&block.value);
441+
single = Some(Http2Header::Single(&block.value));
436442
found = 1;
437443
} else if found == 1 && Rc::strong_count(&block.name) <= 2 {
438-
if let Ok(s) = single {
444+
if let Some(Http2Header::Single(s)) = single {
439445
vec.extend_from_slice(s);
440446
}
441447
vec.extend_from_slice(b", ");
@@ -450,14 +456,11 @@ fn http2_frames_get_header_value<'a>(
450456
}
451457
}
452458
if found == 0 {
453-
return Err(());
459+
return None;
454460
} else if found == 1 {
455461
return single;
456462
} else {
457-
tx.escaped.push(vec);
458-
let idx = tx.escaped.len() - 1;
459-
let value = &tx.escaped[idx];
460-
return Ok(value);
463+
return Some(Http2Header::Multiple(vec));
461464
}
462465
}
463466

@@ -553,92 +556,115 @@ pub unsafe extern "C" fn SCHttp2TxGetMethod(
553556

554557
#[no_mangle]
555558
pub unsafe extern "C" fn SCHttp2TxGetHost(
556-
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
559+
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
557560
) -> u8 {
558-
if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
559-
*buffer = value.as_ptr(); //unsafe
560-
*buffer_len = value.len() as u32;
561-
return 1;
562-
}
563-
return 0;
564-
}
565-
566-
fn http2_lower(value: &[u8]) -> Option<Vec<u8>> {
567-
for i in 0..value.len() {
568-
if value[i].is_ascii_uppercase() {
569-
// we got at least one upper character, need to transform
570-
let mut vec: Vec<u8> = Vec::with_capacity(value.len());
571-
vec.extend_from_slice(value);
572-
for e in &mut vec {
573-
e.make_ascii_lowercase();
561+
let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
562+
if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
563+
match value {
564+
Http2Header::Single(v) => {
565+
*buffer = v.as_ptr(); //unsafe
566+
*buffer_len = v.len() as u32;
567+
}
568+
Http2Header::Multiple(v) => {
569+
tbuf.data = v;
570+
*buffer = tbuf.data.as_ptr(); //unsafe
571+
*buffer_len = tbuf.data.len() as u32;
574572
}
575-
return Some(vec);
576573
}
574+
return 1;
577575
}
578-
return None;
576+
return 0;
579577
}
580578

581579
// returns a tuple with the value and its size
582-
fn http2_normalize_host(value: &[u8]) -> &[u8] {
583-
match value.iter().position(|&x| x == b'@') {
584-
Some(i) => {
585-
let value = &value[i + 1..];
586-
match value.iter().position(|&x| x == b':') {
587-
Some(i) => {
588-
return &value[..i];
580+
fn http2_normalize_host(ve: Http2Header) -> Http2Header {
581+
let vs = match &ve {
582+
Http2Header::Single(v) => v,
583+
Http2Header::Multiple(v) => v.as_slice(),
584+
};
585+
let (start, end) = match vs.iter().position(|&x| x == b'@') {
586+
Some(i) => match &vs[i + 1..].iter().position(|&x| x == b':') {
587+
Some(j) => (i + 1, i + 1 + j),
588+
None => (i + 1, vs.len()),
589+
},
590+
None => match vs.iter().position(|&x| x == b':') {
591+
Some(i) => (0, i),
592+
None => (0, vs.len()),
593+
},
594+
};
595+
return match ve {
596+
Http2Header::Single(v) => {
597+
let mut need_transform = false;
598+
for c in v.iter().take(end).skip(start) {
599+
if c.is_ascii_uppercase() {
600+
need_transform = true;
601+
break;
589602
}
590-
None => {
591-
return value;
603+
}
604+
if need_transform {
605+
let mut vec: Vec<u8> = Vec::with_capacity(end - start);
606+
for c in v.iter().take(end).skip(start) {
607+
vec.push(c.to_ascii_lowercase());
592608
}
609+
Http2Header::Multiple(vec)
610+
} else {
611+
Http2Header::Single(&v[start..end])
593612
}
594613
}
595-
None => match value.iter().position(|&x| x == b':') {
596-
Some(i) => {
597-
return &value[..i];
614+
Http2Header::Multiple(mut v) => {
615+
if end < v.len() {
616+
v.truncate(end);
598617
}
599-
None => {
600-
return value;
618+
if start > 0 {
619+
v.drain(0..start);
601620
}
602-
},
603-
}
621+
for e in &mut v {
622+
e.make_ascii_lowercase();
623+
}
624+
Http2Header::Multiple(v)
625+
}
626+
};
604627
}
605628

606629
#[no_mangle]
607630
pub unsafe extern "C" fn SCHttp2TxGetHostNorm(
608-
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
631+
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
609632
) -> u8 {
610-
if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
611-
let r = http2_normalize_host(value);
612-
// r is a tuple with the value and its size
613-
// this is useful when we only take a substring (before the port)
614-
match http2_lower(r) {
615-
Some(normval) => {
616-
// In case we needed some normalization,
617-
// the transaction needs to take ownership of this normalized host
618-
tx.escaped.push(normval);
619-
let idx = tx.escaped.len() - 1;
620-
let resvalue = &tx.escaped[idx];
621-
*buffer = resvalue.as_ptr(); //unsafe
622-
*buffer_len = resvalue.len() as u32;
623-
return 1;
633+
let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
634+
if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
635+
match http2_normalize_host(value) {
636+
Http2Header::Single(v) => {
637+
*buffer = v.as_ptr(); //unsafe
638+
*buffer_len = v.len() as u32;
624639
}
625-
None => {
626-
*buffer = r.as_ptr(); //unsafe
627-
*buffer_len = r.len() as u32;
628-
return 1;
640+
Http2Header::Multiple(v) => {
641+
tbuf.data = v;
642+
*buffer = tbuf.data.as_ptr(); //unsafe
643+
*buffer_len = tbuf.data.len() as u32;
629644
}
630645
}
646+
return 1;
631647
}
632648
return 0;
633649
}
634650

635651
#[no_mangle]
636652
pub unsafe extern "C" fn SCHttp2TxGetUserAgent(
637-
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
653+
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
638654
) -> u8 {
639-
if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, "user-agent") {
640-
*buffer = value.as_ptr(); //unsafe
641-
*buffer_len = value.len() as u32;
655+
let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
656+
if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, "user-agent") {
657+
match value {
658+
Http2Header::Single(v) => {
659+
*buffer = v.as_ptr(); //unsafe
660+
*buffer_len = v.len() as u32;
661+
}
662+
Http2Header::Multiple(v) => {
663+
tbuf.data = v;
664+
*buffer = tbuf.data.as_ptr(); //unsafe
665+
*buffer_len = tbuf.data.len() as u32;
666+
}
667+
}
642668
return 1;
643669
}
644670
return 0;
@@ -659,16 +685,37 @@ pub unsafe extern "C" fn SCHttp2TxGetStatus(
659685
#[no_mangle]
660686
pub unsafe extern "C" fn SCHttp2TxGetCookie(
661687
tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
688+
tbuf: *mut c_void,
662689
) -> u8 {
690+
let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
663691
if direction == u8::from(Direction::ToServer) {
664-
if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, "cookie") {
665-
*buffer = value.as_ptr(); //unsafe
666-
*buffer_len = value.len() as u32;
692+
if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, "cookie") {
693+
match value {
694+
Http2Header::Single(v) => {
695+
*buffer = v.as_ptr(); //unsafe
696+
*buffer_len = v.len() as u32;
697+
}
698+
Http2Header::Multiple(v) => {
699+
tbuf.data = v;
700+
*buffer = tbuf.data.as_ptr(); //unsafe
701+
*buffer_len = tbuf.data.len() as u32;
702+
}
703+
}
667704
return 1;
668705
}
669-
} else if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToClient, "set-cookie") {
670-
*buffer = value.as_ptr(); //unsafe
671-
*buffer_len = value.len() as u32;
706+
} else if let Some(value) = http2_frames_get_header_value(tx, Direction::ToClient, "set-cookie")
707+
{
708+
match value {
709+
Http2Header::Single(v) => {
710+
*buffer = v.as_ptr(); //unsafe
711+
*buffer_len = v.len() as u32;
712+
}
713+
Http2Header::Multiple(v) => {
714+
tbuf.data = v;
715+
*buffer = tbuf.data.as_ptr(); //unsafe
716+
*buffer_len = tbuf.data.len() as u32;
717+
}
718+
}
672719
return 1;
673720
}
674721
return 0;
@@ -677,13 +724,24 @@ pub unsafe extern "C" fn SCHttp2TxGetCookie(
677724
#[no_mangle]
678725
pub unsafe extern "C" fn SCHttp2TxGetHeaderValue(
679726
tx: &mut HTTP2Transaction, direction: u8, strname: *const std::os::raw::c_char,
680-
buffer: *mut *const u8, buffer_len: *mut u32,
727+
buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
681728
) -> u8 {
729+
let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
682730
let hname: &CStr = CStr::from_ptr(strname); //unsafe
683731
if let Ok(s) = hname.to_str() {
684-
if let Ok(value) = http2_frames_get_header_value(tx, direction.into(), &s.to_lowercase()) {
685-
*buffer = value.as_ptr(); //unsafe
686-
*buffer_len = value.len() as u32;
732+
if let Some(value) = http2_frames_get_header_value(tx, direction.into(), &s.to_lowercase())
733+
{
734+
match value {
735+
Http2Header::Single(v) => {
736+
*buffer = v.as_ptr(); //unsafe
737+
*buffer_len = v.len() as u32;
738+
}
739+
Http2Header::Multiple(v) => {
740+
tbuf.data = v;
741+
*buffer = tbuf.data.as_ptr(); //unsafe
742+
*buffer_len = tbuf.data.len() as u32;
743+
}
744+
}
687745
return 1;
688746
}
689747
}
@@ -1027,20 +1085,20 @@ mod tests {
10271085
#[test]
10281086
fn test_http2_normalize_host() {
10291087
let buf0 = "aBC.com:1234".as_bytes();
1030-
let r0 = http2_normalize_host(buf0);
1031-
assert_eq!(r0, "aBC.com".as_bytes().to_vec());
1088+
let r0 = http2_normalize_host(Http2Header::Single(buf0));
1089+
assert_eq!(r0, Http2Header::Multiple("abc.com".as_bytes().to_vec()));
10321090
let buf1 = "oisf.net".as_bytes();
1033-
let r1 = http2_normalize_host(buf1);
1034-
assert_eq!(r1, "oisf.net".as_bytes().to_vec());
1091+
let r1 = http2_normalize_host(Http2Header::Single(buf1));
1092+
assert_eq!(r1, Http2Header::Single("oisf.net".as_bytes()));
10351093
let buf2 = "localhost:3000".as_bytes();
1036-
let r2 = http2_normalize_host(buf2);
1037-
assert_eq!(r2, "localhost".as_bytes().to_vec());
1094+
let r2 = http2_normalize_host(Http2Header::Single(buf2));
1095+
assert_eq!(r2, Http2Header::Single("localhost".as_bytes()));
10381096
let buf3 = "user:pass@localhost".as_bytes();
1039-
let r3 = http2_normalize_host(buf3);
1040-
assert_eq!(r3, "localhost".as_bytes().to_vec());
1097+
let r3 = http2_normalize_host(Http2Header::Single(buf3));
1098+
assert_eq!(r3, Http2Header::Single("localhost".as_bytes()));
10411099
let buf4 = "user:pass@localhost:123".as_bytes();
1042-
let r4 = http2_normalize_host(buf4);
1043-
assert_eq!(r4, "localhost".as_bytes().to_vec());
1100+
let r4 = http2_normalize_host(Http2Header::Single(buf4));
1101+
assert_eq!(r4, Http2Header::Single("localhost".as_bytes()));
10441102
}
10451103

10461104
#[test]
@@ -1091,12 +1149,12 @@ mod tests {
10911149
header: head,
10921150
data: txdata,
10931151
});
1094-
match http2_frames_get_header_value(&mut tx, Direction::ToServer, "Host") {
1095-
Ok(x) => {
1152+
match http2_frames_get_header_value(&tx, Direction::ToServer, "Host") {
1153+
Some(Http2Header::Multiple(x)) => {
10961154
assert_eq!(x, "abc.com, efg.net".as_bytes());
10971155
}
1098-
Err(e) => {
1099-
panic!("Result should not have been an error: {:?}", e);
1156+
_ => {
1157+
panic!("Result should have been a multiple header value");
11001158
}
11011159
}
11021160
}

rust/src/http2/http2.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,6 @@ pub struct HTTP2Transaction {
176176
pub ft_tc: FileTransferTracker,
177177
pub ft_ts: FileTransferTracker,
178178

179-
//temporary escaped header for detection
180-
//must be attached to transaction for memory management (be freed at the right time)
181-
pub escaped: Vec<Vec<u8>>,
182179
pub req_line: Vec<u8>,
183180
pub resp_line: Vec<u8>,
184181

@@ -211,7 +208,6 @@ impl HTTP2Transaction {
211208
tx_data: AppLayerTxData::new(),
212209
ft_tc: FileTransferTracker::new(),
213210
ft_ts: FileTransferTracker::new(),
214-
escaped: Vec::with_capacity(16),
215211
req_line: Vec::new(),
216212
resp_line: Vec::new(),
217213
doh: None,

0 commit comments

Comments
 (0)