diff options
Diffstat (limited to 'rust/src/sip')
-rw-r--r-- | rust/src/sip/detect.rs | 182 | ||||
-rw-r--r-- | rust/src/sip/log.rs | 54 | ||||
-rwxr-xr-x | rust/src/sip/mod.rs | 25 | ||||
-rw-r--r-- | rust/src/sip/parser.rs | 325 | ||||
-rwxr-xr-x | rust/src/sip/sip.rs | 391 |
5 files changed, 977 insertions, 0 deletions
diff --git a/rust/src/sip/detect.rs b/rust/src/sip/detect.rs new file mode 100644 index 0000000..63f6365 --- /dev/null +++ b/rust/src/sip/detect.rs @@ -0,0 +1,182 @@ +/* Copyright (C) 2019 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// written by Giuseppe Longo <giuseppe@glongo.it> + +use crate::core::Direction; +use crate::sip::sip::SIPTransaction; +use std::ptr; + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_tx_get_method( + tx: &mut SIPTransaction, + buffer: *mut *const u8, + buffer_len: *mut u32, +) -> u8 { + if let Some(ref r) = tx.request { + let m = &r.method; + if !m.is_empty() { + *buffer = m.as_ptr(); + *buffer_len = m.len() as u32; + return 1; + } + } + + *buffer = ptr::null(); + *buffer_len = 0; + + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_tx_get_uri( + tx: &mut SIPTransaction, + buffer: *mut *const u8, + buffer_len: *mut u32, +) -> u8 { + if let Some(ref r) = tx.request { + let p = &r.path; + if !p.is_empty() { + *buffer = p.as_ptr(); + *buffer_len = p.len() as u32; + return 1; + } + } + + *buffer = ptr::null(); + *buffer_len = 0; + + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_tx_get_protocol( + tx: &mut SIPTransaction, + buffer: *mut *const u8, + buffer_len: *mut u32, + direction: u8, +) -> u8 { + match direction.into() { + Direction::ToServer => { + if let Some(ref r) = tx.request { + let v = &r.version; + if !v.is_empty() { + *buffer = v.as_ptr(); + *buffer_len = v.len() as u32; + return 1; + } + } + } + Direction::ToClient => { + if let Some(ref r) = tx.response { + let v = &r.version; + if !v.is_empty() { + *buffer = v.as_ptr(); + *buffer_len = v.len() as u32; + return 1; + } + } + } + } + + *buffer = ptr::null(); + *buffer_len = 0; + + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_tx_get_stat_code( + tx: &mut SIPTransaction, + buffer: *mut *const u8, + buffer_len: *mut u32, +) -> u8 { + if let Some(ref r) = tx.response { + let c = &r.code; + if !c.is_empty() { + *buffer = c.as_ptr(); + *buffer_len = c.len() as u32; + return 1; + } + } + + *buffer = ptr::null(); + *buffer_len = 0; + + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_tx_get_stat_msg( + tx: &mut SIPTransaction, + buffer: *mut *const u8, + buffer_len: *mut u32, +) -> u8 { + if let Some(ref r) = tx.response { + let re = &r.reason; + if !re.is_empty() { + *buffer = re.as_ptr(); + *buffer_len = re.len() as u32; + return 1; + } + } + + *buffer = ptr::null(); + *buffer_len = 0; + + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_tx_get_request_line( + tx: &mut SIPTransaction, + buffer: *mut *const u8, + buffer_len: *mut u32, +) -> u8 { + if let Some(ref r) = tx.request_line { + if !r.is_empty() { + *buffer = r.as_ptr(); + *buffer_len = r.len() as u32; + return 1; + } + } + + *buffer = ptr::null(); + *buffer_len = 0; + + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_tx_get_response_line( + tx: &mut SIPTransaction, + buffer: *mut *const u8, + buffer_len: *mut u32, +) -> u8 { + if let Some(ref r) = tx.response_line { + if !r.is_empty() { + *buffer = r.as_ptr(); + *buffer_len = r.len() as u32; + return 1; + } + } + + *buffer = ptr::null(); + *buffer_len = 0; + + return 0; +} diff --git a/rust/src/sip/log.rs b/rust/src/sip/log.rs new file mode 100644 index 0000000..792acfa --- /dev/null +++ b/rust/src/sip/log.rs @@ -0,0 +1,54 @@ +/* Copyright (C) 2019 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// written by Giuseppe Longo <giuseppe@glongo.it> + +use crate::jsonbuilder::{JsonBuilder, JsonError}; +use crate::sip::sip::SIPTransaction; + +fn log(tx: &SIPTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> { + js.open_object("sip")?; + + if let Some(req) = &tx.request { + js.set_string("method", &req.method)? + .set_string("uri", &req.path)? + .set_string("version", &req.version)?; + } + + if let Some(req_line) = &tx.request_line { + js.set_string("request_line", req_line)?; + } + + if let Some(resp) = &tx.response { + js.set_string("version", &resp.version)? + .set_string("code", &resp.code)? + .set_string("reason", &resp.reason)?; + } + + if let Some(resp_line) = &tx.response_line { + js.set_string("response_line", resp_line)?; + } + + js.close()?; + + Ok(()) +} + +#[no_mangle] +pub extern "C" fn rs_sip_log_json(tx: &mut SIPTransaction, js: &mut JsonBuilder) -> bool { + log(tx, js).is_ok() +}
\ No newline at end of file diff --git a/rust/src/sip/mod.rs b/rust/src/sip/mod.rs new file mode 100755 index 0000000..de63aaa --- /dev/null +++ b/rust/src/sip/mod.rs @@ -0,0 +1,25 @@ +/* Copyright (C) 2019 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +//! SIP protocol parser, detection and logger module. + +// written by Giuseppe Longo <giuseppe@glongo.it> + +pub mod detect; +pub mod log; +pub mod parser; +pub mod sip; diff --git a/rust/src/sip/parser.rs b/rust/src/sip/parser.rs new file mode 100644 index 0000000..a34bc26 --- /dev/null +++ b/rust/src/sip/parser.rs @@ -0,0 +1,325 @@ +/* Copyright (C) 2019-2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// written by Giuseppe Longo <giuseppe@glono.it> + +use nom7::bytes::streaming::{take, take_while, take_while1}; +use nom7::character::streaming::{char, crlf}; +use nom7::character::{is_alphabetic, is_alphanumeric, is_space}; +use nom7::combinator::map_res; +use nom7::sequence::delimited; +use nom7::{Err, IResult, Needed}; +use std; +use std::collections::HashMap; + +#[derive(Debug)] +pub struct Header { + pub name: String, + pub value: String, +} + +#[derive(Debug)] +pub struct Request { + pub method: String, + pub path: String, + pub version: String, + pub headers: HashMap<String, String>, + + pub request_line_len: u16, + pub headers_len: u16, + pub body_offset: u16, + pub body_len: u16, +} + +#[derive(Debug)] +pub struct Response { + pub version: String, + pub code: String, + pub reason: String, + + pub response_line_len: u16, + pub headers_len: u16, + pub body_offset: u16, + pub body_len: u16, +} + +#[inline] +fn is_token_char(b: u8) -> bool { + is_alphanumeric(b) || b"!%'*+-._`".contains(&b) +} + +#[inline] +fn is_method_char(b: u8) -> bool { + is_alphabetic(b) +} + +#[inline] +fn is_request_uri_char(b: u8) -> bool { + is_alphanumeric(b) || is_token_char(b) || b"~#@:".contains(&b) +} + +#[inline] +fn is_version_char(b: u8) -> bool { + is_alphanumeric(b) || b"./".contains(&b) +} + +#[inline] +fn is_reason_phrase(b: u8) -> bool { + is_alphanumeric(b) || is_token_char(b) || b"$&(),/:;=?@[\\]^ ".contains(&b) +} + +fn is_header_name(b: u8) -> bool { + is_alphanumeric(b) || is_token_char(b) +} + +fn is_header_value(b: u8) -> bool { + is_alphanumeric(b) || is_token_char(b) || b"\"#$&(),/;:<=>?@[]{}()^|~\\\t\n\r ".contains(&b) +} + +pub fn sip_parse_request(oi: &[u8]) -> IResult<&[u8], Request> { + let (i, method) = parse_method(oi)?; + let (i, _) = char(' ')(i)?; + let (i, path) = parse_request_uri(i)?; + let (i, _) = char(' ')(i)?; + let (i, version) = parse_version(i)?; + let (hi, _) = crlf(i)?; + let request_line_len = oi.len() - hi.len(); + let (phi, headers) = parse_headers(hi)?; + let headers_len = hi.len() - phi.len(); + let (bi, _) = crlf(phi)?; + let body_offset = oi.len() - bi.len(); + Ok(( + bi, + Request { + method: method.into(), + path: path.into(), + version: version.into(), + headers, + + request_line_len: request_line_len as u16, + headers_len: headers_len as u16, + body_offset: body_offset as u16, + body_len: bi.len() as u16, + }, + )) +} + +pub fn sip_parse_response(oi: &[u8]) -> IResult<&[u8], Response> { + let (i, version) = parse_version(oi)?; + let (i, _) = char(' ')(i)?; + let (i, code) = parse_code(i)?; + let (i, _) = char(' ')(i)?; + let (i, reason) = parse_reason(i)?; + let (hi, _) = crlf(i)?; + let response_line_len = oi.len() - hi.len(); + let (phi, _headers) = parse_headers(hi)?; + let headers_len = hi.len() - phi.len(); + let (bi, _) = crlf(phi)?; + let body_offset = oi.len() - bi.len(); + Ok(( + bi, + Response { + version: version.into(), + code: code.into(), + reason: reason.into(), + + response_line_len: response_line_len as u16, + headers_len: headers_len as u16, + body_offset: body_offset as u16, + body_len: bi.len() as u16, + }, + )) +} + +#[inline] +fn parse_method(i: &[u8]) -> IResult<&[u8], &str> { + map_res(take_while(is_method_char), std::str::from_utf8)(i) +} + +#[inline] +fn parse_request_uri(i: &[u8]) -> IResult<&[u8], &str> { + map_res(take_while1(is_request_uri_char), std::str::from_utf8)(i) +} + +#[inline] +fn parse_version(i: &[u8]) -> IResult<&[u8], &str> { + map_res(take_while1(is_version_char), std::str::from_utf8)(i) +} + +#[inline] +fn parse_code(i: &[u8]) -> IResult<&[u8], &str> { + map_res(take(3_usize), std::str::from_utf8)(i) +} + +#[inline] +fn parse_reason(i: &[u8]) -> IResult<&[u8], &str> { + map_res(take_while(is_reason_phrase), std::str::from_utf8)(i) +} + +#[inline] +fn header_name(i: &[u8]) -> IResult<&[u8], &str> { + map_res(take_while(is_header_name), std::str::from_utf8)(i) +} + +#[inline] +fn header_value(i: &[u8]) -> IResult<&[u8], &str> { + map_res(parse_header_value, std::str::from_utf8)(i) +} + +#[inline] +fn hcolon(i: &[u8]) -> IResult<&[u8], char> { + delimited(take_while(is_space), char(':'), take_while(is_space))(i) +} + +fn message_header(i: &[u8]) -> IResult<&[u8], Header> { + let (i, n) = header_name(i)?; + let (i, _) = hcolon(i)?; + let (i, v) = header_value(i)?; + let (i, _) = crlf(i)?; + Ok(( + i, + Header { + name: String::from(n), + value: String::from(v), + }, + )) +} + +pub fn sip_take_line(i: &[u8]) -> IResult<&[u8], Option<String>> { + let (i, line) = map_res(take_while1(is_reason_phrase), std::str::from_utf8)(i)?; + Ok((i, Some(line.into()))) +} + +pub fn parse_headers(mut input: &[u8]) -> IResult<&[u8], HashMap<String, String>> { + let mut headers_map: HashMap<String, String> = HashMap::new(); + loop { + match crlf(input) as IResult<&[u8], _> { + Ok((_, _)) => { + break; + } + Err(Err::Error(_)) => {} + Err(Err::Failure(_)) => {} + Err(Err::Incomplete(e)) => return Err(Err::Incomplete(e)), + }; + let (rest, header) = message_header(input)?; + headers_map.insert(header.name, header.value); + input = rest; + } + + Ok((input, headers_map)) +} + +fn parse_header_value(buf: &[u8]) -> IResult<&[u8], &[u8]> { + let mut end_pos = 0; + let mut trail_spaces = 0; + let mut idx = 0; + while idx < buf.len() { + match buf[idx] { + b'\n' => { + idx += 1; + if idx >= buf.len() { + return Err(Err::Incomplete(Needed::new(1))); + } + match buf[idx] { + b' ' | b'\t' => { + idx += 1; + continue; + } + _ => { + return Ok((&buf[(end_pos + trail_spaces)..], &buf[..end_pos])); + } + } + } + b' ' | b'\t' => { + trail_spaces += 1; + } + b'\r' => {} + b => { + trail_spaces = 0; + if !is_header_value(b) { + return Err(Err::Incomplete(Needed::new(1))); + } + end_pos = idx + 1; + } + } + idx += 1; + } + Ok((&b""[..], buf)) +} + +#[cfg(test)] +mod tests { + + use crate::sip::parser::*; + + #[test] + fn test_parse_request() { + let buf: &[u8] = "REGISTER sip:sip.cybercity.dk SIP/2.0\r\n\ + From: <sip:voi18063@sip.cybercity.dk>;tag=903df0a\r\n\ + To: <sip:voi18063@sip.cybercity.dk>\r\n\ + Content-Length: 0\r\n\ + \r\n" + .as_bytes(); + + match sip_parse_request(buf) { + Ok((_, req)) => { + assert_eq!(req.method, "REGISTER"); + assert_eq!(req.path, "sip:sip.cybercity.dk"); + assert_eq!(req.version, "SIP/2.0"); + assert_eq!(req.headers["Content-Length"], "0"); + } + _ => { + assert!(false); + } + } + } + + #[test] + fn test_parse_request_trail_space_header() { + let buf: &[u8] = "REGISTER sip:sip.cybercity.dk SIP/2.0\r\n\ + From: <sip:voi18063@sip.cybercity.dk>;tag=903df0a\r\n\ + To: <sip:voi18063@sip.cybercity.dk>\r\n\ + Content-Length: 4 \r\n\ + \r\nABCD" + .as_bytes(); + + let (body, req) = sip_parse_request(buf).expect("parsing failed"); + assert_eq!(req.method, "REGISTER"); + assert_eq!(req.path, "sip:sip.cybercity.dk"); + assert_eq!(req.version, "SIP/2.0"); + assert_eq!(req.headers["Content-Length"], "4"); + assert_eq!(body, "ABCD".as_bytes()); + } + + #[test] + fn test_parse_response() { + let buf: &[u8] = "SIP/2.0 401 Unauthorized\r\n\ + \r\n" + .as_bytes(); + + match sip_parse_response(buf) { + Ok((_, resp)) => { + assert_eq!(resp.version, "SIP/2.0"); + assert_eq!(resp.code, "401"); + assert_eq!(resp.reason, "Unauthorized"); + } + _ => { + assert!(false); + } + } + } +} diff --git a/rust/src/sip/sip.rs b/rust/src/sip/sip.rs new file mode 100755 index 0000000..4e86f5e --- /dev/null +++ b/rust/src/sip/sip.rs @@ -0,0 +1,391 @@ +/* Copyright (C) 2019-2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// written by Giuseppe Longo <giuseppe@glongo.it> + +use crate::frames::*; +use crate::applayer::{self, *}; +use crate::core; +use crate::core::{AppProto, Flow, ALPROTO_UNKNOWN}; +use crate::sip::parser::*; +use nom7::Err; +use std; +use std::ffi::CString; + +// app-layer-frame-documentation tag start: FrameType enum +#[derive(AppLayerFrameType)] +pub enum SIPFrameType { + Pdu, + RequestLine, + ResponseLine, + RequestHeaders, + ResponseHeaders, + RequestBody, + ResponseBody, +} +// app-layer-frame-documentation tag end: FrameType enum + +#[derive(AppLayerEvent)] +pub enum SIPEvent { + IncompleteData, + InvalidData, +} + +#[derive(Default)] +pub struct SIPState { + state_data: AppLayerStateData, + transactions: Vec<SIPTransaction>, + tx_id: u64, +} + +impl State<SIPTransaction> for SIPState { + fn get_transaction_count(&self) -> usize { + self.transactions.len() + } + + fn get_transaction_by_index(&self, index: usize) -> Option<&SIPTransaction> { + self.transactions.get(index) + } +} + +pub struct SIPTransaction { + id: u64, + pub request: Option<Request>, + pub response: Option<Response>, + pub request_line: Option<String>, + pub response_line: Option<String>, + tx_data: applayer::AppLayerTxData, +} + +impl Transaction for SIPTransaction { + fn id(&self) -> u64 { + self.id + } +} + +impl SIPState { + pub fn new() -> SIPState { + Default::default() + } + + pub fn free(&mut self) { + self.transactions.clear(); + } + + fn new_tx(&mut self, _direction: crate::core::Direction) -> SIPTransaction { + self.tx_id += 1; + SIPTransaction::new(self.tx_id) + } + + fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SIPTransaction> { + self.transactions.iter().find(|&tx| tx.id == tx_id + 1) + } + + fn free_tx(&mut self, tx_id: u64) { + let tx = self + .transactions + .iter() + .position(|tx| tx.id == tx_id + 1); + debug_assert!(tx.is_some()); + if let Some(idx) = tx { + let _ = self.transactions.remove(idx); + } + } + + fn set_event(&mut self, event: SIPEvent) { + if let Some(tx) = self.transactions.last_mut() { + tx.tx_data.set_event(event as u8); + } + } + + // 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(); + let _pdu = Frame::new( + flow, + &stream_slice, + input, + input.len() as i64, + SIPFrameType::Pdu as u8, + ); + SCLogDebug!("ts: pdu {:?}", _pdu); + + 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); + return true; + } + // app-layer-frame-documentation tag end: parse_request + Err(Err::Incomplete(_)) => { + self.set_event(SIPEvent::IncompleteData); + return false; + } + Err(_) => { + self.set_event(SIPEvent::InvalidData); + return false; + } + } + } + + fn parse_response(&mut self, flow: *const core::Flow, stream_slice: StreamSlice) -> bool { + let input = stream_slice.as_slice(); + let _pdu = Frame::new(flow, &stream_slice, input, input.len() as i64, SIPFrameType::Pdu as u8); + SCLogDebug!("tc: pdu {:?}", _pdu); + + 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); + return true; + } + Err(Err::Incomplete(_)) => { + self.set_event(SIPEvent::IncompleteData); + return false; + } + Err(_) => { + self.set_event(SIPEvent::InvalidData); + return false; + } + } + } +} + +impl SIPTransaction { + pub fn new(id: u64) -> SIPTransaction { + SIPTransaction { + id, + request: None, + response: None, + request_line: None, + response_line: None, + tx_data: applayer::AppLayerTxData::new(), + } + } +} + +// app-layer-frame-documentation tag start: function to add frames +fn sip_frames_ts(flow: *const core::Flow, stream_slice: &StreamSlice, r: &Request) { + let oi = stream_slice.as_slice(); + let _f = Frame::new( + flow, + stream_slice, + oi, + r.request_line_len as i64, + SIPFrameType::RequestLine as u8, + ); + SCLogDebug!("ts: request_line {:?}", _f); + let hi = &oi[r.request_line_len as usize..]; + let _f = Frame::new( + flow, + stream_slice, + hi, + r.headers_len as i64, + SIPFrameType::RequestHeaders as u8, + ); + SCLogDebug!("ts: request_headers {:?}", _f); + if r.body_len > 0 { + let bi = &oi[r.body_offset as usize..]; + let _f = Frame::new( + flow, + stream_slice, + bi, + r.body_len as i64, + SIPFrameType::RequestBody as u8, + ); + SCLogDebug!("ts: request_body {:?}", _f); + } +} +// app-layer-frame-documentation tag end: function to add frames + +fn sip_frames_tc(flow: *const core::Flow, stream_slice: &StreamSlice, r: &Response) { + let oi = stream_slice.as_slice(); + let _f = Frame::new(flow, stream_slice, oi, r.response_line_len as i64, SIPFrameType::ResponseLine as u8); + let hi = &oi[r.response_line_len as usize ..]; + SCLogDebug!("tc: response_line {:?}", _f); + let _f = Frame::new(flow, stream_slice, hi, r.headers_len as i64, SIPFrameType::ResponseHeaders as u8); + SCLogDebug!("tc: response_headers {:?}", _f); + if r.body_len > 0 { + let bi = &oi[r.body_offset as usize ..]; + let _f = Frame::new(flow, stream_slice, bi, r.body_len as i64, SIPFrameType::ResponseBody as u8); + SCLogDebug!("tc: response_body {:?}", _f); + } +} + +#[no_mangle] +pub extern "C" fn rs_sip_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void { + let state = SIPState::new(); + let boxed = Box::new(state); + return Box::into_raw(boxed) as *mut _; +} + +#[no_mangle] +pub extern "C" fn rs_sip_state_free(state: *mut std::os::raw::c_void) { + let mut state = unsafe { Box::from_raw(state as *mut SIPState) }; + state.free(); +} + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_state_get_tx( + state: *mut std::os::raw::c_void, + tx_id: u64, +) -> *mut std::os::raw::c_void { + let state = cast_pointer!(state, SIPState); + match state.get_tx_by_id(tx_id) { + Some(tx) => tx as *const _ as *mut _, + None => std::ptr::null_mut(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 { + let state = cast_pointer!(state, SIPState); + state.tx_id +} + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) { + let state = cast_pointer!(state, SIPState); + state.free_tx(tx_id); +} + +#[no_mangle] +pub extern "C" fn rs_sip_tx_get_alstate_progress( + _tx: *mut std::os::raw::c_void, + _direction: u8, +) -> std::os::raw::c_int { + 1 +} + +static mut ALPROTO_SIP: AppProto = ALPROTO_UNKNOWN; + +#[no_mangle] +pub unsafe extern "C" fn rs_sip_probing_parser_ts( + _flow: *const Flow, + _direction: u8, + input: *const u8, + input_len: u32, + _rdir: *mut u8, +) -> AppProto { + let buf = build_slice!(input, input_len as usize); + if sip_parse_request(buf).is_ok() { + return ALPROTO_SIP; + } + 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, +) -> AppProto { + let buf = build_slice!(input, input_len as usize); + if sip_parse_response(buf).is_ok() { + return ALPROTO_SIP; + } + 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, + stream_slice: StreamSlice, + _data: *const std::os::raw::c_void, +) -> AppLayerResult { + let state = cast_pointer!(state, SIPState); + state.parse_request(flow, stream_slice).into() +} + +#[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, + stream_slice: StreamSlice, + _data: *const std::os::raw::c_void, +) -> AppLayerResult { + let state = cast_pointer!(state, SIPState); + state.parse_response(flow, stream_slice).into() +} + +export_tx_data_get!(rs_sip_get_tx_data, SIPTransaction); +export_state_data_get!(rs_sip_get_state_data, SIPState); + +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 { + name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, + default_port: default_port.as_ptr(), + ipproto: core::IPPROTO_UDP, + probe_ts: Some(rs_sip_probing_parser_ts), + probe_tc: Some(rs_sip_probing_parser_tc), + min_depth: 0, + max_depth: 16, + state_new: rs_sip_state_new, + state_free: rs_sip_state_free, + tx_free: rs_sip_state_tx_free, + parse_ts: rs_sip_parse_request, + parse_tc: rs_sip_parse_response, + get_tx_count: rs_sip_state_get_tx_count, + get_tx: rs_sip_state_get_tx, + tx_comp_st_ts: 1, + tx_comp_st_tc: 1, + tx_get_progress: rs_sip_tx_get_alstate_progress, + get_eventinfo: Some(SIPEvent::get_event_info), + get_eventinfo_byid: Some(SIPEvent::get_event_info_by_id), + localstorage_new: None, + localstorage_free: None, + get_tx_files: None, + get_tx_iterator: Some(applayer::state_get_tx_iterator::<SIPState, SIPTransaction>), + get_tx_data: rs_sip_get_tx_data, + get_state_data: rs_sip_get_state_data, + apply_tx_config: None, + flags: 0, + truncate: None, + get_frame_id_by_name: Some(SIPFrameType::ffi_id_from_name), + get_frame_name_by_id: Some(SIPFrameType::ffi_name_from_id), + }; + + let ip_proto_str = CString::new("udp").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 detecter and parser disabled for SIP/UDP."); + } +} |