]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
rust/sip: register parser for tcp
authorGiuseppe Longo <giuseppe@glongo.it>
Tue, 1 Aug 2023 19:08:50 +0000 (21:08 +0200)
committerVictor Julien <victor@inliniac.net>
Wed, 28 Feb 2024 06:00:43 +0000 (07:00 +0100)
This patch lets the parser to work over tcp protocol, taking care of handling
data before calling the request/response parsers.

Ticket #3351.

etc/schema.json
rust/src/sip/sip.rs
src/output-json-sip.c

index ec8b915bb5adc2957ee9a3caa4ad3c0fd88fded7..ea790e5368fc575ef2d054486f2c5cabd99186fb 100644 (file)
                                     "description": "Errors encountered parsing RFB protocol",
                                     "$ref": "#/$defs/stats_applayer_error"
                                 },
-                                "sip": {
-                                    "description": "Errors encountered parsing SIP",
+                                "sip_udp": {
+                                    "description": "Errors encountered parsing SIP/UDP protocol",
+                                    "$ref": "#/$defs/stats_applayer_error"
+                                },
+                                "sip_tcp": {
+                                    "description": "Errors encountered parsing SIP/TCP protocol",
                                     "$ref": "#/$defs/stats_applayer_error"
                                 },
                                 "smb": {
                                     "description": "Number of flows for RFB protocol",
                                     "type": "integer"
                                 },
-                                "sip": {
-                                    "description": "Number of flows for SIP",
+                                "sip_udp": {
+                                    "description": "Number of flows for SIP/UDP protocol",
+                                    "type": "integer"
+                                },
+                                "sip_tcp": {
+                                    "description": "Number of flows for SIP/TCP protocol",
                                     "type": "integer"
                                 },
                                 "smb": {
                                 "rfb": {
                                     "type": "integer"
                                 },
-                                "sip": {
+                                "sip_udp": {
+                                    "type": "integer"
+                                },
+                                "sip_tcp": {
                                     "type": "integer"
                                 },
                                 "smb": {
index d1aefde548bee4e56766749d0f0cad9d3c6003f7..e203ae9e2eb7665b7ac643fcd8e549c48554ff74 100755 (executable)
@@ -19,7 +19,7 @@
 
 use crate::applayer::{self, *};
 use crate::core;
-use crate::core::{AppProto, Flow, ALPROTO_UNKNOWN};
+use crate::core::{AppProto, Flow, ALPROTO_FAILED, ALPROTO_UNKNOWN};
 use crate::frames::*;
 use crate::sip::parser::*;
 use nom7::Err;
@@ -50,6 +50,8 @@ pub struct SIPState {
     state_data: AppLayerStateData,
     transactions: Vec<SIPTransaction>,
     tx_id: u64,
+    request_frame: Option<Frame>,
+    response_frame: Option<Frame>,
 }
 
 impl State<SIPTransaction> for SIPState {
@@ -109,6 +111,15 @@ impl SIPState {
         }
     }
 
+    fn build_tx_request(&mut self, input: &[u8], request: Request) {
+        let mut tx = self.new_tx(crate::core::Direction::ToServer);
+        tx.request = Some(request);
+        if let Ok((_, req_line)) = sip_take_line(input) {
+            tx.request_line = req_line;
+        }
+        self.transactions.push(tx);
+    }
+
     // app-layer-frame-documentation tag start: parse_request
     fn parse_request(&mut self, flow: *const core::Flow, stream_slice: StreamSlice) -> bool {
         let input = stream_slice.as_slice();
@@ -124,12 +135,7 @@ impl SIPState {
         match sip_parse_request(input) {
             Ok((_, request)) => {
                 sip_frames_ts(flow, &stream_slice, &request);
-                let mut tx = self.new_tx(crate::core::Direction::ToServer);
-                tx.request = Some(request);
-                if let Ok((_, req_line)) = sip_take_line(input) {
-                    tx.request_line = req_line;
-                }
-                self.transactions.push(tx);
+                self.build_tx_request(input, request);
                 return true;
             }
             // app-layer-frame-documentation tag end: parse_request
@@ -144,6 +150,68 @@ impl SIPState {
         }
     }
 
+    fn parse_request_tcp(
+        &mut self, flow: *const core::Flow, stream_slice: StreamSlice,
+    ) -> AppLayerResult {
+        let input = stream_slice.as_slice();
+        if input.is_empty() {
+            return AppLayerResult::ok();
+        }
+
+        let mut start = input;
+        while !start.is_empty() {
+            if self.request_frame.is_none() {
+                self.request_frame = Frame::new(
+                    flow,
+                    &stream_slice,
+                    start,
+                    -1_i64,
+                    SIPFrameType::Pdu as u8,
+                );
+                SCLogDebug!("ts: pdu {:?}", self.request_frame);
+            }
+            match sip_parse_request(start) {
+                Ok((rem, request)) => {
+                    sip_frames_ts(flow, &stream_slice, &request);
+                    self.build_tx_request(start, request);
+                    let consumed = start.len() - rem.len();
+                    start = rem;
+
+                    if let Some(frame) = &self.request_frame {
+                        frame.set_len(flow, consumed as i64);
+                        self.request_frame = None;
+                    }
+                }
+                Err(Err::Incomplete(_needed)) => {
+                    let consumed = input.len() - start.len();
+                    let needed_estimation = start.len() + 1;
+                    SCLogDebug!(
+                        "Needed: {:?}, estimated needed: {:?}",
+                        _needed,
+                        needed_estimation
+                    );
+                    return AppLayerResult::incomplete(consumed as u32, needed_estimation as u32);
+                }
+                Err(_) => {
+                    self.set_event(SIPEvent::InvalidData);
+                    return AppLayerResult::err();
+                }
+            }
+        }
+
+        // input fully consumed.
+        return AppLayerResult::ok();
+    }
+
+    fn build_tx_response(&mut self, input: &[u8], response: Response) {
+        let mut tx = self.new_tx(crate::core::Direction::ToClient);
+        tx.response = Some(response);
+        if let Ok((_, resp_line)) = sip_take_line(input) {
+            tx.response_line = resp_line;
+        }
+        self.transactions.push(tx);
+    }
+
     fn parse_response(&mut self, flow: *const core::Flow, stream_slice: StreamSlice) -> bool {
         let input = stream_slice.as_slice();
         let _pdu = Frame::new(
@@ -158,12 +226,7 @@ impl SIPState {
         match sip_parse_response(input) {
             Ok((_, response)) => {
                 sip_frames_tc(flow, &stream_slice, &response);
-                let mut tx = self.new_tx(crate::core::Direction::ToClient);
-                tx.response = Some(response);
-                if let Ok((_, resp_line)) = sip_take_line(input) {
-                    tx.response_line = resp_line;
-                }
-                self.transactions.push(tx);
+                self.build_tx_response(input, response);
                 return true;
             }
             Err(Err::Incomplete(_)) => {
@@ -176,6 +239,59 @@ impl SIPState {
             }
         }
     }
+
+    fn parse_response_tcp(
+        &mut self, flow: *const core::Flow, stream_slice: StreamSlice,
+    ) -> AppLayerResult {
+        let input = stream_slice.as_slice();
+        if input.is_empty() {
+            return AppLayerResult::ok();
+        }
+
+        let mut start = input;
+        while !start.is_empty() {
+            if self.response_frame.is_none() {
+                self.response_frame = Frame::new(
+                    flow,
+                    &stream_slice,
+                    start,
+                    -1_i64,
+                    SIPFrameType::Pdu as u8,
+                );
+                SCLogDebug!("tc: pdu {:?}", self.request_frame);
+            }
+            match sip_parse_response(start) {
+                Ok((rem, response)) => {
+                    sip_frames_tc(flow, &stream_slice, &response);
+                    self.build_tx_response(start, response);
+                    let consumed = start.len() - rem.len();
+                    start = rem;
+
+                    if let Some(frame) = &self.response_frame {
+                        frame.set_len(flow, consumed as i64);
+                        self.response_frame = None;
+                    }
+                }
+                Err(Err::Incomplete(_needed)) => {
+                    let consumed = input.len() - start.len();
+                    let needed_estimation = start.len() + 1;
+                    SCLogDebug!(
+                        "Needed: {:?}, estimated needed: {:?}",
+                        _needed,
+                        needed_estimation
+                    );
+                    return AppLayerResult::incomplete(consumed as u32, needed_estimation as u32);
+                }
+                Err(_) => {
+                    self.set_event(SIPEvent::InvalidData);
+                    return AppLayerResult::err();
+                }
+            }
+        }
+
+        // input fully consumed.
+        return AppLayerResult::ok();
+    }
 }
 
 impl SIPTransaction {
@@ -315,6 +431,27 @@ pub unsafe extern "C" fn rs_sip_probing_parser_ts(
     return ALPROTO_UNKNOWN;
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn rs_sip_probing_parser_tcp_ts(
+    _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
+) -> AppProto {
+    if !input.is_null() {
+        let buf = build_slice!(input, input_len as usize);
+        match sip_parse_request(buf) {
+            Ok((_, _request)) => {
+                return ALPROTO_SIP;
+            }
+            Err(Err::Incomplete(_)) => {
+                return ALPROTO_UNKNOWN;
+            }
+            Err(_e) => {
+                return ALPROTO_FAILED;
+            }
+        }
+    }
+    return ALPROTO_UNKNOWN;
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn rs_sip_probing_parser_tc(
     _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
@@ -326,6 +463,27 @@ pub unsafe extern "C" fn rs_sip_probing_parser_tc(
     return ALPROTO_UNKNOWN;
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn rs_sip_probing_parser_tcp_tc(
+    _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
+) -> AppProto {
+    if !input.is_null() {
+        let buf = build_slice!(input, input_len as usize);
+        match sip_parse_response(buf) {
+            Ok((_, _response)) => {
+                return ALPROTO_SIP;
+            }
+            Err(Err::Incomplete(_)) => {
+                return ALPROTO_UNKNOWN;
+            }
+            Err(_e) => {
+                return ALPROTO_FAILED;
+            }
+        }
+    }
+    return ALPROTO_UNKNOWN;
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn rs_sip_parse_request(
     flow: *const core::Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
@@ -335,6 +493,23 @@ pub unsafe extern "C" fn rs_sip_parse_request(
     state.parse_request(flow, stream_slice).into()
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn rs_sip_parse_request_tcp(
+    flow: *const core::Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void,
+    stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
+) -> AppLayerResult {
+    if stream_slice.is_empty() {
+        if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) > 0 {
+            return AppLayerResult::ok();
+        } else {
+            return AppLayerResult::err();
+        }
+    }
+
+    let state = cast_pointer!(state, SIPState);
+    state.parse_request_tcp(flow, stream_slice)
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn rs_sip_parse_response(
     flow: *const core::Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
@@ -344,6 +519,23 @@ pub unsafe extern "C" fn rs_sip_parse_response(
     state.parse_response(flow, stream_slice).into()
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn rs_sip_parse_response_tcp(
+    flow: *const core::Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void,
+    stream_slice: StreamSlice, _data: *const std::os::raw::c_void,
+) -> AppLayerResult {
+    if stream_slice.is_empty() {
+        if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) > 0 {
+            return AppLayerResult::ok();
+        } else {
+            return AppLayerResult::err();
+        }
+    }
+
+    let state = cast_pointer!(state, SIPState);
+    state.parse_response_tcp(flow, stream_slice)
+}
+
 export_tx_data_get!(rs_sip_get_tx_data, SIPTransaction);
 export_state_data_get!(rs_sip_get_state_data, SIPState);
 
@@ -352,7 +544,7 @@ const PARSER_NAME: &[u8] = b"sip\0";
 #[no_mangle]
 pub unsafe extern "C" fn rs_sip_register_parser() {
     let default_port = CString::new("[5060,5061]").unwrap();
-    let parser = RustParser {
+    let mut parser = RustParser {
         name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
         default_port: default_port.as_ptr(),
         ipproto: core::IPPROTO_UDP,
@@ -393,6 +585,24 @@ pub unsafe extern "C" fn rs_sip_register_parser() {
             let _ = AppLayerRegisterParser(&parser, alproto);
         }
     } else {
-        SCLogDebug!("Protocol detecter and parser disabled for SIP/UDP.");
+        SCLogDebug!("Protocol detection and parsing disabled for UDP SIP.");
+    }
+
+    // register TCP parser
+    parser.ipproto = core::IPPROTO_TCP;
+    parser.probe_ts = Some(rs_sip_probing_parser_tcp_ts);
+    parser.probe_tc = Some(rs_sip_probing_parser_tcp_tc);
+    parser.parse_ts = rs_sip_parse_request_tcp;
+    parser.parse_tc = rs_sip_parse_response_tcp;
+
+    let ip_proto_str = CString::new("tcp").unwrap();
+    if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
+        let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
+        ALPROTO_SIP = alproto;
+        if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
+            let _ = AppLayerRegisterParser(&parser, alproto);
+        }
+    } else {
+        SCLogDebug!("Protocol detection and parsing disabled for TCP SIP.");
     }
 }
index 7dd442cf6aba0727a43efaf8173cca7802440277..f147a755e28df2c3c9c60fed0539b1ef3946eed0 100644 (file)
@@ -77,6 +77,7 @@ static OutputInitResult OutputSIPLogInitSub(ConfNode *conf,
     OutputCtx *parent_ctx)
 {
     AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_SIP);
+    AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SIP);
     return OutputJsonLogInitSub(conf, parent_ctx);
 }