diff --git a/etc/schema.json b/etc/schema.json index ec8b915bb5..ea790e5368 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -3838,8 +3838,12 @@ "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": { @@ -3992,8 +3996,12 @@ "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": { @@ -4111,7 +4119,10 @@ "rfb": { "type": "integer" }, - "sip": { + "sip_udp": { + "type": "integer" + }, + "sip_tcp": { "type": "integer" }, "smb": { diff --git a/rust/src/sip/sip.rs b/rust/src/sip/sip.rs index d1aefde548..e203ae9e2e 100755 --- a/rust/src/sip/sip.rs +++ b/rust/src/sip/sip.rs @@ -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, tx_id: u64, + request_frame: Option, + response_frame: Option, } impl State 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."); } } diff --git a/src/output-json-sip.c b/src/output-json-sip.c index 7dd442cf6a..f147a755e2 100644 --- a/src/output-json-sip.c +++ b/src/output-json-sip.c @@ -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); }