detect/http2: use ThreadCtx for all headers keywords

Instead of custom inefficient "escaped" Vec

Ticket: 8291
pull/15004/head
Philippe Antoine 5 months ago committed by Victor Julien
parent 4e538dfa3b
commit 062f56ddde

@ -416,12 +416,18 @@ pub fn http2_frames_get_header_value_vec(
}
}
#[derive(Debug, PartialEq)]
enum Http2Header<'a> {
Single(&'a [u8]),
Multiple(Vec<u8>),
}
fn http2_frames_get_header_value<'a>(
tx: &'a mut HTTP2Transaction, direction: Direction, name: &str,
) -> Result<&'a [u8], ()> {
tx: &'a HTTP2Transaction, direction: Direction, name: &str,
) -> Option<Http2Header<'a>> {
let mut found = 0;
let mut vec = Vec::new();
let mut single: Result<&[u8], ()> = Err(());
let mut single = None;
let frames = if direction == Direction::ToServer {
&tx.frames_ts
} else {
@ -432,10 +438,10 @@ fn http2_frames_get_header_value<'a>(
for block in blocks.iter() {
if block.name.as_ref() == name.as_bytes() {
if found == 0 {
single = Ok(&block.value);
single = Some(Http2Header::Single(&block.value));
found = 1;
} else if found == 1 && Rc::strong_count(&block.name) <= 2 {
if let Ok(s) = single {
if let Some(Http2Header::Single(s)) = single {
vec.extend_from_slice(s);
}
vec.extend_from_slice(b", ");
@ -450,14 +456,11 @@ fn http2_frames_get_header_value<'a>(
}
}
if found == 0 {
return Err(());
return None;
} 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);
return Some(Http2Header::Multiple(vec));
}
}
@ -553,92 +556,115 @@ pub unsafe extern "C" fn SCHttp2TxGetMethod(
#[no_mangle]
pub unsafe extern "C" fn SCHttp2TxGetHost(
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
) -> u8 {
if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
*buffer = value.as_ptr(); //unsafe
*buffer_len = value.len() as u32;
return 1;
}
return 0;
}
fn http2_lower(value: &[u8]) -> Option<Vec<u8>> {
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<u8> = Vec::with_capacity(value.len());
vec.extend_from_slice(value);
for e in &mut vec {
e.make_ascii_lowercase();
let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
match value {
Http2Header::Single(v) => {
*buffer = v.as_ptr(); //unsafe
*buffer_len = v.len() as u32;
}
Http2Header::Multiple(v) => {
tbuf.data = v;
*buffer = tbuf.data.as_ptr(); //unsafe
*buffer_len = tbuf.data.len() as u32;
}
return Some(vec);
}
return 1;
}
return None;
return 0;
}
// returns a tuple with the value and its size
fn http2_normalize_host(value: &[u8]) -> &[u8] {
match value.iter().position(|&x| x == b'@') {
Some(i) => {
let value = &value[i + 1..];
match value.iter().position(|&x| x == b':') {
Some(i) => {
return &value[..i];
fn http2_normalize_host(ve: Http2Header) -> Http2Header {
let vs = match &ve {
Http2Header::Single(v) => v,
Http2Header::Multiple(v) => v.as_slice(),
};
let (start, end) = match vs.iter().position(|&x| x == b'@') {
Some(i) => match &vs[i + 1..].iter().position(|&x| x == b':') {
Some(j) => (i + 1, i + 1 + j),
None => (i + 1, vs.len()),
},
None => match vs.iter().position(|&x| x == b':') {
Some(i) => (0, i),
None => (0, vs.len()),
},
};
return match ve {
Http2Header::Single(v) => {
let mut need_transform = false;
for c in v.iter().take(end).skip(start) {
if c.is_ascii_uppercase() {
need_transform = true;
break;
}
None => {
return value;
}
if need_transform {
let mut vec: Vec<u8> = Vec::with_capacity(end - start);
for c in v.iter().take(end).skip(start) {
vec.push(c.to_ascii_lowercase());
}
Http2Header::Multiple(vec)
} else {
Http2Header::Single(&v[start..end])
}
}
None => match value.iter().position(|&x| x == b':') {
Some(i) => {
return &value[..i];
Http2Header::Multiple(mut v) => {
if end < v.len() {
v.truncate(end);
}
None => {
return value;
if start > 0 {
v.drain(0..start);
}
},
}
for e in &mut v {
e.make_ascii_lowercase();
}
Http2Header::Multiple(v)
}
};
}
#[no_mangle]
pub unsafe extern "C" fn SCHttp2TxGetHostNorm(
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
) -> u8 {
if let Ok(value) = http2_frames_get_header_value(tx, Direction::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)
match http2_lower(r) {
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];
*buffer = resvalue.as_ptr(); //unsafe
*buffer_len = resvalue.len() as u32;
return 1;
let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
match http2_normalize_host(value) {
Http2Header::Single(v) => {
*buffer = v.as_ptr(); //unsafe
*buffer_len = v.len() as u32;
}
None => {
*buffer = r.as_ptr(); //unsafe
*buffer_len = r.len() as u32;
return 1;
Http2Header::Multiple(v) => {
tbuf.data = v;
*buffer = tbuf.data.as_ptr(); //unsafe
*buffer_len = tbuf.data.len() as u32;
}
}
return 1;
}
return 0;
}
#[no_mangle]
pub unsafe extern "C" fn SCHttp2TxGetUserAgent(
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
) -> u8 {
if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, "user-agent") {
*buffer = value.as_ptr(); //unsafe
*buffer_len = value.len() as u32;
let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, "user-agent") {
match value {
Http2Header::Single(v) => {
*buffer = v.as_ptr(); //unsafe
*buffer_len = v.len() as u32;
}
Http2Header::Multiple(v) => {
tbuf.data = v;
*buffer = tbuf.data.as_ptr(); //unsafe
*buffer_len = tbuf.data.len() as u32;
}
}
return 1;
}
return 0;
@ -659,16 +685,37 @@ pub unsafe extern "C" fn SCHttp2TxGetStatus(
#[no_mangle]
pub unsafe extern "C" fn SCHttp2TxGetCookie(
tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
tbuf: *mut c_void,
) -> u8 {
let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
if direction == u8::from(Direction::ToServer) {
if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, "cookie") {
*buffer = value.as_ptr(); //unsafe
*buffer_len = value.len() as u32;
if let Some(value) = http2_frames_get_header_value(tx, Direction::ToServer, "cookie") {
match value {
Http2Header::Single(v) => {
*buffer = v.as_ptr(); //unsafe
*buffer_len = v.len() as u32;
}
Http2Header::Multiple(v) => {
tbuf.data = v;
*buffer = tbuf.data.as_ptr(); //unsafe
*buffer_len = tbuf.data.len() as u32;
}
}
return 1;
}
} else if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToClient, "set-cookie") {
*buffer = value.as_ptr(); //unsafe
*buffer_len = value.len() as u32;
} else if let Some(value) = http2_frames_get_header_value(tx, Direction::ToClient, "set-cookie")
{
match value {
Http2Header::Single(v) => {
*buffer = v.as_ptr(); //unsafe
*buffer_len = v.len() as u32;
}
Http2Header::Multiple(v) => {
tbuf.data = v;
*buffer = tbuf.data.as_ptr(); //unsafe
*buffer_len = tbuf.data.len() as u32;
}
}
return 1;
}
return 0;
@ -677,13 +724,24 @@ pub unsafe extern "C" fn SCHttp2TxGetCookie(
#[no_mangle]
pub unsafe extern "C" fn SCHttp2TxGetHeaderValue(
tx: &mut HTTP2Transaction, direction: u8, strname: *const std::os::raw::c_char,
buffer: *mut *const u8, buffer_len: *mut u32,
buffer: *mut *const u8, buffer_len: *mut u32, tbuf: *mut c_void,
) -> u8 {
let tbuf = cast_pointer!(tbuf, Http2ThreadBuf);
let hname: &CStr = CStr::from_ptr(strname); //unsafe
if let Ok(s) = hname.to_str() {
if let Ok(value) = http2_frames_get_header_value(tx, direction.into(), &s.to_lowercase()) {
*buffer = value.as_ptr(); //unsafe
*buffer_len = value.len() as u32;
if let Some(value) = http2_frames_get_header_value(tx, direction.into(), &s.to_lowercase())
{
match value {
Http2Header::Single(v) => {
*buffer = v.as_ptr(); //unsafe
*buffer_len = v.len() as u32;
}
Http2Header::Multiple(v) => {
tbuf.data = v;
*buffer = tbuf.data.as_ptr(); //unsafe
*buffer_len = tbuf.data.len() as u32;
}
}
return 1;
}
}
@ -1027,20 +1085,20 @@ mod tests {
#[test]
fn test_http2_normalize_host() {
let buf0 = "aBC.com:1234".as_bytes();
let r0 = http2_normalize_host(buf0);
assert_eq!(r0, "aBC.com".as_bytes().to_vec());
let r0 = http2_normalize_host(Http2Header::Single(buf0));
assert_eq!(r0, Http2Header::Multiple("abc.com".as_bytes().to_vec()));
let buf1 = "oisf.net".as_bytes();
let r1 = http2_normalize_host(buf1);
assert_eq!(r1, "oisf.net".as_bytes().to_vec());
let r1 = http2_normalize_host(Http2Header::Single(buf1));
assert_eq!(r1, Http2Header::Single("oisf.net".as_bytes()));
let buf2 = "localhost:3000".as_bytes();
let r2 = http2_normalize_host(buf2);
assert_eq!(r2, "localhost".as_bytes().to_vec());
let r2 = http2_normalize_host(Http2Header::Single(buf2));
assert_eq!(r2, Http2Header::Single("localhost".as_bytes()));
let buf3 = "user:pass@localhost".as_bytes();
let r3 = http2_normalize_host(buf3);
assert_eq!(r3, "localhost".as_bytes().to_vec());
let r3 = http2_normalize_host(Http2Header::Single(buf3));
assert_eq!(r3, Http2Header::Single("localhost".as_bytes()));
let buf4 = "user:pass@localhost:123".as_bytes();
let r4 = http2_normalize_host(buf4);
assert_eq!(r4, "localhost".as_bytes().to_vec());
let r4 = http2_normalize_host(Http2Header::Single(buf4));
assert_eq!(r4, Http2Header::Single("localhost".as_bytes()));
}
#[test]
@ -1091,12 +1149,12 @@ mod tests {
header: head,
data: txdata,
});
match http2_frames_get_header_value(&mut tx, Direction::ToServer, "Host") {
Ok(x) => {
match http2_frames_get_header_value(&tx, Direction::ToServer, "Host") {
Some(Http2Header::Multiple(x)) => {
assert_eq!(x, "abc.com, efg.net".as_bytes());
}
Err(e) => {
panic!("Result should not have been an error: {:?}", e);
_ => {
panic!("Result should have been a multiple header value");
}
}
}

@ -176,9 +176,6 @@ pub struct HTTP2Transaction {
pub ft_tc: FileTransferTracker,
pub ft_ts: FileTransferTracker,
//temporary escaped header for detection
//must be attached to transaction for memory management (be freed at the right time)
pub escaped: Vec<Vec<u8>>,
pub req_line: Vec<u8>,
pub resp_line: Vec<u8>,
@ -211,7 +208,6 @@ impl HTTP2Transaction {
tx_data: AppLayerTxData::new(),
ft_tc: FileTransferTracker::new(),
ft_ts: FileTransferTracker::new(),
escaped: Vec::with_capacity(16),
req_line: Vec::new(),
resp_line: Vec::new(),
doh: None,

@ -67,6 +67,7 @@ static int DetectHttpCookieSetupSticky (DetectEngineCtx *, Signature *, const ch
static void DetectHttpCookieRegisterTests(void);
#endif
static int g_http_cookie_buffer_id = 0;
static int g_http2_thread_id = 0;
static InspectionBuffer *GetRequestData(DetectEngineThreadCtx *det_ctx,
const DetectEngineTransforms *transforms,
@ -131,6 +132,9 @@ void DetectHttpCookieRegister(void)
DetectBufferTypeSetDescriptionByName("http_cookie",
"http cookie header");
g_http2_thread_id = DetectRegisterThreadCtxGlobalFuncs(
"http_cookie", SCHttp2ThreadBufDataInit, NULL, SCHttp2ThreadBufDataFree);
g_http_cookie_buffer_id = DetectBufferTypeGetByName("http_cookie");
}
@ -234,7 +238,10 @@ static InspectionBuffer *GetRequestData2(DetectEngineThreadCtx *det_ctx,
uint32_t b_len = 0;
const uint8_t *b = NULL;
if (SCHttp2TxGetCookie(txv, STREAM_TOSERVER, &b, &b_len) != 1)
void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id);
if (thread_buf == NULL)
return NULL;
if (SCHttp2TxGetCookie(txv, STREAM_TOSERVER, &b, &b_len, thread_buf) != 1)
return NULL;
if (b == NULL || b_len == 0)
return NULL;
@ -254,7 +261,10 @@ static InspectionBuffer *GetResponseData2(DetectEngineThreadCtx *det_ctx,
uint32_t b_len = 0;
const uint8_t *b = NULL;
if (SCHttp2TxGetCookie(txv, STREAM_TOCLIENT, &b, &b_len) != 1)
void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id);
if (thread_buf == NULL)
return NULL;
if (SCHttp2TxGetCookie(txv, STREAM_TOCLIENT, &b, &b_len, thread_buf) != 1)
return NULL;
if (b == NULL || b_len == 0)
return NULL;

@ -42,6 +42,7 @@
#include "rust.h"
static int g_buffer_id = 0;
static int g_http2_thread_id = 0;
#ifdef KEYWORD_TOSERVER
static InspectionBuffer *GetRequestData(DetectEngineThreadCtx *det_ctx,
@ -85,7 +86,10 @@ static InspectionBuffer *GetRequestData2(DetectEngineThreadCtx *det_ctx,
uint32_t b_len = 0;
const uint8_t *b = NULL;
if (SCHttp2TxGetHeaderValue(txv, STREAM_TOSERVER, HEADER_NAME, &b, &b_len) != 1)
void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id);
if (thread_buf == NULL)
return NULL;
if (SCHttp2TxGetHeaderValue(txv, STREAM_TOSERVER, HEADER_NAME, &b, &b_len, thread_buf) != 1)
return NULL;
if (b == NULL || b_len == 0)
return NULL;
@ -139,7 +143,10 @@ static InspectionBuffer *GetResponseData2(DetectEngineThreadCtx *det_ctx,
uint32_t b_len = 0;
const uint8_t *b = NULL;
if (SCHttp2TxGetHeaderValue(txv, STREAM_TOCLIENT, HEADER_NAME, &b, &b_len) != 1)
void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id);
if (thread_buf == NULL)
return NULL;
if (SCHttp2TxGetHeaderValue(txv, STREAM_TOCLIENT, HEADER_NAME, &b, &b_len, thread_buf) != 1)
return NULL;
if (b == NULL || b_len == 0)
return NULL;
@ -214,5 +221,8 @@ static void DetectHttpHeadersRegisterStub(void)
DetectBufferTypeSetDescriptionByName(BUFFER_NAME, BUFFER_DESC);
g_http2_thread_id = DetectRegisterThreadCtxGlobalFuncs(
BUFFER_NAME, SCHttp2ThreadBufDataInit, NULL, SCHttp2ThreadBufDataFree);
g_buffer_id = DetectBufferTypeGetByName(BUFFER_NAME);
}

@ -83,6 +83,8 @@ 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;
static int g_http2_thread_id = 0;
static int g_http2_raw_thread_id = 0;
/**
* \brief Registers the keyword handlers for the "http_host" keyword.
@ -126,6 +128,9 @@ void DetectHttpHHRegister(void)
DetectBufferTypeSetDescriptionByName("http_host",
"http host");
g_http2_thread_id = DetectRegisterThreadCtxGlobalFuncs(
"http_host", SCHttp2ThreadBufDataInit, NULL, SCHttp2ThreadBufDataFree);
g_http_host_buffer_id = DetectBufferTypeGetByName("http_host");
/* http_raw_host content modifier */
@ -160,6 +165,9 @@ void DetectHttpHHRegister(void)
DetectBufferTypeSetDescriptionByName("http_raw_host",
"http raw host header");
g_http2_raw_thread_id = DetectRegisterThreadCtxGlobalFuncs(
"http_raw_host", SCHttp2ThreadBufDataInit, NULL, SCHttp2ThreadBufDataFree);
g_http_raw_host_buffer_id = DetectBufferTypeGetByName("http_raw_host");
}
@ -269,8 +277,10 @@ static InspectionBuffer *GetData2(DetectEngineThreadCtx *det_ctx,
if (buffer->inspect == NULL) {
uint32_t b_len = 0;
const uint8_t *b = NULL;
if (SCHttp2TxGetHostNorm(txv, &b, &b_len) != 1)
void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id);
if (thread_buf == NULL)
return NULL;
if (SCHttp2TxGetHostNorm(txv, &b, &b_len, thread_buf) != 1)
return NULL;
if (b == NULL || b_len == 0)
return NULL;
@ -289,8 +299,11 @@ static InspectionBuffer *GetRawData2(DetectEngineThreadCtx *det_ctx,
if (buffer->inspect == NULL) {
uint32_t b_len = 0;
const uint8_t *b = NULL;
void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_raw_thread_id);
if (thread_buf == NULL)
return NULL;
if (SCHttp2TxGetHost(txv, &b, &b_len) != 1)
if (SCHttp2TxGetHost(txv, &b, &b_len, thread_buf) != 1)
return NULL;
if (b == NULL || b_len == 0)
return NULL;

@ -65,6 +65,7 @@ static int DetectHttpUASetup(DetectEngineCtx *, Signature *, const char *);
static void DetectHttpUARegisterTests(void);
#endif
static int g_http_ua_buffer_id = 0;
static int g_http2_thread_id = 0;
static int DetectHttpUserAgentSetup(DetectEngineCtx *, Signature *, const char *);
static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
const DetectEngineTransforms *transforms,
@ -115,6 +116,9 @@ void DetectHttpUARegister(void)
DetectBufferTypeSetDescriptionByName("http_user_agent",
"http user agent");
g_http2_thread_id = DetectRegisterThreadCtxGlobalFuncs(
"http_user_agent", SCHttp2ThreadBufDataInit, NULL, SCHttp2ThreadBufDataFree);
g_http_ua_buffer_id = DetectBufferTypeGetByName("http_user_agent");
}
@ -192,8 +196,10 @@ static InspectionBuffer *GetData2(DetectEngineThreadCtx *det_ctx,
if (buffer->inspect == NULL) {
uint32_t b_len = 0;
const uint8_t *b = NULL;
if (SCHttp2TxGetUserAgent(txv, &b, &b_len) != 1)
void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id);
if (thread_buf == NULL)
return NULL;
if (SCHttp2TxGetUserAgent(txv, &b, &b_len, thread_buf) != 1)
return NULL;
if (b == NULL || b_len == 0)
return NULL;

Loading…
Cancel
Save