]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
rust/sip: add parser for SIP protocol
authorGiuseppe Longo <giuseppe@glongo.it>
Sat, 9 Feb 2019 08:02:11 +0000 (09:02 +0100)
committerVictor Julien <victor@inliniac.net>
Tue, 17 Sep 2019 08:42:20 +0000 (10:42 +0200)
13 files changed:
rust/gen-c-headers.py
rust/src/lib.rs
rust/src/sip/mod.rs [new file with mode: 0644]
rust/src/sip/parser.rs [new file with mode: 0644]
rust/src/sip/sip.rs [new file with mode: 0755]
src/Makefile.am
src/app-layer-detect-proto.c
src/app-layer-parser.c
src/app-layer-protos.c
src/app-layer-protos.h
src/app-layer-sip.c [new file with mode: 0644]
src/app-layer-sip.h [new file with mode: 0644]
suricata.yaml.in

index ecf8b0fd90ab72a7c20408bceba754f35b46a605..ea7f8733220d4d0cc34e4b199bb269a8b1f28355 100755 (executable)
@@ -93,6 +93,8 @@ type_map = {
     "IKEV2Transaction": "IKEV2Transaction",
     "KRB5State": "KRB5State",
     "KRB5Transaction": "KRB5Transaction",
+    "SIPState": "SIPState",
+    "SIPTransaction": "SIPTransaction",
     "JsonT": "json_t",
     "DetectEngineState": "DetectEngineState",
     "core::DetectEngineState": "DetectEngineState",
index 3a08d605c3dcbf6732193b3b415d696069cb3bb5..e0955b1a41aba3f8bf2a3397fe3a3b0ab1e123f6 100644 (file)
@@ -65,5 +65,6 @@ pub mod snmp;
 pub mod ntp;
 pub mod tftp;
 pub mod dhcp;
+pub mod sip;
 pub mod applayertemplate;
 pub mod rdp;
diff --git a/rust/src/sip/mod.rs b/rust/src/sip/mod.rs
new file mode 100644 (file)
index 0000000..d9ff115
--- /dev/null
@@ -0,0 +1,21 @@
+/* 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>
+
+pub mod sip;
+pub mod parser;
diff --git a/rust/src/sip/parser.rs b/rust/src/sip/parser.rs
new file mode 100644 (file)
index 0000000..a82b1f3
--- /dev/null
@@ -0,0 +1,250 @@
+/* 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@glono.it>
+
+use std;
+use std::collections::HashMap;
+use nom::{IResult,crlf};
+use nom::*;
+
+#[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>
+}
+
+#[derive(Debug)]
+pub struct Response {
+    pub version: String,
+    pub code: String,
+    pub reason: String
+}
+
+#[derive(PartialEq,Debug,Clone)]
+pub enum Method {
+    Register,
+    Custom(String),
+}
+
+#[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)
+}
+
+named!(pub sip_parse_request<&[u8], Request>,
+    do_parse!(
+        method: parse_method >> char!(' ') >>
+        path: parse_request_uri >> char!(' ') >>
+        version: parse_version >> crlf >>
+        headers: parse_headers >>
+        crlf >>
+        (Request { method: method.into(), path: path.into(), version: version.into(), headers: headers})
+    )
+);
+
+named!(pub sip_parse_response<&[u8], Response>,
+    do_parse!(
+        version: parse_version >> char!(' ') >>
+        code: parse_code >> char!(' ') >>
+        reason: parse_reason >> crlf >>
+        (Response { version: version.into(), code: code.into(), reason: reason.into() })
+    )
+);
+
+named!(#[inline], parse_method<&[u8], &str>,
+    map_res!(take_while!(is_method_char), std::str::from_utf8)
+);
+
+named!(#[inline], parse_request_uri<&[u8], &str>,
+    map_res!(take_while1!(is_request_uri_char), std::str::from_utf8)
+);
+
+named!(#[inline], parse_version<&[u8], &str>,
+    map_res!(take_while1!(is_version_char), std::str::from_utf8)
+);
+
+named!(#[inline], parse_code<&[u8], &str>,
+    map_res!(take!(3), std::str::from_utf8)
+);
+
+named!(#[inline], parse_reason<&[u8], &str>,
+    map_res!(take_while!(is_reason_phrase), std::str::from_utf8)
+);
+
+named!(#[inline], header_name<&[u8], &str>,
+        map_res!(take_while!(is_header_name), std::str::from_utf8)
+);
+
+named!(#[inline], header_value<&[u8], &str>,
+    map_res!(parse_header_value, std::str::from_utf8)
+);
+
+named!(hcolon<char>, delimited!(
+    take_while!(is_space),
+    char!(':'),
+    take_while!(is_space)
+));
+
+named!(message_header<Header>, do_parse!(
+    n: header_name >>
+    hcolon >>
+    v: header_value >>
+    crlf >>
+    (Header{ name: String::from(n), value: String::from(v) })
+));
+
+named!(pub sip_take_line<&[u8], Option<String> >,
+    do_parse!(
+        line: map_res!(take_while1!(is_reason_phrase), std::str::from_utf8) >>
+        (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) {
+            Ok((_, _)) => {
+                break;
+            }
+            Err(Err::Error(_)) => {}
+            Err(Err::Failure(_)) => {}
+            Err(Err::Incomplete(e)) => return Err(Err::Incomplete(e)),
+        };
+        let (rest, header) = try_parse!(input, message_header);
+        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 idx = 0;
+    while idx < buf.len() {
+        match buf[idx] {
+            b'\n' => {
+                idx += 1;
+                if idx >= buf.len() {
+                    return Err(Err::Incomplete(Needed::Size(1)));
+                }
+                match buf[idx] {
+                    b' ' | b'\t' => {
+                        idx += 1;
+                        continue;
+                    }
+                    _ => {
+                        return Ok((&buf[end_pos..], &buf[..end_pos]));
+                    }
+                }
+            }
+            b' ' | b'\t' | b'\r' => {}
+            b => {
+                if !is_header_value(b) {
+                    return Err(Err::Incomplete(Needed::Size(1)));
+                }
+                end_pos = idx + 1;
+            }
+        }
+        idx += 1;
+    }
+    Ok((&b""[..], buf))
+}
+
+#[cfg(test)]
+mod tests {
+
+    use crate::sip::sip::*;
+    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_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 (executable)
index 0000000..0c1f366
--- /dev/null
@@ -0,0 +1,433 @@
+/* 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>
+
+extern crate nom;
+
+use std;
+use std::ffi::{CStr,CString};
+use applayer;
+use core;
+use core::{AppProto,Flow,ALPROTO_UNKNOWN,sc_detect_engine_state_free};
+use parser::*;
+use log::*;
+use sip::parser::*;
+
+#[repr(u32)]
+pub enum SIPEvent {
+    IncompleteData = 0,
+    InvalidData,
+}
+
+impl SIPEvent {
+    fn from_i32(value: i32) -> Option<SIPEvent> {
+        match value {
+            0 => Some(SIPEvent::IncompleteData),
+            1 => Some(SIPEvent::InvalidData),
+            _ => None,
+        }
+    }
+}
+
+pub struct SIPState {
+    transactions: Vec<SIPTransaction>,
+    tx_id: u64,
+}
+
+pub struct SIPTransaction {
+    id: u64,
+    pub request: Option<Request>,
+    pub response: Option<Response>,
+    pub request_line: Option<String>,
+    pub response_line: Option<String>,
+    de_state: Option<*mut core::DetectEngineState>,
+    events: *mut core::AppLayerDecoderEvents,
+    logged: applayer::LoggerFlags,
+}
+
+impl SIPState {
+    pub fn new() -> SIPState {
+        SIPState{
+            transactions: Vec::new(),
+            tx_id: 0,
+        }
+    }
+
+    pub fn free(&mut self) {
+        self.transactions.clear();
+    }
+
+    fn new_tx(&mut self) -> 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(|ref tx| tx.id == tx_id + 1);
+        debug_assert!(tx != None);
+        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() {
+            let ev = event as u8;
+            core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events, ev);
+        }
+    }
+
+    fn parse_request(&mut self, input: &[u8]) -> bool {
+        match sip_parse_request(input) {
+            Ok((_, request)) => {
+                let mut tx = self.new_tx();
+                tx.request = Some(request);
+                if let Ok((_, req_line)) = sip_take_line(input) {
+                    tx.request_line = req_line;
+                }
+                self.transactions.push(tx);
+                return true;
+            }
+            Err(nom::Err::Incomplete(_)) => {
+                self.set_event(SIPEvent::IncompleteData);
+                return false;
+            }
+            Err(_) => {
+                self.set_event(SIPEvent::InvalidData);
+                return false;
+            }
+        }
+    }
+
+    fn parse_response(&mut self, input: &[u8]) -> bool {
+        match sip_parse_response(input) {
+            Ok((_, response)) => {
+                let mut tx = self.new_tx();
+                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(nom::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: id,
+            de_state: None,
+            request: None,
+            response: None,
+            request_line: None,
+            response_line: None,
+            events: std::ptr::null_mut(),
+            logged: applayer::LoggerFlags::new(),
+        }
+    }
+}
+
+impl Drop for SIPTransaction {
+    fn drop(&mut self) {
+        if self.events != std::ptr::null_mut() {
+            core::sc_app_layer_decoder_events_free_events(&mut self.events);
+        }
+        if let Some(state) = self.de_state {
+            sc_detect_engine_state_free(state);
+        }
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_sip_state_new() -> *mut std::os::raw::c_void {
+    let state = SIPState::new();
+    let boxed = Box::new(state);
+    return unsafe{std::mem::transmute(boxed)};
+}
+
+#[no_mangle]
+pub extern "C" fn rs_sip_state_free(state: *mut std::os::raw::c_void) {
+    let mut state: Box<SIPState> = unsafe{std::mem::transmute(state)};
+    state.free();
+}
+
+#[no_mangle]
+pub 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) => unsafe{std::mem::transmute(tx)},
+        None     => std::ptr::null_mut(),
+    }
+}
+
+#[no_mangle]
+pub 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 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_state_progress_completion_status(
+    _direction: u8)
+    -> std::os::raw::c_int
+{
+    return 1;
+}
+
+#[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
+}
+
+#[no_mangle]
+pub extern "C" fn rs_sip_tx_set_logged(_state: *mut std::os::raw::c_void,
+                                       tx: *mut std::os::raw::c_void,
+                                       logged: u32)
+{
+    let tx = cast_pointer!(tx,SIPTransaction);
+    tx.logged.set(logged);
+}
+
+#[no_mangle]
+pub extern "C" fn rs_sip_tx_get_logged(_state: *mut std::os::raw::c_void,
+                                       tx: *mut std::os::raw::c_void)
+                                       -> u32
+{
+    let tx = cast_pointer!(tx,SIPTransaction);
+    return tx.logged.get();
+}
+
+
+#[no_mangle]
+pub extern "C" fn rs_sip_state_set_tx_detect_state(
+    tx: *mut std::os::raw::c_void,
+    de_state: &mut core::DetectEngineState) -> std::os::raw::c_int
+{
+    let tx = cast_pointer!(tx,SIPTransaction);
+    tx.de_state = Some(de_state);
+    0
+}
+
+#[no_mangle]
+pub extern "C" fn rs_sip_state_get_tx_detect_state(
+    tx: *mut std::os::raw::c_void)
+    -> *mut core::DetectEngineState
+{
+    let tx = cast_pointer!(tx,SIPTransaction);
+    match tx.de_state {
+        Some(ds) => ds,
+        None => std::ptr::null_mut(),
+    }
+}
+
+
+#[no_mangle]
+pub extern "C" fn rs_sip_state_get_events(tx: *mut std::os::raw::c_void)
+                                          -> *mut core::AppLayerDecoderEvents
+{
+    let tx = cast_pointer!(tx, SIPTransaction);
+    return tx.events
+}
+
+#[no_mangle]
+pub extern "C" fn rs_sip_state_get_event_info(event_name: *const std::os::raw::c_char,
+                                              event_id: *mut std::os::raw::c_int,
+                                              event_type: *mut core::AppLayerEventType)
+                                              -> std::os::raw::c_int
+{
+    if event_name == std::ptr::null() { return -1; }
+    let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) };
+    let event = match c_event_name.to_str() {
+        Ok(s) => {
+            match s {
+                "incomplete_data"     => SIPEvent::IncompleteData as i32,
+                "invalid_data"        => SIPEvent::InvalidData as i32,
+                _                     => -1, // unknown event
+            }
+        },
+        Err(_) => -1, // UTF-8 conversion failed
+    };
+    unsafe{
+        *event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION;
+        *event_id = event as std::os::raw::c_int;
+    };
+    0
+}
+
+#[no_mangle]
+pub extern "C" fn rs_sip_state_get_event_info_by_id(event_id: std::os::raw::c_int,
+                                                    event_name: *mut *const std::os::raw::c_char,
+                                                    event_type: *mut core::AppLayerEventType)
+                                                    -> i8
+{
+    if let Some(e) = SIPEvent::from_i32(event_id as i32) {
+        let estr = match e {
+            SIPEvent::IncompleteData    => { "incomplete_data\0" },
+            SIPEvent::InvalidData       => { "invalid_data\0" },
+        };
+        unsafe{
+            *event_name = estr.as_ptr() as *const std::os::raw::c_char;
+            *event_type = core::APP_LAYER_EVENT_TYPE_TRANSACTION;
+        };
+        0
+    } else {
+        -1
+    }
+}
+
+static mut ALPROTO_SIP : AppProto = ALPROTO_UNKNOWN;
+
+#[no_mangle]
+pub 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 unsafe{ ALPROTO_SIP };
+    }
+    return ALPROTO_UNKNOWN;
+}
+
+#[no_mangle]
+pub 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 unsafe { ALPROTO_SIP };
+    }
+    return ALPROTO_UNKNOWN;
+}
+
+#[no_mangle]
+pub 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,
+                                       input: *const u8,
+                                       input_len: u32,
+                                       _data: *const std::os::raw::c_void,
+                                       _flags: u8) -> i32 {
+    let buf = build_slice!(input,input_len as usize);
+    let state = cast_pointer!(state,SIPState);
+    if state.parse_request(buf) {
+        1
+    } else {
+        -1
+    }
+}
+
+#[no_mangle]
+pub 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,
+                                       input: *const u8,
+                                       input_len: u32,
+                                       _data: *const std::os::raw::c_void,
+                                       _flags: u8) -> i32 {
+    let buf = build_slice!(input,input_len as usize);
+    let state = cast_pointer!(state,SIPState);
+    if state.parse_response(buf) {
+        1
+    } else {
+        -1
+    }
+}
+
+const PARSER_NAME : &'static [u8] = b"sip\0";
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_sip_register_parser() {
+    let default_port = CString::new("5060").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           : rs_sip_probing_parser_ts,
+        probe_tc           : 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_get_comp_st     : rs_sip_state_progress_completion_status,
+        tx_get_progress    : rs_sip_tx_get_alstate_progress,
+        get_tx_logged      : Some(rs_sip_tx_get_logged),
+        set_tx_logged      : Some(rs_sip_tx_set_logged),
+        get_de_state       : rs_sip_state_get_tx_detect_state,
+        set_de_state       : rs_sip_state_set_tx_detect_state,
+        get_events         : Some(rs_sip_state_get_events),
+        get_eventinfo      : Some(rs_sip_state_get_event_info),
+        get_eventinfo_byid : Some(rs_sip_state_get_event_info_by_id),
+        localstorage_new   : None,
+        localstorage_free  : None,
+        get_tx_mpm_id      : None,
+        set_tx_mpm_id      : None,
+        get_files          : None,
+        get_tx_iterator    : None,
+    };
+
+    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.");
+    }
+}
index 38d116bbc3fceab85391cd31b1d1429a4b2fbb4b..b29ec4086df286c88cd453e1cd33bf514174ec48 100644 (file)
@@ -54,6 +54,7 @@ app-layer-template-rust.c app-layer-template-rust.h \
 app-layer-rdp.c app-layer-rdp.h \
 app-layer-ssh.c app-layer-ssh.h \
 app-layer-ssl.c app-layer-ssl.h \
+app-layer-sip.c app-layer-sip.h \
 conf.c conf.h \
 conf-yaml-loader.c conf-yaml-loader.h \
 counters.c counters.h \
index c1cf52756406d226de886ad547629bdc7974cd14..5d078ad024b22b08db4b6432f0af06f1d1a9e81a 100644 (file)
@@ -867,6 +867,8 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar
                         printf("            alproto: ALPROTO_DHCP\n");
                     else if (pp_pe->alproto == ALPROTO_SNMP)
                         printf("            alproto: ALPROTO_SNMP\n");
+                    else if (pp_pe->alproto == ALPROTO_SIP)
+                        printf("            alproto: ALPROTO_SIP\n");
                     else if (pp_pe->alproto == ALPROTO_TEMPLATE_RUST)
                         printf("            alproto: ALPROTO_TEMPLATE_RUST\n");
                     else if (pp_pe->alproto == ALPROTO_TEMPLATE)
@@ -940,6 +942,8 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar
                     printf("            alproto: ALPROTO_DHCP\n");
                 else if (pp_pe->alproto == ALPROTO_SNMP)
                     printf("            alproto: ALPROTO_SNMP\n");
+                else if (pp_pe->alproto == ALPROTO_SIP)
+                    printf("            alproto: ALPROTO_SIP\n");
                 else if (pp_pe->alproto == ALPROTO_TEMPLATE_RUST)
                     printf("            alproto: ALPROTO_TEMPLATE_RUST\n");
                 else if (pp_pe->alproto == ALPROTO_TEMPLATE)
index 8fc629c56e9d41de1833232db7ec64cd3d1e2078..54bd2532b96e3f7d70668e37eb08b54c5bee3815 100644 (file)
@@ -67,6 +67,7 @@
 #include "app-layer-krb5.h"
 #include "app-layer-dhcp.h"
 #include "app-layer-snmp.h"
+#include "app-layer-sip.h"
 #include "app-layer-template.h"
 #include "app-layer-template-rust.h"
 #include "app-layer-rdp.h"
@@ -1533,6 +1534,7 @@ void AppLayerParserRegisterProtocolParsers(void)
     RegisterKRB5Parsers();
     RegisterDHCPParsers();
     RegisterSNMPParsers();
+    RegisterSIPParsers();
     RegisterTemplateRustParsers();
     RegisterTemplateParsers();
     RegisterRdpParsers();
index c937fc9ce5e0b79b1415748b9eb5756ae06363e9..e80656b10149dd4c5f26cb708898cf11efb6c0a4 100644 (file)
@@ -102,6 +102,9 @@ const char *AppProtoToString(AppProto alproto)
         case ALPROTO_SNMP:
             proto_name = "snmp";
             break;
+        case ALPROTO_SIP:
+            proto_name = "sip";
+            break;
         case ALPROTO_TEMPLATE:
             proto_name = "template";
             break;
@@ -150,6 +153,7 @@ AppProto StringToAppProto(const char *proto_name)
     if (strcmp(proto_name,"krb5")==0) return ALPROTO_KRB5;
     if (strcmp(proto_name,"dhcp")==0) return ALPROTO_DHCP;
     if (strcmp(proto_name,"snmp")==0) return ALPROTO_SNMP;
+    if (strcmp(proto_name,"sip")==0) return ALPROTO_SIP;
     if (strcmp(proto_name,"template")==0) return ALPROTO_TEMPLATE;
     if (strcmp(proto_name,"template-rust")==0) return ALPROTO_TEMPLATE_RUST;
     if (strcmp(proto_name,"rdp")==0) return ALPROTO_RDP;
index da9548f97a1c7a139b93c4077d30d7971a238e53..45d04adea87df4ab02a0cfbb3b4189225d8c71ea 100644 (file)
@@ -51,6 +51,7 @@ enum AppProtoEnum {
     ALPROTO_KRB5,
     ALPROTO_DHCP,
     ALPROTO_SNMP,
+    ALPROTO_SIP,
     ALPROTO_TEMPLATE,
     ALPROTO_TEMPLATE_RUST,
     ALPROTO_RDP,
diff --git a/src/app-layer-sip.c b/src/app-layer-sip.c
new file mode 100644 (file)
index 0000000..aa42044
--- /dev/null
@@ -0,0 +1,55 @@
+/* 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.
+ */
+
+/**
+ * \file
+ *
+ * \author Giuseppe Longo <giuseppe@glongo.it>
+ *
+ * Parser for SIP application layer running on UDP port 5060.
+ */
+
+#include "suricata-common.h"
+#include "stream.h"
+#include "conf.h"
+
+#include "util-unittest.h"
+
+#include "app-layer-detect-proto.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-sip.h"
+#include "rust-sip-sip-gen.h"
+
+void RegisterSIPParsers(void)
+{
+    rs_sip_register_parser();
+
+#ifdef UNITTESTS
+    AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SIP,
+        SIPParserRegisterTests);
+#endif
+}
+
+#ifdef UNITTESTS
+#endif
+
+void SIPParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+#endif
+}
diff --git a/src/app-layer-sip.h b/src/app-layer-sip.h
new file mode 100644 (file)
index 0000000..0bae924
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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.
+ */
+
+/**
+ * \file
+ *
+ * \author Giuseppe Longo <giuseppe@glongo.it>
+ */
+
+#ifndef __APP_LAYER_SIP_H__
+#define __APP_LAYER_SIP_H__
+
+void RegisterSIPParsers(void);
+void SIPParserRegisterTests(void);
+
+/** Opaque Rust types. */
+typedef struct SIPState_ SIPState;
+typedef struct SIPTransaction_ SIPTransaction;
+
+#endif /* __APP_LAYER_SIP_H__ */
index 1078580db4c0da079556ef06f54385f61824f9ac..240f875288167978b0d9c953f8c7a931995d31b5 100644 (file)
@@ -1042,6 +1042,9 @@ app-layer:
     dhcp:
       enabled: yes
 
+    sip:
+      enabled: yes
+
 # Limit for the maximum number of asn1 frames to decode (default 256)
 asn1-max-frames: 256