summaryrefslogtreecommitdiffstats
path: root/rust/src/sip
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/sip')
-rw-r--r--rust/src/sip/detect.rs182
-rw-r--r--rust/src/sip/log.rs54
-rwxr-xr-xrust/src/sip/mod.rs25
-rw-r--r--rust/src/sip/parser.rs325
-rwxr-xr-xrust/src/sip/sip.rs391
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.");
+ }
+}