From b54b2b70df6fc6f0563866daed4b8c17c2d017c3 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Wed, 26 May 2021 20:54:01 +0200 Subject: [PATCH 1/8] http2: turn Host header into authority during upgrade HTTP1 uses Host, but HTTP2 uses rather :authority cf HPACK (cherry picked from commit 75f75e1eb09c208a42e58b2babbc55027cd79e28) --- rust/src/http2/detect.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index da572dfbd825..b374a1699923 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -866,6 +866,13 @@ fn http2_tx_set_settings(state: &mut HTTP2State, input: &[u8]) { } } +fn http2_caseinsensitive_cmp(s1: &[u8], s2: &str) -> bool { + if let Ok(s) = std::str::from_utf8(s1) { + return s.to_lowercase() == s2; + } + return false; +} + #[no_mangle] pub extern "C" fn rs_http2_tx_add_header( state: &mut HTTP2State, name: *const u8, name_len: u32, value: *const u8, value_len: u32, @@ -874,6 +881,8 @@ pub extern "C" fn rs_http2_tx_add_header( let slice_value = build_slice!(value, value_len as usize); if slice_name == "HTTP2-Settings".as_bytes() { http2_tx_set_settings(state, slice_value) + } else if http2_caseinsensitive_cmp(slice_name, "host") { + http2_tx_set_header(state, ":authority".as_bytes(), slice_value) } else { http2_tx_set_header(state, slice_name, slice_value) } From d48f5c478d67498c18f09abdb9d75535721df8a9 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Wed, 26 May 2021 21:45:30 +0200 Subject: [PATCH 2/8] http2: http.host normalized keyword now works for HTTP2 (cherry picked from commit 547e9f4ab42fb4a67dc67f85fa58e0c9a7e4c634) --- rust/src/http2/detect.rs | 92 ++++++++++++++++++++++++++++++++++++++++ src/detect-http-host.c | 36 ++++++++++++++-- 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index b374a1699923..f17571d55182 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -552,6 +552,60 @@ pub unsafe extern "C" fn rs_http2_tx_get_host( return 0; } +fn http2_lower(value: &[u8]) -> Option> { + for i in 0..value.len() { + if value[i].is_ascii_uppercase() { + // we got at least one upper character, need to transform + let mut vec: Vec = Vec::with_capacity(value.len()); + vec.extend_from_slice(value); + for j in i..vec.len() { + vec[j].make_ascii_lowercase(); + } + return Some(vec); + } + } + return None; +} + +// returns a tuple with the value and its size +fn http2_normalize_host(value: &[u8]) -> (Option>, usize) { + match value.iter().position(|&x| x == ':' as u8) { + Some(i) => { + return (http2_lower(&value[..i]), i); + } + None => { + return (http2_lower(value), value.len()); + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn rs_http2_tx_get_host_norm( + tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, +) -> u8 { + if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, ":authority") { + let r = http2_normalize_host(value); + // r is a tuple with the value and its size + // this is useful when we only take a substring (before the port) + match r.0 { + Some(normval) => { + tx.escaped.push(normval); + let idx = tx.escaped.len() - 1; + let resvalue = &tx.escaped[idx]; + *buffer = resvalue.as_ptr(); //unsafe + *buffer_len = r.1 as u32; + return 1; + } + None => { + *buffer = value.as_ptr(); //unsafe + *buffer_len = r.1 as u32; + return 1; + } + } + } + return 0; +} + #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_useragent( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, @@ -887,3 +941,41 @@ pub extern "C" fn rs_http2_tx_add_header( http2_tx_set_header(state, slice_name, slice_value) } } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_http2_normalize_host() { + let buf0 = "aBC.com:1234".as_bytes(); + let r0 = http2_normalize_host(buf0); + match r0.0 { + Some(r) => { + assert_eq!(r, "abc.com".as_bytes().to_vec()); + } + None => { + panic!("Result should not have been None"); + } + } + let buf1 = "oisf.net".as_bytes(); + let r1 = http2_normalize_host(buf1); + match r1.0 { + Some(r) => { + panic!("Result should not have been None, not {:?}", r); + } + None => {} + } + assert_eq!(r1.1, "oisf.net".len()); + let buf2 = "localhost:3000".as_bytes(); + let r2 = http2_normalize_host(buf2); + match r2.0 { + Some(r) => { + panic!("Result should not have been None, not {:?}", r); + } + None => {} + } + assert_eq!(r2.1, "localhost".len()); + } +} diff --git a/src/detect-http-host.c b/src/detect-http-host.c index d470105469e2..108ca344304a 100644 --- a/src/detect-http-host.c +++ b/src/detect-http-host.c @@ -77,6 +77,9 @@ static int DetectHttpHostRawSetupSticky(DetectEngineCtx *de_ctx, Signature *s, c static InspectionBuffer *GetRawData(DetectEngineThreadCtx *det_ctx, const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv, const int list_id); +static InspectionBuffer *GetRawData2(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv, + const int list_id); static int g_http_host_buffer_id = 0; /** @@ -110,6 +113,12 @@ void DetectHttpHHRegister(void) PrefilterGenericMpmRegister, GetData, ALPROTO_HTTP, HTP_REQUEST_HEADERS); + DetectAppLayerInspectEngineRegister2("http_host", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, + HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetData2); + + DetectAppLayerMpmRegister2("http_host", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, + GetData2, ALPROTO_HTTP2, HTTP2StateDataClient); + DetectBufferTypeRegisterValidateCallback("http_host", DetectHttpHostValidateCallback); @@ -142,10 +151,10 @@ void DetectHttpHHRegister(void) HTP_REQUEST_HEADERS); DetectAppLayerInspectEngineRegister2("http_raw_host", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, - HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetData2); + HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetRawData2); DetectAppLayerMpmRegister2("http_raw_host", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, - GetData2, ALPROTO_HTTP2, HTTP2StateDataClient); + GetRawData2, ALPROTO_HTTP2, HTTP2StateDataClient); DetectBufferTypeSetDescriptionByName("http_raw_host", "http raw host header"); @@ -224,7 +233,7 @@ static int DetectHttpHostSetup(DetectEngineCtx *de_ctx, Signature *s, const char if (DetectBufferSetActiveList(s, g_http_host_buffer_id) < 0) return -1; if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) - return -1; + return -1; return 0; } @@ -258,6 +267,27 @@ static InspectionBuffer *GetData2(DetectEngineThreadCtx *det_ctx, uint32_t b_len = 0; const uint8_t *b = NULL; + if (rs_http2_tx_get_host_norm(txv, &b, &b_len) != 1) + return NULL; + if (b == NULL || b_len == 0) + return NULL; + + InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); + InspectionBufferApplyTransforms(buffer, transforms); + } + + return buffer; +} + +static InspectionBuffer *GetRawData2(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv, + const int list_id) +{ + InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); + if (buffer->inspect == NULL) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + if (rs_http2_tx_get_host(txv, &b, &b_len) != 1) return NULL; if (b == NULL || b_len == 0) From 8027a38266d4c744f0f2bd9e64232d37a44507fc Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 31 May 2021 10:11:27 +0200 Subject: [PATCH 3/8] http2: http.header_names keyword now works for HTTP2 (cherry picked from commit 9b9f909d7db9ba4485bf50577868fa7072998487) --- rust/src/http2/detect.rs | 38 ++++++++++++++++++++++++++++++++++ src/detect-http-header-names.c | 31 +++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index f17571d55182..aba0b2b3f24e 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -692,6 +692,44 @@ fn http2_escape_header(hd: &parser::HTTP2FrameHeaders, i: u32) -> Vec { return vec; } +#[no_mangle] +pub unsafe extern "C" fn rs_http2_tx_get_header_names( + tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, +) -> u8 { + let mut vec = Vec::new(); + vec.push('\r' as u8); + vec.push('\n' as u8); + let frames = if direction & STREAM_TOSERVER != 0 { + &tx.frames_ts + } else { + &tx.frames_tc + }; + for i in 0..frames.len() { + match &frames[i].data { + HTTP2FrameTypeData::HEADERS(hd) => { + for j in 0..hd.blocks.len() { + // we do not escape linefeeds in headers names + vec.extend_from_slice(&hd.blocks[j].name); + vec.push('\r' as u8); + vec.push('\n' as u8); + } + } + _ => {} + } + } + if vec.len() > 2 { + vec.push('\r' as u8); + vec.push('\n' as u8); + tx.escaped.push(vec); + let idx = tx.escaped.len() - 1; + let value = &tx.escaped[idx]; + *buffer = value.as_ptr(); //unsafe + *buffer_len = value.len() as u32; + return 1; + } + return 0; +} + #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_header( tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32, diff --git a/src/detect-http-header-names.c b/src/detect-http-header-names.c index 989a24c3e517..c7477a0f98ef 100644 --- a/src/detect-http-header-names.c +++ b/src/detect-http-header-names.c @@ -150,6 +150,27 @@ static uint8_t *GetBufferForTX(htp_tx_t *tx, uint64_t tx_id, return buf->buffer; } +static InspectionBuffer *GetBuffer2ForTX(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flow_flags, void *txv, + const int list_id) +{ + InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); + if (buffer->inspect == NULL) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (rs_http2_tx_get_header_names(txv, flow_flags, &b, &b_len) != 1) + return NULL; + if (b == NULL || b_len == 0) + return NULL; + + InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); + InspectionBufferApplyTransforms(buffer, transforms); + } + + return buffer; +} + typedef struct PrefilterMpmHttpHeaderCtx { int list_id; const MpmCtx *mpm_ctx; @@ -412,6 +433,16 @@ void DetectHttpHeaderNamesRegister(void) ALPROTO_HTTP, SIG_FLAG_TOCLIENT, HTP_RESPONSE_HEADERS, InspectEngineHttpHeaderNames, NULL); + DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, + GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataClient); + DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, + GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataServer); + + DetectAppLayerInspectEngineRegister2(BUFFER_NAME, ALPROTO_HTTP2, SIG_FLAG_TOSERVER, + HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); + DetectAppLayerInspectEngineRegister2(BUFFER_NAME, ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, + HTTP2StateDataServer, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); + DetectBufferTypeSetDescriptionByName(BUFFER_NAME, BUFFER_DESC); From c8650a5bba318499f7b9007a2be647fd8d64b975 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 31 May 2021 10:50:38 +0200 Subject: [PATCH 4/8] http2: http.header keyword now works for HTTP2 As well as http.header.raw (cherry picked from commit 0b0649d98e07eb7dc8da60100c80fbcfe6cdd9e8) --- rust/src/http2/detect.rs | 124 +++++++++++++++++++++++++++++++++++ src/detect-http-header.c | 31 +++++++++ src/detect-http-raw-header.c | 34 ++++++++++ 3 files changed, 189 insertions(+) diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index aba0b2b3f24e..c7a800c007b6 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -730,6 +730,117 @@ pub unsafe extern "C" fn rs_http2_tx_get_header_names( return 0; } +fn http2_header_iscookie(direction: u8, hname: &[u8]) -> bool { + if let Ok(s) = std::str::from_utf8(hname) { + if direction & STREAM_TOSERVER != 0 { + if s.to_lowercase() == "cookie" { + return true; + } + } else { + if s.to_lowercase() == "set-cookie" { + return true; + } + } + } + return false; +} + +fn http2_header_trimspaces(value: &[u8]) -> &[u8] { + let mut start = 0; + let mut end = value.len(); + while start < value.len() { + if value[start] == b' ' || value[start] == b'\t' { + start += 1; + } else { + break; + } + } + while end > start { + if value[end - 1] == b' ' || value[end - 1] == b'\t' { + end -= 1; + } else { + break; + } + } + return &value[start..end]; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_http2_tx_get_headers( + tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, +) -> u8 { + let mut vec = Vec::new(); + let frames = if direction & STREAM_TOSERVER != 0 { + &tx.frames_ts + } else { + &tx.frames_tc + }; + for i in 0..frames.len() { + match &frames[i].data { + HTTP2FrameTypeData::HEADERS(hd) => { + for j in 0..hd.blocks.len() { + if !http2_header_iscookie(direction, &hd.blocks[j].name) { + // we do not escape linefeeds nor : in headers names + vec.extend_from_slice(&hd.blocks[j].name); + vec.push(':' as u8); + vec.push(' ' as u8); + vec.extend_from_slice(http2_header_trimspaces(&hd.blocks[j].value)); + vec.push('\r' as u8); + vec.push('\n' as u8); + } + } + } + _ => {} + } + } + if vec.len() > 0 { + tx.escaped.push(vec); + let idx = tx.escaped.len() - 1; + let value = &tx.escaped[idx]; + *buffer = value.as_ptr(); //unsafe + *buffer_len = value.len() as u32; + return 1; + } + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_http2_tx_get_headers_raw( + tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, +) -> u8 { + let mut vec = Vec::new(); + let frames = if direction & STREAM_TOSERVER != 0 { + &tx.frames_ts + } else { + &tx.frames_tc + }; + for i in 0..frames.len() { + match &frames[i].data { + HTTP2FrameTypeData::HEADERS(hd) => { + for j in 0..hd.blocks.len() { + // we do not escape linefeeds nor : in headers names + vec.extend_from_slice(&hd.blocks[j].name); + vec.push(':' as u8); + vec.push(' ' as u8); + vec.extend_from_slice(&hd.blocks[j].value); + vec.push('\r' as u8); + vec.push('\n' as u8); + } + } + _ => {} + } + } + if vec.len() > 0 { + tx.escaped.push(vec); + let idx = tx.escaped.len() - 1; + let value = &tx.escaped[idx]; + *buffer = value.as_ptr(); //unsafe + *buffer_len = value.len() as u32; + return 1; + } + return 0; +} + #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_header( tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32, @@ -1016,4 +1127,17 @@ mod tests { } assert_eq!(r2.1, "localhost".len()); } + + #[test] + fn test_http2_header_trimspaces() { + let buf0 = "nospaces".as_bytes(); + let r0 = http2_header_trimspaces(buf0); + assert_eq!(r0, "nospaces".as_bytes()); + let buf1 = " spaces\t".as_bytes(); + let r1 = http2_header_trimspaces(buf1); + assert_eq!(r1, "spaces".as_bytes()); + let buf2 = " \t".as_bytes(); + let r2 = http2_header_trimspaces(buf2); + assert_eq!(r2, "".as_bytes()); + } } diff --git a/src/detect-http-header.c b/src/detect-http-header.c index 8eabfebecf90..70fc8956c9cf 100644 --- a/src/detect-http-header.c +++ b/src/detect-http-header.c @@ -146,6 +146,27 @@ static uint8_t *GetBufferForTX(htp_tx_t *tx, uint64_t tx_id, return buf->buffer; } +static InspectionBuffer *GetBuffer2ForTX(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flow_flags, void *txv, + const int list_id) +{ + InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); + if (buffer->inspect == NULL) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (rs_http2_tx_get_headers(txv, flow_flags, &b, &b_len) != 1) + return NULL; + if (b == NULL || b_len == 0) + return NULL; + + InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); + InspectionBufferApplyTransforms(buffer, transforms); + } + + return buffer; +} + /** \internal * \brief custom inspect function to utilize the cached headers */ @@ -439,6 +460,16 @@ void DetectHttpHeaderRegister(void) PrefilterMpmHttpHeaderResponseRegister, NULL, ALPROTO_HTTP, 0); /* not used, registered twice: HEADERS/TRAILER */ + DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, + HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); + DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, + GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataClient); + + DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, + HTTP2StateDataServer, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); + DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, + GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataServer); + DetectBufferTypeSetDescriptionByName("http_header", "http headers"); diff --git a/src/detect-http-raw-header.c b/src/detect-http-raw-header.c index e75dbb96eea4..c7646fcc792f 100644 --- a/src/detect-http-raw-header.c +++ b/src/detect-http-raw-header.c @@ -62,6 +62,9 @@ static int g_http_raw_header_buffer_id = 0; static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flow_flags, void *txv, const int list_id); +static InspectionBuffer *GetData2(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flow_flags, void *txv, + const int list_id); static int PrefilterMpmHttpHeaderRawRequestRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, @@ -107,6 +110,16 @@ void DetectHttpRawHeaderRegister(void) PrefilterMpmHttpHeaderRawResponseRegister, NULL, ALPROTO_HTTP, 0); /* progress handled in register */ + DetectAppLayerInspectEngineRegister2("http_raw_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, + HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetData2); + DetectAppLayerInspectEngineRegister2("http_raw_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, + HTTP2StateDataServer, DetectEngineInspectBufferGeneric, GetData2); + + DetectAppLayerMpmRegister2("http_raw_header", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, + GetData2, ALPROTO_HTTP2, HTTP2StateDataClient); + DetectAppLayerMpmRegister2("http_raw_header", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, + GetData2, ALPROTO_HTTP2, HTTP2StateDataServer); + DetectBufferTypeSetDescriptionByName("http_raw_header", "raw http headers"); @@ -196,6 +209,27 @@ static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, return buffer; } +static InspectionBuffer *GetData2(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flow_flags, void *txv, + const int list_id) +{ + InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); + if (buffer->inspect == NULL) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (rs_http2_tx_get_headers_raw(txv, flow_flags, &b, &b_len) != 1) + return NULL; + if (b == NULL || b_len == 0) + return NULL; + + InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); + InspectionBufferApplyTransforms(buffer, transforms); + } + + return buffer; +} + typedef struct PrefilterMpmHttpHeaderRawCtx { int list_id; const MpmCtx *mpm_ctx; From 70671adce762a0f30bad1ad467c7620c8c47998d Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 31 May 2021 11:40:42 +0200 Subject: [PATCH 5/8] http2: generic http2_header_blocks so as not to forget continuation and push promise when iterating over headers (cherry picked from commit e3ff0e7731e8d652ee7f22945311674e65455a74) --- rust/src/http2/detect.rs | 238 ++++++++++++++++++--------------------- 1 file changed, 107 insertions(+), 131 deletions(-) diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index c7a800c007b6..60c288881554 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -355,38 +355,38 @@ pub unsafe extern "C" fn rs_detect_u64_free(ctx: *mut std::os::raw::c_void) { } fn http2_detect_sizeupdate_match( - hd: &parser::HTTP2FrameHeaders, ctx: &parser::DetectU64Data, + blocks: &Vec, ctx: &parser::DetectU64Data, ) -> std::os::raw::c_int { - for i in 0..hd.blocks.len() { + for i in 0..blocks.len() { match ctx.mode { parser::DetectUintMode::DetectUintModeEqual => { - if hd.blocks[i].sizeupdate == ctx.value - && hd.blocks[i].error + if blocks[i].sizeupdate == ctx.value + && blocks[i].error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } } parser::DetectUintMode::DetectUintModeLt => { - if hd.blocks[i].sizeupdate <= ctx.value - && hd.blocks[i].error + if blocks[i].sizeupdate <= ctx.value + && blocks[i].error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } } parser::DetectUintMode::DetectUintModeGt => { - if hd.blocks[i].sizeupdate >= ctx.value - && hd.blocks[i].error + if blocks[i].sizeupdate >= ctx.value + && blocks[i].error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } } parser::DetectUintMode::DetectUintModeRange => { - if hd.blocks[i].sizeupdate <= ctx.value - && hd.blocks[i].sizeupdate >= ctx.valrange - && hd.blocks[i].error + if blocks[i].sizeupdate <= ctx.value + && blocks[i].sizeupdate >= ctx.valrange + && blocks[i].error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; @@ -397,29 +397,39 @@ fn http2_detect_sizeupdate_match( return 0; } +fn http2_header_blocks(frame: &HTTP2Frame) -> Option<&Vec> { + match &frame.data { + HTTP2FrameTypeData::HEADERS(hd) => { + return Some(&hd.blocks); + } + HTTP2FrameTypeData::CONTINUATION(hd) => { + return Some(&hd.blocks); + } + HTTP2FrameTypeData::PUSHPROMISE(hd) => { + return Some(&hd.blocks); + } + _ => {} + } + return None; +} + fn http2_detect_sizeupdatectx_match( ctx: &mut parser::DetectU64Data, tx: &mut HTTP2Transaction, direction: u8, ) -> std::os::raw::c_int { if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { - match &tx.frames_ts[i].data { - HTTP2FrameTypeData::HEADERS(hd) => { - if http2_detect_sizeupdate_match(&hd, ctx) != 0 { - return 1; - } + if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) { + if http2_detect_sizeupdate_match(blocks, ctx) != 0 { + return 1; } - _ => {} } } } else { for i in 0..tx.frames_tc.len() { - match &tx.frames_tc[i].data { - HTTP2FrameTypeData::HEADERS(hd) => { - if http2_detect_sizeupdate_match(&hd, ctx) != 0 { - return 1; - } + if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) { + if http2_detect_sizeupdate_match(blocks, ctx) != 0 { + return 1; } - _ => {} } } } @@ -444,34 +454,28 @@ pub unsafe extern "C" fn rs_http2_tx_get_header_name( let mut pos = 0 as u32; if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { - match &tx.frames_ts[i].data { - HTTP2FrameTypeData::HEADERS(hd) => { - if nb < pos + hd.blocks.len() as u32 { - let value = &hd.blocks[(nb - pos) as usize].name; - *buffer = value.as_ptr(); //unsafe - *buffer_len = value.len() as u32; - return 1; - } else { - pos = pos + hd.blocks.len() as u32; - } + if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) { + if nb < pos + blocks.len() as u32 { + let value = &blocks[(nb - pos) as usize].name; + *buffer = value.as_ptr(); //unsafe + *buffer_len = value.len() as u32; + return 1; + } else { + pos = pos + blocks.len() as u32; } - _ => {} } } } else { for i in 0..tx.frames_tc.len() { - match &tx.frames_tc[i].data { - HTTP2FrameTypeData::HEADERS(hd) => { - if nb < pos + hd.blocks.len() as u32 { - let value = &hd.blocks[(nb - pos) as usize].name; - *buffer = value.as_ptr(); //unsafe - *buffer_len = value.len() as u32; - return 1; - } else { - pos = pos + hd.blocks.len() as u32; - } + if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) { + if nb < pos + blocks.len() as u32 { + let value = &blocks[(nb - pos) as usize].name; + *buffer = value.as_ptr(); //unsafe + *buffer_len = value.len() as u32; + return 1; + } else { + pos = pos + blocks.len() as u32; } - _ => {} } } } @@ -493,23 +497,10 @@ fn http2_frames_get_header_value<'a>( frames: &'a Vec, name: &str, ) -> Result<&'a [u8], ()> { for i in 0..frames.len() { - match &frames[i].data { - HTTP2FrameTypeData::HEADERS(hd) => { - if let Ok(value) = http2_blocks_get_header_value(&hd.blocks, name) { - return Ok(value); - } + if let Some(blocks) = http2_header_blocks(&frames[i]) { + if let Ok(value) = http2_blocks_get_header_value(&blocks, name) { + return Ok(value); } - HTTP2FrameTypeData::PUSHPROMISE(hd) => { - if let Ok(value) = http2_blocks_get_header_value(&hd.blocks, name) { - return Ok(value); - } - } - HTTP2FrameTypeData::CONTINUATION(hd) => { - if let Ok(value) = http2_blocks_get_header_value(&hd.blocks, name) { - return Ok(value); - } - } - _ => {} } } @@ -671,21 +662,21 @@ pub unsafe extern "C" fn rs_http2_tx_get_header_value( return 0; } -fn http2_escape_header(hd: &parser::HTTP2FrameHeaders, i: u32) -> Vec { +fn http2_escape_header(blocks: &Vec, i: u32) -> Vec { //minimum size + 2 for escapes - let normalsize = hd.blocks[i as usize].value.len() + 2 + hd.blocks[i as usize].name.len() + 2; + let normalsize = blocks[i as usize].value.len() + 2 + blocks[i as usize].name.len() + 2; let mut vec = Vec::with_capacity(normalsize); - for j in 0..hd.blocks[i as usize].name.len() { - vec.push(hd.blocks[i as usize].name[j]); - if hd.blocks[i as usize].name[j] == ':' as u8 { + for j in 0..blocks[i as usize].name.len() { + vec.push(blocks[i as usize].name[j]); + if blocks[i as usize].name[j] == ':' as u8 { vec.push(':' as u8); } } vec.push(':' as u8); vec.push(' ' as u8); - for j in 0..hd.blocks[i as usize].value.len() { - vec.push(hd.blocks[i as usize].value[j]); - if hd.blocks[i as usize].value[j] == ':' as u8 { + for j in 0..blocks[i as usize].value.len() { + vec.push(blocks[i as usize].value[j]); + if blocks[i as usize].value[j] == ':' as u8 { vec.push(':' as u8); } } @@ -705,16 +696,13 @@ pub unsafe extern "C" fn rs_http2_tx_get_header_names( &tx.frames_tc }; for i in 0..frames.len() { - match &frames[i].data { - HTTP2FrameTypeData::HEADERS(hd) => { - for j in 0..hd.blocks.len() { - // we do not escape linefeeds in headers names - vec.extend_from_slice(&hd.blocks[j].name); - vec.push('\r' as u8); - vec.push('\n' as u8); - } + if let Some(blocks) = http2_header_blocks(&frames[i]) { + for j in 0..blocks.len() { + // we do not escape linefeeds in headers names + vec.extend_from_slice(&blocks[j].name); + vec.push('\r' as u8); + vec.push('\n' as u8); } - _ => {} } } if vec.len() > 2 { @@ -776,21 +764,18 @@ pub unsafe extern "C" fn rs_http2_tx_get_headers( &tx.frames_tc }; for i in 0..frames.len() { - match &frames[i].data { - HTTP2FrameTypeData::HEADERS(hd) => { - for j in 0..hd.blocks.len() { - if !http2_header_iscookie(direction, &hd.blocks[j].name) { - // we do not escape linefeeds nor : in headers names - vec.extend_from_slice(&hd.blocks[j].name); - vec.push(':' as u8); - vec.push(' ' as u8); - vec.extend_from_slice(http2_header_trimspaces(&hd.blocks[j].value)); - vec.push('\r' as u8); - vec.push('\n' as u8); - } + if let Some(blocks) = http2_header_blocks(&frames[i]) { + for j in 0..blocks.len() { + if !http2_header_iscookie(direction, &blocks[j].name) { + // we do not escape linefeeds nor : in headers names + vec.extend_from_slice(&blocks[j].name); + vec.push(':' as u8); + vec.push(' ' as u8); + vec.extend_from_slice(http2_header_trimspaces(&blocks[j].value)); + vec.push('\r' as u8); + vec.push('\n' as u8); } } - _ => {} } } if vec.len() > 0 { @@ -815,19 +800,16 @@ pub unsafe extern "C" fn rs_http2_tx_get_headers_raw( &tx.frames_tc }; for i in 0..frames.len() { - match &frames[i].data { - HTTP2FrameTypeData::HEADERS(hd) => { - for j in 0..hd.blocks.len() { - // we do not escape linefeeds nor : in headers names - vec.extend_from_slice(&hd.blocks[j].name); - vec.push(':' as u8); - vec.push(' ' as u8); - vec.extend_from_slice(&hd.blocks[j].value); - vec.push('\r' as u8); - vec.push('\n' as u8); - } + if let Some(blocks) = http2_header_blocks(&frames[i]) { + for j in 0..blocks.len() { + // we do not escape linefeeds nor : in headers names + vec.extend_from_slice(&blocks[j].name); + vec.push(':' as u8); + vec.push(' ' as u8); + vec.extend_from_slice(&blocks[j].value); + vec.push('\r' as u8); + vec.push('\n' as u8); } - _ => {} } } if vec.len() > 0 { @@ -848,40 +830,34 @@ pub unsafe extern "C" fn rs_http2_tx_get_header( let mut pos = 0 as u32; if direction & STREAM_TOSERVER != 0 { for i in 0..tx.frames_ts.len() { - match &tx.frames_ts[i].data { - HTTP2FrameTypeData::HEADERS(hd) => { - if nb < pos + hd.blocks.len() as u32 { - let ehdr = http2_escape_header(&hd, nb - pos); - tx.escaped.push(ehdr); - let idx = tx.escaped.len() - 1; - let value = &tx.escaped[idx]; - *buffer = value.as_ptr(); //unsafe - *buffer_len = value.len() as u32; - return 1; - } else { - pos = pos + hd.blocks.len() as u32; - } + if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) { + if nb < pos + blocks.len() as u32 { + let ehdr = http2_escape_header(&blocks, nb - pos); + tx.escaped.push(ehdr); + let idx = tx.escaped.len() - 1; + let value = &tx.escaped[idx]; + *buffer = value.as_ptr(); //unsafe + *buffer_len = value.len() as u32; + return 1; + } else { + pos = pos + blocks.len() as u32; } - _ => {} } } } else { for i in 0..tx.frames_tc.len() { - match &tx.frames_tc[i].data { - HTTP2FrameTypeData::HEADERS(hd) => { - if nb < pos + hd.blocks.len() as u32 { - let ehdr = http2_escape_header(&hd, nb - pos); - tx.escaped.push(ehdr); - let idx = tx.escaped.len() - 1; - let value = &tx.escaped[idx]; - *buffer = value.as_ptr(); //unsafe - *buffer_len = value.len() as u32; - return 1; - } else { - pos = pos + hd.blocks.len() as u32; - } + if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) { + if nb < pos + blocks.len() as u32 { + let ehdr = http2_escape_header(&blocks, nb - pos); + tx.escaped.push(ehdr); + let idx = tx.escaped.len() - 1; + let value = &tx.escaped[idx]; + *buffer = value.as_ptr(); //unsafe + *buffer_len = value.len() as u32; + return 1; + } else { + pos = pos + blocks.len() as u32; } - _ => {} } } } From ae5874ef0c1caacfc7693217efe51b8663635959 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 31 May 2021 16:17:22 +0200 Subject: [PATCH 6/8] http2: concatenate one headers multiple values For detection, as is done with HTTP1 (cherry picked from commit 48ed874ddad4bbe7321191712263086f4567a5ab) --- rust/src/http2/detect.rs | 132 +++++++++++++++++++++++++++++++-------- 1 file changed, 107 insertions(+), 25 deletions(-) diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index 60c288881554..cf0359cbce5b 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -19,7 +19,7 @@ use super::http2::{ HTTP2Event, HTTP2Frame, HTTP2FrameTypeData, HTTP2State, HTTP2Transaction, HTTP2TransactionState, }; use super::parser; -use crate::core::STREAM_TOSERVER; +use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER}; use std::ffi::CStr; use std::mem::transmute; use std::str::FromStr; @@ -482,36 +482,78 @@ pub unsafe extern "C" fn rs_http2_tx_get_header_name( return 0; } -fn http2_blocks_get_header_value<'a>( - blocks: &'a Vec, name: &str, +fn http2_frames_get_header_firstvalue<'a>( + tx: &'a mut HTTP2Transaction, direction: u8, name: &str, ) -> Result<&'a [u8], ()> { - for j in 0..blocks.len() { - if blocks[j].name == name.as_bytes().to_vec() { - return Ok(&blocks[j].value); + let frames = if direction == STREAM_TOSERVER { + &tx.frames_ts + } else { + &tx.frames_tc + }; + for i in 0..frames.len() { + if let Some(blocks) = http2_header_blocks(&frames[i]) { + for j in 0..blocks.len() { + if blocks[j].name == name.as_bytes().to_vec() { + return Ok(&blocks[j].value); + } + } } } return Err(()); } fn http2_frames_get_header_value<'a>( - frames: &'a Vec, name: &str, + tx: &'a mut HTTP2Transaction, direction: u8, name: &str, ) -> Result<&'a [u8], ()> { + let mut found = 0; + let mut vec = Vec::new(); + let mut single: Result<&[u8], ()> = Err(()); + let frames = if direction == STREAM_TOSERVER { + &tx.frames_ts + } else { + &tx.frames_tc + }; for i in 0..frames.len() { if let Some(blocks) = http2_header_blocks(&frames[i]) { - if let Ok(value) = http2_blocks_get_header_value(&blocks, name) { - return Ok(value); + for j in 0..blocks.len() { + if blocks[j].name == name.as_bytes().to_vec() { + if found == 0 { + single = Ok(&blocks[j].value); + found = 1; + } else if found == 1 { + if let Ok(s) = single { + vec.extend_from_slice(s); + } + vec.push(b','); + vec.push(b' '); + vec.extend_from_slice(&blocks[j].value); + found = 2; + } else { + vec.push(b','); + vec.push(b' '); + vec.extend_from_slice(&blocks[j].value); + } + } } } } - - return Err(()); + if found == 0 { + return Err(()); + } else if found == 1 { + return single; + } else { + tx.escaped.push(vec); + let idx = tx.escaped.len() - 1; + let value = &tx.escaped[idx]; + return Ok(&value); + } } #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_uri( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { - if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, ":path") { + if let Ok(value) = http2_frames_get_header_firstvalue(tx, STREAM_TOSERVER, ":path") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; @@ -523,7 +565,7 @@ pub unsafe extern "C" fn rs_http2_tx_get_uri( pub unsafe extern "C" fn rs_http2_tx_get_method( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { - if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, ":method") { + if let Ok(value) = http2_frames_get_header_firstvalue(tx, STREAM_TOSERVER, ":method") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; @@ -535,7 +577,7 @@ pub unsafe extern "C" fn rs_http2_tx_get_method( pub unsafe extern "C" fn rs_http2_tx_get_host( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { - if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, ":authority") { + if let Ok(value) = http2_frames_get_header_value(tx, STREAM_TOSERVER, ":authority") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; @@ -574,7 +616,7 @@ fn http2_normalize_host(value: &[u8]) -> (Option>, usize) { pub unsafe extern "C" fn rs_http2_tx_get_host_norm( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { - if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, ":authority") { + if let Ok(value) = http2_frames_get_header_value(tx, STREAM_TOSERVER, ":authority") { let r = http2_normalize_host(value); // r is a tuple with the value and its size // this is useful when we only take a substring (before the port) @@ -601,7 +643,7 @@ pub unsafe extern "C" fn rs_http2_tx_get_host_norm( pub unsafe extern "C" fn rs_http2_tx_get_useragent( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { - if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, "user-agent") { + if let Ok(value) = http2_frames_get_header_value(tx, STREAM_TOSERVER, "user-agent") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; @@ -613,7 +655,7 @@ pub unsafe extern "C" fn rs_http2_tx_get_useragent( pub unsafe extern "C" fn rs_http2_tx_get_status( tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { - if let Ok(value) = http2_frames_get_header_value(&tx.frames_tc, ":status") { + if let Ok(value) = http2_frames_get_header_firstvalue(tx, STREAM_TOCLIENT, ":status") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; @@ -626,13 +668,13 @@ pub unsafe extern "C" fn rs_http2_tx_get_cookie( tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { if direction == STREAM_TOSERVER { - if let Ok(value) = http2_frames_get_header_value(&tx.frames_ts, "cookie") { + if let Ok(value) = http2_frames_get_header_value(tx, STREAM_TOSERVER, "cookie") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; } } else { - if let Ok(value) = http2_frames_get_header_value(&tx.frames_tc, "set-cookie") { + if let Ok(value) = http2_frames_get_header_value(tx, STREAM_TOCLIENT, "set-cookie") { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; @@ -648,12 +690,7 @@ pub unsafe extern "C" fn rs_http2_tx_get_header_value( ) -> u8 { let hname: &CStr = CStr::from_ptr(strname); //unsafe if let Ok(s) = hname.to_str() { - let frames = if direction == STREAM_TOSERVER { - &tx.frames_ts - } else { - &tx.frames_tc - }; - if let Ok(value) = http2_frames_get_header_value(frames, &s.to_lowercase()) { + if let Ok(value) = http2_frames_get_header_value(tx, direction, &s.to_lowercase()) { *buffer = value.as_ptr(); //unsafe *buffer_len = value.len() as u32; return 1; @@ -1116,4 +1153,49 @@ mod tests { let r2 = http2_header_trimspaces(buf2); assert_eq!(r2, "".as_bytes()); } + + #[test] + fn test_http2_frames_get_header_value() { + let mut tx = HTTP2Transaction::new(); + let head = parser::HTTP2FrameHeader { + length: 0, + ftype: parser::HTTP2FrameType::HEADERS as u8, + flags: 0, + reserved: 0, + stream_id: 1, + }; + let mut blocks = Vec::new(); + let b = parser::HTTP2FrameHeaderBlock { + name: "Host".as_bytes().to_vec(), + value: "abc.com".as_bytes().to_vec(), + error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, + sizeupdate: 0, + }; + blocks.push(b); + let b2 = parser::HTTP2FrameHeaderBlock { + name: "Host".as_bytes().to_vec(), + value: "efg.net".as_bytes().to_vec(), + error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, + sizeupdate: 0, + }; + blocks.push(b2); + let hs = parser::HTTP2FrameHeaders { + padlength: None, + priority: None, + blocks: blocks, + }; + let txdata = HTTP2FrameTypeData::HEADERS(hs); + tx.frames_ts.push(HTTP2Frame { + header: head, + data: txdata, + }); + match http2_frames_get_header_value(&mut tx, STREAM_TOSERVER, "Host") { + Ok(x) => { + assert_eq!(x, "abc.com, efg.net".as_bytes()); + } + Err(e) => { + panic!("Result should not have been an error: {:?}", e); + } + } + } } From 944c2cc30d26a6661410cc251ad97f0eb3133917 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Fri, 4 Jun 2021 15:18:32 +0200 Subject: [PATCH 7/8] http2: better rust style (cherry picked from commit 596a4a9d6edf43c73de85356b2e0cec3cb91e471) --- rust/src/http2/detect.rs | 99 +++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 56 deletions(-) diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index cf0359cbce5b..24436843edfa 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -260,7 +260,7 @@ pub unsafe extern "C" fn rs_http2_detect_settingsctx_free(ctx: *mut std::os::raw } fn http2_detect_settings_match( - set: &Vec, ctx: &parser::DetectHTTP2settingsSigCtx, + set: &[parser::HTTP2FrameSettings], ctx: &parser::DetectHTTP2settingsSigCtx, ) -> std::os::raw::c_int { for i in 0..set.len() { if set[i].id == ctx.id { @@ -355,39 +355,35 @@ pub unsafe extern "C" fn rs_detect_u64_free(ctx: *mut std::os::raw::c_void) { } fn http2_detect_sizeupdate_match( - blocks: &Vec, ctx: &parser::DetectU64Data, + blocks: &[parser::HTTP2FrameHeaderBlock], ctx: &parser::DetectU64Data, ) -> std::os::raw::c_int { - for i in 0..blocks.len() { + for block in blocks.iter() { match ctx.mode { parser::DetectUintMode::DetectUintModeEqual => { - if blocks[i].sizeupdate == ctx.value - && blocks[i].error - == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate + if block.sizeupdate == ctx.value + && block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } } parser::DetectUintMode::DetectUintModeLt => { - if blocks[i].sizeupdate <= ctx.value - && blocks[i].error - == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate + if block.sizeupdate <= ctx.value + && block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } } parser::DetectUintMode::DetectUintModeGt => { - if blocks[i].sizeupdate >= ctx.value - && blocks[i].error - == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate + if block.sizeupdate >= ctx.value + && block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } } parser::DetectUintMode::DetectUintModeRange => { - if blocks[i].sizeupdate <= ctx.value - && blocks[i].sizeupdate >= ctx.valrange - && blocks[i].error - == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate + if block.sizeupdate <= ctx.value + && block.sizeupdate >= ctx.valrange + && block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { return 1; } @@ -397,7 +393,7 @@ fn http2_detect_sizeupdate_match( return 0; } -fn http2_header_blocks(frame: &HTTP2Frame) -> Option<&Vec> { +fn http2_header_blocks(frame: &HTTP2Frame) -> Option<&[parser::HTTP2FrameHeaderBlock]> { match &frame.data { HTTP2FrameTypeData::HEADERS(hd) => { return Some(&hd.blocks); @@ -492,9 +488,9 @@ fn http2_frames_get_header_firstvalue<'a>( }; for i in 0..frames.len() { if let Some(blocks) = http2_header_blocks(&frames[i]) { - for j in 0..blocks.len() { - if blocks[j].name == name.as_bytes().to_vec() { - return Ok(&blocks[j].value); + for block in blocks.iter() { + if block.name == name.as_bytes().to_vec() { + return Ok(&block.value); } } } @@ -515,23 +511,21 @@ fn http2_frames_get_header_value<'a>( }; for i in 0..frames.len() { if let Some(blocks) = http2_header_blocks(&frames[i]) { - for j in 0..blocks.len() { - if blocks[j].name == name.as_bytes().to_vec() { + for block in blocks.iter() { + if block.name == name.as_bytes().to_vec() { if found == 0 { - single = Ok(&blocks[j].value); + single = Ok(&block.value); found = 1; } else if found == 1 { if let Ok(s) = single { vec.extend_from_slice(s); } - vec.push(b','); - vec.push(b' '); - vec.extend_from_slice(&blocks[j].value); + vec.extend_from_slice(&[b',', b' ']); + vec.extend_from_slice(&block.value); found = 2; } else { - vec.push(b','); - vec.push(b' '); - vec.extend_from_slice(&blocks[j].value); + vec.extend_from_slice(&[b',', b' ']); + vec.extend_from_slice(&block.value); } } } @@ -622,6 +616,8 @@ pub unsafe extern "C" fn rs_http2_tx_get_host_norm( // this is useful when we only take a substring (before the port) match r.0 { Some(normval) => { + // In case we needed some normalization, + // the transaction needs to take ownership of this normalized host tx.escaped.push(normval); let idx = tx.escaped.len() - 1; let resvalue = &tx.escaped[idx]; @@ -699,7 +695,7 @@ pub unsafe extern "C" fn rs_http2_tx_get_header_value( return 0; } -fn http2_escape_header(blocks: &Vec, i: u32) -> Vec { +fn http2_escape_header(blocks: &[parser::HTTP2FrameHeaderBlock], i: u32) -> Vec { //minimum size + 2 for escapes let normalsize = blocks[i as usize].value.len() + 2 + blocks[i as usize].name.len() + 2; let mut vec = Vec::with_capacity(normalsize); @@ -709,8 +705,7 @@ fn http2_escape_header(blocks: &Vec, i: u32) -> V vec.push(':' as u8); } } - vec.push(':' as u8); - vec.push(' ' as u8); + vec.extend_from_slice(&[b':', b' ']); for j in 0..blocks[i as usize].value.len() { vec.push(blocks[i as usize].value[j]); if blocks[i as usize].value[j] == ':' as u8 { @@ -724,9 +719,7 @@ fn http2_escape_header(blocks: &Vec, i: u32) -> V pub unsafe extern "C" fn rs_http2_tx_get_header_names( tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, ) -> u8 { - let mut vec = Vec::new(); - vec.push('\r' as u8); - vec.push('\n' as u8); + let mut vec = vec![b'\r', b'\n']; let frames = if direction & STREAM_TOSERVER != 0 { &tx.frames_ts } else { @@ -734,17 +727,15 @@ pub unsafe extern "C" fn rs_http2_tx_get_header_names( }; for i in 0..frames.len() { if let Some(blocks) = http2_header_blocks(&frames[i]) { - for j in 0..blocks.len() { + for block in blocks.iter() { // we do not escape linefeeds in headers names - vec.extend_from_slice(&blocks[j].name); - vec.push('\r' as u8); - vec.push('\n' as u8); + vec.extend_from_slice(&block.name); + vec.extend_from_slice(&[b'\r', b'\n']); } } } if vec.len() > 2 { - vec.push('\r' as u8); - vec.push('\n' as u8); + vec.extend_from_slice(&[b'\r', b'\n']); tx.escaped.push(vec); let idx = tx.escaped.len() - 1; let value = &tx.escaped[idx]; @@ -802,15 +793,13 @@ pub unsafe extern "C" fn rs_http2_tx_get_headers( }; for i in 0..frames.len() { if let Some(blocks) = http2_header_blocks(&frames[i]) { - for j in 0..blocks.len() { - if !http2_header_iscookie(direction, &blocks[j].name) { + for block in blocks.iter() { + if !http2_header_iscookie(direction, &block.name) { // we do not escape linefeeds nor : in headers names - vec.extend_from_slice(&blocks[j].name); - vec.push(':' as u8); - vec.push(' ' as u8); - vec.extend_from_slice(http2_header_trimspaces(&blocks[j].value)); - vec.push('\r' as u8); - vec.push('\n' as u8); + vec.extend_from_slice(&block.name); + vec.extend_from_slice(&[b':', b' ']); + vec.extend_from_slice(http2_header_trimspaces(&block.value)); + vec.extend_from_slice(&[b'\r', b'\n']); } } } @@ -838,14 +827,12 @@ pub unsafe extern "C" fn rs_http2_tx_get_headers_raw( }; for i in 0..frames.len() { if let Some(blocks) = http2_header_blocks(&frames[i]) { - for j in 0..blocks.len() { + for block in blocks.iter() { // we do not escape linefeeds nor : in headers names - vec.extend_from_slice(&blocks[j].name); - vec.push(':' as u8); - vec.push(' ' as u8); - vec.extend_from_slice(&blocks[j].value); - vec.push('\r' as u8); - vec.push('\n' as u8); + vec.extend_from_slice(&block.name); + vec.extend_from_slice(&[b':', b' ']); + vec.extend_from_slice(&block.value); + vec.extend_from_slice(&[b'\r', b'\n']); } } } From cb2ab13765950c9025295ddb4273c696546f00f2 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Tue, 7 Sep 2021 14:44:25 +0200 Subject: [PATCH 8/8] http2: better rust lifetimes so that borrow check gets happy (cherry picked from commit 56fae072b225d1c175116efe117c264afbdbf4a3) --- rust/src/http2/decompression.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/src/http2/decompression.rs b/rust/src/http2/decompression.rs index 74637e0f9a73..380fcf3a82de 100644 --- a/rust/src/http2/decompression.rs +++ b/rust/src/http2/decompression.rs @@ -187,7 +187,7 @@ impl HTTP2DecoderHalf { } pub fn decompress<'a>( - &'a mut self, input: &'a [u8], output: &'a mut Vec, + &mut self, input: &'a [u8], output: &'a mut Vec, ) -> io::Result<&'a [u8]> { match self.decoder { HTTP2Decompresser::GZIP(ref mut gzip_decoder) => { @@ -249,7 +249,7 @@ impl HTTP2Decoder { } pub fn decompress<'a>( - &'a mut self, input: &'a [u8], output: &'a mut Vec, dir: u8, + &mut self, input: &'a [u8], output: &'a mut Vec, dir: u8, ) -> io::Result<&'a [u8]> { if dir == STREAM_TOCLIENT { return self.decoder_tc.decompress(input, output);