]> git.ipfire.org Git - people/ms/suricata.git/commitdiff
http2: concatenate one headers multiple values
authorPhilippe Antoine <contact@catenacyber.fr>
Mon, 31 May 2021 14:17:22 +0000 (16:17 +0200)
committerVictor Julien <victor@inliniac.net>
Fri, 3 Sep 2021 08:37:00 +0000 (10:37 +0200)
For detection, as is done with HTTP1

rust/src/http2/detect.rs

index 05ea3f8b9421bb22e78c8d78426a264d3b45b710..5bfb1147873b5433f285d2ae3c2d36bdfe703d52 100644 (file)
@@ -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::str::FromStr;
 
@@ -468,36 +468,78 @@ pub unsafe extern "C" fn rs_http2_tx_get_header_name(
     return 0;
 }
 
-fn http2_blocks_get_header_value<'a>(
-    blocks: &'a Vec<parser::HTTP2FrameHeaderBlock>, 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<HTTP2Frame>, 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;
@@ -509,7 +551,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;
@@ -521,7 +563,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;
@@ -560,7 +602,7 @@ fn http2_normalize_host(value: &[u8]) -> (Option<Vec<u8>>, 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)
@@ -587,7 +629,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;
@@ -599,7 +641,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;
@@ -612,13 +654,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;
@@ -634,12 +676,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;
@@ -1104,4 +1141,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);
+            }
+        }
+    }
 }