mirror of https://github.com/OISF/suricata
rust/sip: add parser for SIP protocol
parent
a2356a89f7
commit
2e975a0481
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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.");
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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__ */
|
||||
Loading…
Reference in New Issue