]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
http2: mimic HTTP1 request from upgrade
authorPhilippe Antoine <contact@catenacyber.fr>
Fri, 4 Sep 2020 14:06:16 +0000 (16:06 +0200)
committerVictor Julien <victor@inliniac.net>
Wed, 9 Sep 2020 14:22:11 +0000 (16:22 +0200)
rust/src/http2/detect.rs
rust/src/http2/http2.rs
src/app-layer-htp.c
src/app-layer-htp.h
src/app-layer-http2.c
src/app-layer-http2.h

index f2ff45ebbb1acb449793188d3ff0238458c4fb58..8778a44abb95e94c86b156d43eafcce5d475066d 100644 (file)
  * 02110-1301, USA.
  */
 
-use super::http2::{HTTP2FrameTypeData, HTTP2Transaction};
+use super::http2::{
+    HTTP2Frame, HTTP2FrameTypeData, HTTP2State, HTTP2Transaction, HTTP2TransactionState,
+};
 use super::parser;
 use crate::core::STREAM_TOSERVER;
+use crate::log::*;
 use std::ffi::CStr;
 use std::mem::transmute;
 use std::str::FromStr;
@@ -544,3 +547,61 @@ pub unsafe extern "C" fn rs_http2_tx_get_header(
 
     return 0;
 }
