From: Giuseppe Longo Date: Sat, 9 Feb 2019 08:02:11 +0000 (+0100) Subject: rust/sip: add parser for SIP protocol X-Git-Tag: suricata-5.0.0-rc1~38 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2e975a0481ed93428a2956466688a89f61188f8d;p=thirdparty%2Fsuricata.git rust/sip: add parser for SIP protocol --- diff --git a/rust/gen-c-headers.py b/rust/gen-c-headers.py index ecf8b0fd90..ea7f873322 100755 --- a/rust/gen-c-headers.py +++ b/rust/gen-c-headers.py @@ -93,6 +93,8 @@ type_map = { "IKEV2Transaction": "IKEV2Transaction", "KRB5State": "KRB5State", "KRB5Transaction": "KRB5Transaction", + "SIPState": "SIPState", + "SIPTransaction": "SIPTransaction", "JsonT": "json_t", "DetectEngineState": "DetectEngineState", "core::DetectEngineState": "DetectEngineState", diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 3a08d605c3..e0955b1a41 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -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 index 0000000000..d9ff115112 --- /dev/null +++ b/rust/src/sip/mod.rs @@ -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 + +pub mod sip; +pub mod parser; diff --git a/rust/src/sip/parser.rs b/rust/src/sip/parser.rs new file mode 100644 index 0000000000..a82b1f3d3e --- /dev/null +++ b/rust/src/sip/parser.rs @@ -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 + +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 +} + +#[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, delimited!( + take_while!(is_space), + char!(':'), + take_while!(is_space) +)); + +named!(message_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 >, + 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> +{ + let mut headers_map: HashMap = 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: ;tag=903df0a\r\n\ + To: \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 index 0000000000..0c1f366d3e --- /dev/null +++ b/rust/src/sip/sip.rs @@ -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 + +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 { + match value { + 0 => Some(SIPEvent::IncompleteData), + 1 => Some(SIPEvent::InvalidData), + _ => None, + } + } +} + +pub struct SIPState { + transactions: Vec, + tx_id: u64, +} + +pub struct SIPTransaction { + id: u64, + pub request: Option, + pub response: Option, + pub request_line: Option, + pub response_line: Option, + 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 = 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."); + } +} diff --git a/src/Makefile.am b/src/Makefile.am index 38d116bbc3..b29ec4086d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index c1cf527564..5d078ad024 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -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) diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 8fc629c56e..54bd2532b9 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -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(); diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index c937fc9ce5..e80656b101 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -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; diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index da9548f97a..45d04adea8 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -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 index 0000000000..aa42044f4c --- /dev/null +++ b/src/app-layer-sip.c @@ -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 + * + * 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 index 0000000000..0bae92496d --- /dev/null +++ b/src/app-layer-sip.h @@ -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 + */ + +#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__ */ diff --git a/suricata.yaml.in b/suricata.yaml.in index 1078580db4..240f875288 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -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