+
+fn http2_tx_set_header(state: &mut HTTP2State, name: &[u8], input: &[u8]) {
+    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: name.to_vec(),
+        value: input.to_vec(),
+        error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
+        sizeupdate: 0,
+    };
+    blocks.push(b);
+    let hs = parser::HTTP2FrameHeaders {
+        padlength: None,
+        priority: None,
+        blocks: blocks,
+    };
+    let txdata = HTTP2FrameTypeData::HEADERS(hs);
+    let tx = state.find_or_create_tx(&head, &txdata, STREAM_TOSERVER);
+    tx.frames_ts.push(HTTP2Frame {
+        header: head,
+        data: txdata,
+    });
+    //we do not expect more data from client
+    tx.state = HTTP2TransactionState::HTTP2StateHalfClosedClient;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_http2_tx_set_method(
+    state: &mut HTTP2State, buffer: *const u8, buffer_len: u32,
+) {
+    let slice = build_slice!(buffer, buffer_len as usize);
+    http2_tx_set_header(state, ":method".as_bytes(), slice)
+}
+
+#[no_mangle]
+pub extern "C" fn rs_http2_tx_set_uri(state: &mut HTTP2State, buffer: *const u8, buffer_len: u32) {
+    let slice = build_slice!(buffer, buffer_len as usize);
+    http2_tx_set_header(state, ":path".as_bytes(), slice)
+}
+
+#[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,
+) {
+    let slice_name = build_slice!(name, name_len as usize);
+    let slice_value = build_slice!(value, value_len as usize);
+    if slice_name == "HTTP2-Settings".as_bytes() {
+        SCLogNotice!("lol seetings TODO");
+    } else {
+        http2_tx_set_header(state, slice_name, slice_value)
+    }
+}
index e2dd984dba7a476aeca7ce11624aa6f926e5bc88..3ee0e29973fed83e7f9620a42a0b8045bc65b95f 100644 (file)
@@ -117,7 +117,7 @@ pub struct HTTP2Frame {
 pub struct HTTP2Transaction {
     tx_id: u64,
     pub stream_id: u32,
-    state: HTTP2TransactionState,
+    pub state: HTTP2TransactionState,
     child_stream_id: u32,
 
     pub frames_tc: Vec<HTTP2Frame>,
@@ -373,7 +373,7 @@ impl HTTP2State {
         return self.transactions.last_mut().unwrap();
     }
 
-    fn find_or_create_tx(
+    pub fn find_or_create_tx(
         &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: u8,
     ) -> &mut HTTP2Transaction {
         if header.stream_id == 0 {
@@ -876,11 +876,27 @@ pub extern "C" fn rs_http2_probing_parser_tc(
     return ALPROTO_UNKNOWN;
 }
 
+/// Extern functions operating on HTTP2.
+extern "C" {
+    pub fn HTTP2MimicHttp1Request(
+        orig_state: *mut std::os::raw::c_void, new_state: *mut std::os::raw::c_void,
+    );
+}
+
 #[no_mangle]
-pub extern "C" fn rs_http2_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
+pub extern "C" fn rs_http2_state_new(
+    orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
+) -> *mut std::os::raw::c_void {
     let state = HTTP2State::new();
     let boxed = Box::new(state);
-    return unsafe { transmute(boxed) };
+    let r = unsafe { transmute(boxed) };
+    if orig_state != std::ptr::null_mut() {
+        //we could check ALPROTO_HTTP == orig_proto
+        unsafe {
+            HTTP2MimicHttp1Request(orig_state, r);
+        }
+    }
+    return r;
 }
 
 #[no_mangle]
index 2c2a49f4e7a2b791f7282ceb3ff285dfd1a946f8..08df37d6541b0960d4c3b914f872e0e2561b185d 100644 (file)
@@ -968,13 +968,6 @@ static AppLayerResult HTPHandleResponseData(Flow *f, void *htp_state,
                             }
                             consumed = htp_connp_res_data_consumed(hstate->connp);
                             AppLayerRequestProtocolChange(hstate->f, dp, ALPROTO_HTTP2);
-                            // close connection to log HTTP1 request in tunnel mode
-                            if (!(hstate->flags & HTP_FLAG_STATE_CLOSED_TC)) {
-                                htp_connp_close(hstate->connp, &ts);
-                                hstate->flags |= HTP_FLAG_STATE_CLOSED_TC;
-                            }
-                            // TODO mimic HTTP1 request into HTTP2
-
                             // During HTTP2 upgrade, we may consume the HTTP1 part of the data
                             // and we need to parser the remaining part with HTTP2
                             if (consumed > 0 && consumed < input_len) {
@@ -2958,6 +2951,19 @@ static void *HTPStateGetTx(void *alstate, uint64_t tx_id)
         return NULL;
 }
 
+void *HtpGetTxForH2(void *alstate)
+{
+    // gets last transaction
+    HtpState *http_state = (HtpState *)alstate;
+    if (http_state != NULL && http_state->conn != NULL) {
+        size_t txid = htp_list_array_size(http_state->conn->transactions);
+        if (txid > 0) {
+            return htp_list_get(http_state->conn->transactions, txid - 1);
+        }
+    }
+    return NULL;
+}
+
 static int HTPStateGetAlstateProgressCompletionStatus(uint8_t direction)
 {
     return (direction & STREAM_TOSERVER) ? HTP_REQUEST_COMPLETE : HTP_RESPONSE_COMPLETE;
index 72acc41d3adb7edd2474b2b46c219df2fd388ce8..dee49f5c1972c81c1ecdfa6486120ed82368ce53 100644 (file)
@@ -288,6 +288,8 @@ void HTPConfigure(void);
 void HtpConfigCreateBackup(void);
 void HtpConfigRestoreBackup(void);
 
+void *HtpGetTxForH2(void *);
+
 #endif /* __APP_LAYER_HTP_H__ */
 
 /**
index c937c5a539fe33622729c9429a2895cd0c18bd71..8576697780a5024ab62f30f0703a50885078423d 100644 (file)
@@ -32,6 +32,7 @@
 #include "app-layer-detect-proto.h"
 #include "app-layer-parser.h"
 
+#include "app-layer-htp.h"
 #include "app-layer-http2.h"
 #include "rust.h"
 
@@ -69,3 +70,22 @@ void RegisterHTTP2Parsers(void)
     //TODOask HTTP2ParserRegisterTests();
 #endif
 }
+
+void HTTP2MimicHttp1Request(void *alstate_orig, void *h2s)
+{
+    htp_tx_t *h1tx = HtpGetTxForH2(alstate_orig);
+    if (h2s == NULL || h1tx == NULL) {
+        return;
+    }
+
+    rs_http2_tx_set_method(h2s, bstr_ptr(h1tx->request_method), bstr_len(h1tx->request_method));
+    rs_http2_tx_set_uri(h2s, bstr_ptr(h1tx->request_uri), bstr_len(h1tx->request_uri));
+    size_t nbheaders = htp_table_size(h1tx->request_headers);
+    for (size_t i = 0; i < nbheaders; i++) {
+        htp_header_t *h = htp_table_get_index(h1tx->request_headers, i, NULL);
+        if (h != NULL) {
+            rs_http2_tx_add_header(h2s, bstr_ptr(h->name), bstr_len(h->name), bstr_ptr(h->value),
+                    bstr_len(h->value));
+        }
+    }
+}
index 043cf7b30c9a69b632ea52593fa54114a3809729..b3c52c1d70dcd3fa57139cd29b184fefc870c71f 100644 (file)
@@ -26,4 +26,6 @@
 
 void RegisterHTTP2Parsers(void);
 
+void HTTP2MimicHttp1Request(void *, void *);
+
 #endif /* __APP_LAYER_HTTP2_H__ */