]> git.ipfire.org Git - people/ms/suricata.git/commitdiff
parse: move SSH parser from C to Rust
authorPhilippe Antoine <contact@catenacyber.fr>
Thu, 5 Mar 2020 14:11:00 +0000 (15:11 +0100)
committerVictor Julien <victor@inliniac.net>
Fri, 22 May 2020 06:40:01 +0000 (08:40 +0200)
18 files changed:
rust/src/lib.rs
rust/src/ssh/detect.rs [new file with mode: 0644]
rust/src/ssh/logger.rs [new file with mode: 0644]
rust/src/ssh/mod.rs [new file with mode: 0644]
rust/src/ssh/parser.rs [new file with mode: 0644]
rust/src/ssh/ssh.rs [new file with mode: 0644]
src/alert-prelude.c
src/app-layer-ssh.c
src/app-layer-ssh.h
src/detect-ssh-proto-version.c
src/detect-ssh-proto.c
src/detect-ssh-software-version.c
src/detect-ssh-software.c
src/output-json-alert.c
src/output-json-ssh.c
src/output-json-ssh.h
src/output-lua.c
src/util-lua-ssh.c

index a510cc736d745c0b1e335463b2a5476a1d274971..68ee041a00bdad23d57229decba51f98739146df 100644 (file)
@@ -71,3 +71,4 @@ pub mod rfb;
 pub mod applayertemplate;
 pub mod rdp;
 pub mod x509;
+pub mod ssh;
diff --git a/rust/src/ssh/detect.rs b/rust/src/ssh/detect.rs
new file mode 100644 (file)
index 0000000..8b2b0df
--- /dev/null
@@ -0,0 +1,92 @@
+/* Copyright (C) 2020 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.
+ */
+
+use super::ssh::SSHTransaction;
+use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER};
+use std::ptr;
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_tx_get_protocol(
+    tx: *mut std::os::raw::c_void, buffer: *mut *const u8, buffer_len: *mut u32, direction: u8,
+) -> u8 {
+    let tx = cast_pointer!(tx, SSHTransaction);
+    match direction {
+        STREAM_TOSERVER => {
+            let m = &tx.cli_hdr.protover;
+            if m.len() > 0 {
+                unsafe {
+                    *buffer = m.as_ptr();
+                    *buffer_len = m.len() as u32;
+                }
+                return 1;
+            }
+        }
+        STREAM_TOCLIENT => {
+            let m = &tx.srv_hdr.protover;
+            if m.len() > 0 {
+                unsafe {
+                    *buffer = m.as_ptr();
+                    *buffer_len = m.len() as u32;
+                }
+                return 1;
+            }
+        }
+        _ => {}
+    }
+    unsafe {
+        *buffer = ptr::null();
+        *buffer_len = 0;
+    }
+
+    return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_tx_get_software(
+    tx: *mut std::os::raw::c_void, buffer: *mut *const u8, buffer_len: *mut u32, direction: u8,
+) -> u8 {
+    let tx = cast_pointer!(tx, SSHTransaction);
+    match direction {
+        STREAM_TOSERVER => {
+            let m = &tx.cli_hdr.swver;
+            if m.len() > 0 {
+                unsafe {
+                    *buffer = m.as_ptr();
+                    *buffer_len = m.len() as u32;
+                }
+                return 1;
+            }
+        }
+        STREAM_TOCLIENT => {
+            let m = &tx.srv_hdr.swver;
+            if m.len() > 0 {
+                unsafe {
+                    *buffer = m.as_ptr();
+                    *buffer_len = m.len() as u32;
+                }
+                return 1;
+            }
+        }
+        _ => {}
+    }
+    unsafe {
+        *buffer = ptr::null();
+        *buffer_len = 0;
+    }
+
+    return 0;
+}
diff --git a/rust/src/ssh/logger.rs b/rust/src/ssh/logger.rs
new file mode 100644 (file)
index 0000000..000d7af
--- /dev/null
@@ -0,0 +1,64 @@
+/* Copyright (C) 2020 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.
+ */
+
+use super::ssh::SSHTransaction;
+use crate::json::*;
+
+fn log_ssh(tx: &SSHTransaction) -> Option<Json> {
+    if tx.cli_hdr.protover.len() == 0 && tx.srv_hdr.protover.len() == 0 {
+        return None;
+    }
+    let js = Json::object();
+    if tx.cli_hdr.protover.len() > 0 {
+        let cjs = Json::object();
+        cjs.set_string_from_bytes(
+            "proto_version",
+            &tx.cli_hdr.protover,
+        );
+        if tx.cli_hdr.swver.len() > 0 {
+            cjs.set_string_from_bytes(
+                "software_version",
+                &tx.cli_hdr.swver,
+            );
+        }
+        js.set("client", cjs);
+    }
+    if tx.srv_hdr.protover.len() > 0 {
+        let sjs = Json::object();
+        sjs.set_string_from_bytes(
+            "proto_version",
+            &tx.srv_hdr.protover,
+        );
+        if tx.srv_hdr.swver.len() > 0 {
+            sjs.set_string_from_bytes(
+                "software_version",
+                &tx.srv_hdr.swver,
+            );
+        }
+        js.set("server", sjs);
+    }
+    return Some(js);
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_log_json(tx: *mut std::os::raw::c_void) -> *mut JsonT {
+    let tx = cast_pointer!(tx, SSHTransaction);
+    match log_ssh(tx) {
+        Some(js) => js.unwrap(),
+        None => std::ptr::null_mut(),
+    }
+}
diff --git a/rust/src/ssh/mod.rs b/rust/src/ssh/mod.rs
new file mode 100644 (file)
index 0000000..12efccd
--- /dev/null
@@ -0,0 +1,21 @@
+/* Copyright (C) 2020 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.
+ */
+
+pub mod detect;
+pub mod logger;
+mod parser;
+pub mod ssh;
diff --git a/rust/src/ssh/parser.rs b/rust/src/ssh/parser.rs
new file mode 100644 (file)
index 0000000..1d2ad84
--- /dev/null
@@ -0,0 +1,196 @@
+/* Copyright (C) 2020 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.
+ */
+
+use nom::combinator::rest;
+use nom::number::streaming::{be_u32, be_u8};
+use std::fmt;
+
+#[inline]
+fn is_not_lineend(b: u8) -> bool {
+    if b == 10 || b == 13 {
+        return false;
+    }
+    return true;
+}
+
+//may leave \r at the end to be removed
+named!(pub ssh_parse_line<&[u8], &[u8]>,
+    terminated!(
+        take_while!(is_not_lineend),
+        alt!( tag!("\n") | tag!("\r\n") |
+              do_parse!(
+                    bytes: tag!("\r") >>
+                    not!(eof!()) >> (bytes)
+                )
+            )
+    )
+);
+
+#[derive(PartialEq)]
+pub struct SshBanner<'a> {
+    pub protover: &'a [u8],
+    pub swver: &'a [u8],
+}
+
+// Could be simplified adding dummy \n at the end
+// or use nom5 nom::bytes::complete::is_not
+named!(pub ssh_parse_banner<SshBanner>,
+    do_parse!(
+        tag!("SSH-") >>
+        protover: is_not!("-") >>
+        char!('-') >>
+        swver: alt!( complete!( is_not!(" \r\n") ) | rest ) >>
+        //remaining after space is comments
+        (SshBanner{protover, swver})
+    )
+);
+
+#[derive(PartialEq)]
+pub struct SshRecordHeader {
+    pub pkt_len: u32,
+    padding_len: u8,
+    pub msg_code: u8,
+}
+
+impl fmt::Display for SshRecordHeader {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "(pkt_len:{}, padding_len:{}, msg_code:{})",
+            self.pkt_len, self.padding_len, self.msg_code
+        )
+    }
+}
+
+named!(pub ssh_parse_record_header<SshRecordHeader>,
+    do_parse!(
+        pkt_len: verify!(be_u32, |&val| val > 1) >>
+        padding_len: be_u8 >>
+        msg_code: be_u8 >>
+        (SshRecordHeader{pkt_len, padding_len, msg_code})
+    )
+);
+
+//test for evasion against pkt_len=0or1...
+named!(pub ssh_parse_record<SshRecordHeader>,
+    do_parse!(
+        pkt_len: verify!(be_u32, |&val| val > 1) >>
+        padding_len: be_u8 >>
+        msg_code: be_u8 >>
+        take!((pkt_len-2) as usize) >>
+        (SshRecordHeader{pkt_len, padding_len, msg_code})
+    )
+);
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+
+    /// Simple test of some valid data.
+    #[test]
+    fn test_ssh_parse_banner() {
+        let buf = b"SSH-Single-";
+        let result = ssh_parse_banner(buf);
+        match result {
+            Ok((_, message)) => {
+                // Check the first message.
+                assert_eq!(message.protover, b"Single");
+                assert_eq!(message.swver, b"");
+            }
+            Err(err) => {
+                panic!("Result should not be an error: {:?}.", err);
+            }
+        }
+        let buf2 = b"SSH-2.0-Soft";
+        let result2 = ssh_parse_banner(buf2);
+        match result2 {
+            Ok((_, message)) => {
+                // Check the first message.
+                assert_eq!(message.protover, b"2.0");
+                assert_eq!(message.swver, b"Soft");
+            }
+            Err(err) => {
+                panic!("Result should not be an error: {:?}.", err);
+            }
+        }
+    }
+
+    #[test]
+    fn test_parse_line() {
+        let buf = b"SSH-Single\n";
+        let result = ssh_parse_line(buf);
+        match result {
+            Ok((_, message)) => {
+                // Check the first message.
+                assert_eq!(message, b"SSH-Single");
+            }
+            Err(err) => {
+                panic!("Result should not be an error: {:?}.", err);
+            }
+        }
+        let buf2 = b"SSH-Double\r\n";
+        let result2 = ssh_parse_line(buf2);
+        match result2 {
+            Ok((_, message)) => {
+                // Check the first message.
+                assert_eq!(message, b"SSH-Double");
+            }
+            Err(err) => {
+                panic!("Result should not be an error: {:?}.", err);
+            }
+        }
+        let buf3 = b"SSH-Oops\rMore\r\n";
+        let result3 = ssh_parse_line(buf3);
+        match result3 {
+            Ok((rem, message)) => {
+                // Check the first message.
+                assert_eq!(message, b"SSH-Oops");
+                assert_eq!(rem, b"More\r\n");
+            }
+            Err(err) => {
+                panic!("Result should not be an error: {:?}.", err);
+            }
+        }
+        let buf4 = b"SSH-Miss\r";
+        let result4 = ssh_parse_line(buf4);
+        match result4 {
+            Ok((_, _)) => {
+                panic!("Expected incomplete result");
+            }
+            Err(nom::Err::Incomplete(_)) => {
+                //OK
+                assert_eq!(1, 1);
+            }
+            Err(err) => {
+                panic!("Result should not be an error: {:?}.", err);
+            }
+        }
+        let buf5 = b"\n";
+        let result5 = ssh_parse_line(buf5);
+        match result5 {
+            Ok((_, message)) => {
+                // Check empty line
+                assert_eq!(message, b"");
+            }
+            Err(err) => {
+                panic!("Result should not be an error: {:?}.", err);
+            }
+        }
+    }
+
+}
diff --git a/rust/src/ssh/ssh.rs b/rust/src/ssh/ssh.rs
new file mode 100644 (file)
index 0000000..eba07c6
--- /dev/null
@@ -0,0 +1,531 @@
+/* Copyright (C) 2020 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.
+ */
+
+use super::parser;
+use crate::applayer::*;
+use crate::core::STREAM_TOSERVER;
+use crate::core::{self, AppProto, Flow, ALPROTO_UNKNOWN, IPPROTO_TCP};
+use crate::log::*;
+use std::ffi::{CStr, CString};
+use std::mem::transmute;
+
+static mut ALPROTO_SSH: AppProto = ALPROTO_UNKNOWN;
+
+#[repr(u32)]
+pub enum SSHEvent {
+    InvalidBanner = 0,
+    LongBanner,
+    InvalidRecord,
+}
+
+impl SSHEvent {
+    fn from_i32(value: i32) -> Option<SSHEvent> {
+        match value {
+            0 => Some(SSHEvent::InvalidBanner),
+            1 => Some(SSHEvent::LongBanner),
+            2 => Some(SSHEvent::InvalidRecord),
+            _ => None,
+        }
+    }
+}
+
+#[repr(u8)]
+#[derive(Copy, Clone, PartialOrd, PartialEq)]
+pub enum SSHConnectionState {
+    SshStateInProgress = 0,
+    SshStateBannerWaitEol = 1,
+    SshStateBannerDone = 2,
+    SshStateFinished = 3,
+}
+
+const SSH_MAX_BANNER_LEN: usize = 256;
+const SSH_RECORD_HEADER_LEN: usize = 6;
+//TODO complete enum and parse messages contents
+const SSH_MSG_NEWKEYS: u8 = 21;
+
+pub struct SshHeader {
+    record_left: u32,
+    flags: SSHConnectionState,
+    pub protover: Vec<u8>,
+    pub swver: Vec<u8>,
+}
+
+impl SshHeader {
+    pub fn new() -> SshHeader {
+        SshHeader {
+            record_left: 0,
+            flags: SSHConnectionState::SshStateInProgress,
+            protover: Vec::new(),
+            swver: Vec::new(),
+        }
+    }
+}
+
+pub struct SSHTransaction {
+    pub srv_hdr: SshHeader,
+    pub cli_hdr: SshHeader,
+
+    logged: LoggerFlags,
+    de_state: Option<*mut core::DetectEngineState>,
+    detect_flags: TxDetectFlags,
+    events: *mut core::AppLayerDecoderEvents,
+}
+
+impl SSHTransaction {
+    pub fn new() -> SSHTransaction {
+        SSHTransaction {
+            srv_hdr: SshHeader::new(),
+            cli_hdr: SshHeader::new(),
+            logged: LoggerFlags::new(),
+            de_state: None,
+            detect_flags: TxDetectFlags::default(),
+            events: std::ptr::null_mut(),
+        }
+    }
+
+    pub fn free(&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 {
+            core::sc_detect_engine_state_free(state);
+        }
+    }
+}
+
+impl Drop for SSHTransaction {
+    fn drop(&mut self) {
+        self.free();
+    }
+}
+
+pub struct SSHState {
+    transaction: SSHTransaction,
+}
+
+impl SSHState {
+    pub fn new() -> Self {
+        Self {
+            transaction: SSHTransaction::new(),
+        }
+    }
+
+    fn set_event(&mut self, event: SSHEvent) {
+        let ev = event as u8;
+        core::sc_app_layer_decoder_events_set_event_raw(&mut self.transaction.events, ev);
+    }
+
+    fn parse_record(
+        &mut self, mut input: &[u8], resp: bool, pstate: *mut std::os::raw::c_void,
+    ) -> AppLayerResult {
+        let (mut hdr, ohdr) = if !resp {
+            (&mut self.transaction.cli_hdr, &self.transaction.srv_hdr)
+        } else {
+            (&mut self.transaction.srv_hdr, &self.transaction.cli_hdr)
+        };
+        let il = input.len();
+        //first skip record left bytes
+        if hdr.record_left > 0 {
+            //should we check for overflow ?
+            let ilen = input.len() as u32;
+            if hdr.record_left >= ilen {
+                hdr.record_left -= ilen;
+                return AppLayerResult::ok();
+            } else {
+                let start = hdr.record_left as usize;
+                input = &input[start..];
+                hdr.record_left = 0;
+            }
+        }
+        //parse records out of input
+        while input.len() > 0 {
+            match parser::ssh_parse_record(input) {
+                Ok((rem, head)) => {
+                    SCLogDebug!("SSH valid record {}", head);
+                    input = rem;
+                    if head.msg_code == SSH_MSG_NEWKEYS {
+                        hdr.flags = SSHConnectionState::SshStateFinished;
+                        if ohdr.flags >= SSHConnectionState::SshStateFinished {
+                            unsafe {
+                                AppLayerParserStateSetFlag(
+                                    pstate,
+                                    APP_LAYER_PARSER_NO_INSPECTION
+                                        | APP_LAYER_PARSER_NO_REASSEMBLY
+                                        | APP_LAYER_PARSER_BYPASS_READY,
+                                );
+                            }
+                        }
+                    }
+                    //header and complete data (not returned)
+                }
+                Err(nom::Err::Incomplete(_)) => {
+                    match parser::ssh_parse_record_header(input) {
+                        Ok((rem, head)) => {
+                            SCLogDebug!("SSH valid record header {}", head);
+                            let remlen = rem.len() as u32;
+                            hdr.record_left = head.pkt_len - 2 - remlen;
+                            //header with rem as incomplete data
+                            if head.msg_code == SSH_MSG_NEWKEYS {
+                                hdr.flags = SSHConnectionState::SshStateFinished;
+                            }
+                            return AppLayerResult::ok();
+                        }
+                        Err(nom::Err::Incomplete(_)) => {
+                            //we may have consumed data from previous records
+                            if input.len() < SSH_RECORD_HEADER_LEN {
+                                //do not trust nom incomplete value
+                                return AppLayerResult::incomplete(
+                                    (il - input.len()) as u32,
+                                    SSH_RECORD_HEADER_LEN as u32,
+                                );
+                            } else {
+                                panic!("SSH invalid length record header");
+                            }
+                        }
+                        Err(e) => {
+                            SCLogDebug!("SSH invalid record header {}", e);
+                            self.set_event(SSHEvent::InvalidRecord);
+                            return AppLayerResult::err();
+                        }
+                    }
+                }
+                Err(e) => {
+                    SCLogDebug!("SSH invalid record {}", e);
+                    self.set_event(SSHEvent::InvalidRecord);
+                    return AppLayerResult::err();
+                }
+            }
+        }
+        return AppLayerResult::ok();
+    }
+
+    fn parse_banner(
+        &mut self, input: &[u8], resp: bool, pstate: *mut std::os::raw::c_void,
+    ) -> AppLayerResult {
+        let mut hdr = if !resp {
+            &mut self.transaction.cli_hdr
+        } else {
+            &mut self.transaction.srv_hdr
+        };
+        if hdr.flags == SSHConnectionState::SshStateBannerWaitEol {
+            match parser::ssh_parse_line(input) {
+                Ok((rem, _)) => {
+                    return self.parse_record(rem, resp, pstate);
+                }
+                Err(nom::Err::Incomplete(_)) => {
+                    return AppLayerResult::incomplete(0 as u32, (input.len() + 1) as u32);
+                }
+                Err(e) => {
+                    SCLogDebug!("SSH invalid banner {}", e);
+                    self.set_event(SSHEvent::InvalidBanner);
+                    return AppLayerResult::err();
+                }
+            }
+        }
+        match parser::ssh_parse_line(input) {
+            Ok((rem, line)) => {
+                if let Ok((_, banner)) = parser::ssh_parse_banner(line) {
+                    hdr.protover.extend(banner.protover);
+                    if banner.swver.len() > 0 {
+                        hdr.swver.extend(banner.swver);
+                    }
+                    hdr.flags = SSHConnectionState::SshStateBannerDone;
+                } else {
+                    SCLogDebug!("SSH invalid banner");
+                    self.set_event(SSHEvent::InvalidBanner);
+                    return AppLayerResult::err();
+                }
+                if line.len() >= SSH_MAX_BANNER_LEN {
+                    SCLogDebug!(
+                        "SSH banner too long {} vs {}",
+                        line.len(),
+                        SSH_MAX_BANNER_LEN
+                    );
+                    self.set_event(SSHEvent::LongBanner);
+                }
+                return self.parse_record(rem, resp, pstate);
+            }
+            Err(nom::Err::Incomplete(_)) => {
+                if input.len() < SSH_MAX_BANNER_LEN {
+                    //0 consumed, needs at least one more byte
+                    return AppLayerResult::incomplete(0 as u32, (input.len() + 1) as u32);
+                } else {
+                    SCLogDebug!(
+                        "SSH banner too long {} vs {} and waiting for eol",
+                        input.len(),
+                        SSH_MAX_BANNER_LEN
+                    );
+                    if let Ok((_, banner)) = parser::ssh_parse_banner(input) {
+                        hdr.protover.extend(banner.protover);
+                        if banner.swver.len() > 0 {
+                            hdr.swver.extend(banner.swver);
+                        }
+                        hdr.flags = SSHConnectionState::SshStateBannerWaitEol;
+                        self.set_event(SSHEvent::LongBanner);
+                        return AppLayerResult::ok();
+                    } else {
+                        self.set_event(SSHEvent::InvalidBanner);
+                        return AppLayerResult::err();
+                    }
+                }
+            }
+            Err(e) => {
+                SCLogDebug!("SSH invalid banner {}", e);
+                self.set_event(SSHEvent::InvalidBanner);
+                return AppLayerResult::err();
+            }
+        }
+    }
+}
+
+// C exports.
+
+export_tx_get_detect_state!(rs_ssh_tx_get_detect_state, SSHTransaction);
+export_tx_set_detect_state!(rs_ssh_tx_set_detect_state, SSHTransaction);
+
+export_tx_detect_flags_set!(rs_ssh_set_tx_detect_flags, SSHTransaction);
+export_tx_detect_flags_get!(rs_ssh_get_tx_detect_flags, SSHTransaction);
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_state_get_events(
+    tx: *mut std::os::raw::c_void,
+) -> *mut core::AppLayerDecoderEvents {
+    let tx = cast_pointer!(tx, SSHTransaction);
+    return tx.events;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_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 {
+                "invalid_banner" => SSHEvent::InvalidBanner as i32,
+                "long_banner" => SSHEvent::LongBanner as i32,
+                "invalid_record" => SSHEvent::InvalidRecord 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_ssh_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) = SSHEvent::from_i32(event_id as i32) {
+        let estr = match e {
+            SSHEvent::InvalidBanner => "invalid_banner\0",
+            SSHEvent::LongBanner => "long_banner\0",
+            SSHEvent::InvalidRecord => "invalid_record\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
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_state_new() -> *mut std::os::raw::c_void {
+    let state = SSHState::new();
+    let boxed = Box::new(state);
+    return unsafe { transmute(boxed) };
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_state_free(state: *mut std::os::raw::c_void) {
+    // Just unbox...
+    let _drop: Box<SSHState> = unsafe { transmute(state) };
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_state_tx_free(_state: *mut std::os::raw::c_void, _tx_id: u64) {
+    //do nothing
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_parse_request(
+    _flow: *const 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,
+) -> AppLayerResult {
+    let state = &mut cast_pointer!(state, SSHState);
+    let buf = build_slice!(input, input_len as usize);
+    let hdr = &mut state.transaction.cli_hdr;
+    if hdr.flags < SSHConnectionState::SshStateBannerDone {
+        return state.parse_banner(buf, false, pstate);
+    } else {
+        return state.parse_record(buf, false, pstate);
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_parse_response(
+    _flow: *const 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,
+) -> AppLayerResult {
+    let state = &mut cast_pointer!(state, SSHState);
+    let buf = build_slice!(input, input_len as usize);
+    let hdr = &mut state.transaction.srv_hdr;
+    if hdr.flags < SSHConnectionState::SshStateBannerDone {
+        return state.parse_banner(buf, true, pstate);
+    } else {
+        return state.parse_record(buf, true, pstate);
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_state_get_tx(
+    state: *mut std::os::raw::c_void, _tx_id: u64,
+) -> *mut std::os::raw::c_void {
+    let state = cast_pointer!(state, SSHState);
+    return unsafe { transmute(&state.transaction) };
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_state_get_tx_count(_state: *mut std::os::raw::c_void) -> u64 {
+    return 1;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_state_progress_completion_status(_direction: u8) -> std::os::raw::c_int {
+    return SSHConnectionState::SshStateFinished as i32;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_tx_get_flags(
+    tx: *mut std::os::raw::c_void, direction: u8,
+) -> SSHConnectionState {
+    let tx = cast_pointer!(tx, SSHTransaction);
+    if direction == STREAM_TOSERVER {
+        return tx.cli_hdr.flags;
+    } else {
+        return tx.srv_hdr.flags;
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_tx_get_alstate_progress(
+    tx: *mut std::os::raw::c_void, direction: u8,
+) -> std::os::raw::c_int {
+    let tx = cast_pointer!(tx, SSHTransaction);
+
+    if tx.cli_hdr.flags >= SSHConnectionState::SshStateFinished
+        && tx.srv_hdr.flags >= SSHConnectionState::SshStateFinished
+    {
+        return SSHConnectionState::SshStateFinished as i32;
+    }
+
+    if direction == STREAM_TOSERVER {
+        if tx.cli_hdr.flags >= SSHConnectionState::SshStateBannerDone {
+            return SSHConnectionState::SshStateBannerDone as i32;
+        }
+    } else {
+        if tx.srv_hdr.flags >= SSHConnectionState::SshStateBannerDone {
+            return SSHConnectionState::SshStateBannerDone as i32;
+        }
+    }
+
+    return SSHConnectionState::SshStateInProgress as i32;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_tx_get_logged(
+    _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void,
+) -> u32 {
+    let tx = cast_pointer!(tx, SSHTransaction);
+    return tx.logged.get();
+}
+
+#[no_mangle]
+pub extern "C" fn rs_ssh_tx_set_logged(
+    _state: *mut std::os::raw::c_void, tx: *mut std::os::raw::c_void, logged: u32,
+) {
+    let tx = cast_pointer!(tx, SSHTransaction);
+    tx.logged.set(logged);
+}
+
+// Parser name as a C style string.
+const PARSER_NAME: &'static [u8] = b"ssh\0";
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_ssh_register_parser() {
+    let default_port = CString::new("[22]").unwrap();
+    let parser = RustParser {
+        name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
+        default_port: default_port.as_ptr(),
+        ipproto: IPPROTO_TCP,
+        //simple patterns, no probing
+        probe_ts: None,
+        probe_tc: None,
+        min_depth: 0,
+        max_depth: 0,
+        state_new: rs_ssh_state_new,
+        state_free: rs_ssh_state_free,
+        tx_free: rs_ssh_state_tx_free,
+        parse_ts: rs_ssh_parse_request,
+        parse_tc: rs_ssh_parse_response,
+        get_tx_count: rs_ssh_state_get_tx_count,
+        get_tx: rs_ssh_state_get_tx,
+        tx_get_comp_st: rs_ssh_state_progress_completion_status,
+        tx_get_progress: rs_ssh_tx_get_alstate_progress,
+        get_tx_logged: Some(rs_ssh_tx_get_logged),
+        set_tx_logged: Some(rs_ssh_tx_set_logged),
+        get_de_state: rs_ssh_tx_get_detect_state,
+        set_de_state: rs_ssh_tx_set_detect_state,
+        get_events: Some(rs_ssh_state_get_events),
+        get_eventinfo: Some(rs_ssh_state_get_event_info),
+        get_eventinfo_byid: Some(rs_ssh_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,
+        get_tx_detect_flags: Some(rs_ssh_get_tx_detect_flags),
+        set_tx_detect_flags: Some(rs_ssh_set_tx_detect_flags),
+    };
+
+    let ip_proto_str = CString::new("tcp").unwrap();
+
+    if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
+        let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
+        ALPROTO_SSH = alproto;
+        if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
+            let _ = AppLayerRegisterParser(&parser, alproto);
+        }
+        SCLogDebug!("Rust ssh parser registered.");
+    } else {
+        SCLogNotice!("Protocol detector and parser disabled for SSH.");
+    }
+}
index 81f8bf1d9acccc244a22f3d8fdd2a67b763e5ce7..33c853c6bd1e3e25c572c926d26659e030d75ec7 100644 (file)
@@ -724,17 +724,17 @@ static void PacketToDataProtoTLS(const Packet *p, const PacketAlert *pa, idmef_a
 static void PacketToDataProtoSSH(const Packet *p, const PacketAlert *pa, idmef_alert_t *alert)
 {
     json_t *js, *s_js;
-    SshState *ssh_state = (SshState *)FlowGetAppState(p->flow);
+    void *ssh_state = FlowGetAppState(p->flow);
 
     if (ssh_state == NULL)
         return;
 
-    js = json_object();
-    if (js == NULL)
+    void *tx_ptr = rs_ssh_state_get_tx(ssh_state, 0);
+    BUG_ON(tx_ptr == NULL);
+    js = rs_ssh_log_json(tx_ptr);
+    if (unlikely(js == NULL))
         return;
 
-    JsonSshLogJSON(js, ssh_state);
-
     s_js = json_object_get(js, "server");
     if (s_js != NULL) {
         JsonToAdditionalData(NULL, s_js, alert);
index 910803f4abf37177e4c5ae54e71bb19816ea74af..44b40048707d78adec1b9910a117459cc50ebf88 100644 (file)
@@ -41,6 +41,7 @@
 #include "app-layer-protos.h"
 #include "app-layer-parser.h"
 #include "app-layer-ssh.h"
+#include "rust.h"
 
 #include "conf.h"
 
 #include "util-byte.h"
 #include "util-memcmp.h"
 
-/** \internal
- *  \brief Function to parse the SSH version string of the client
- *
- *  The input to this function is a byte buffer starting with SSH-
- *
- *  \param  ssh_state   Pointer the state in which the value to be stored
- *  \param  input       Pointer the received input data
- *  \param  input_len   Length in bytes of the received data
- *
- *  \retval len remaining length in input
- */
-static int SSHParseBanner(SshState *state, SshHeader *header, const uint8_t *input, uint32_t input_len)
-{
-    const uint8_t *line_ptr = input;
-    uint32_t line_len = input_len;
-
-    /* is it the version line? */
-    if (line_len < 4) {
-        SCReturnInt(-1);
-    }
-    if (SCMemcmp("SSH-", line_ptr, 4) != 0) {
-        SCReturnInt(-1);
-    }
-
-    const uint8_t *banner_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\r", 1);
-    if (banner_end == NULL) {
-        banner_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\n", 1);
-        if (banner_end == NULL) {
-            SCLogDebug("No EOL at the end of banner buffer");
-            SCReturnInt(-1);
-        }
-    }
-
-    if ((banner_end - line_ptr) > 255) {
-        SCLogDebug("Invalid version string, it should be less than 255 "
-                "characters including <CR><NL>, input value is %"PRIuMAX,
-                (uintmax_t)(banner_end - line_ptr));
-        SCReturnInt(-1);
-    }
-
-    /* don't search things behind the end of banner */
-    line_len = banner_end - line_ptr;
-
-    /* ok, we have found the version line/string, skip it and parse proto version */
-    line_ptr += 4;
-    line_len -= 4;
-
-    uint8_t *proto_end = BasicSearch(line_ptr, line_len, (uint8_t*)"-", 1);
-    if (proto_end == NULL) {
-        /* Strings starting with SSH- are not allowed
-         * if they are not the real version string */
-        SCLogDebug("Info Version String for SSH (invalid usage of SSH- prefix)");
-        SCReturnInt(-1);
-    }
-    uint64_t proto_ver_len = (uint64_t)(proto_end - line_ptr);
-    header->proto_version = SCMalloc(proto_ver_len + 1);
-    if (header->proto_version == NULL) {
-        SCReturnInt(-1);
-    }
-    memcpy(header->proto_version, line_ptr, proto_ver_len);
-    header->proto_version[proto_ver_len] = '\0';
-
-    /* Now lets parse the software & version */
-    line_ptr += proto_ver_len + 1;
-    line_len -= proto_ver_len + 1;
-    if (line_len < 1) {
-        SCLogDebug("No software version specified (weird)");
-        header->flags |= SSH_FLAG_VERSION_PARSED;
-        /* Return the remaining length */
-        SCReturnInt(0);
-    }
-
-    uint64_t sw_ver_len = (uint64_t)(banner_end - line_ptr);
-    /* sanity check on this arithmetic */
-    if ((sw_ver_len <= 1) || (sw_ver_len >= input_len)) {
-        SCLogDebug("Should not have sw version length '%" PRIu64 "'", sw_ver_len);
-        header->flags |= SSH_FLAG_VERSION_PARSED;
-        SCReturnInt(-1);
-    }
-
-    header->software_version = SCMalloc(sw_ver_len + 1);
-    if (header->software_version == NULL) {
-        header->flags |= SSH_FLAG_VERSION_PARSED;
-        SCReturnInt(-1);
-    }
-    memcpy(header->software_version, line_ptr, sw_ver_len);
-    header->software_version[sw_ver_len] = '\0';
-    if (header->software_version[sw_ver_len - 1] == 0x0d)
-        header->software_version[sw_ver_len - 1] = '\0';
-
-    header->flags |= SSH_FLAG_VERSION_PARSED;
-
-    /* Return the remaining length */
-    int len = input_len - (banner_end - input);
-    SCReturnInt(len);
-}
-
-static int SSHParseRecordHeader(SshState *state, SshHeader *header,
-        const uint8_t *input, uint32_t input_len)
-{
-#ifdef DEBUG
-    BUG_ON(input_len != 6);
-#else
-    if (input_len < 6)
-        SCReturnInt(-1);
-#endif
-    /* input and input_len now point past initial line */
-    uint32_t pkt_len = 0;
-    int r = ByteExtractUint32(&pkt_len, BYTE_BIG_ENDIAN,
-            4, input);
-    if (r != 4) {
-        SCLogDebug("xtract 4 bytes failed %d", r);
-        SCReturnInt(-1);
-    }
-    if (pkt_len < 2) {
-        SCReturnInt(-1);
-    }
-
-    header->pkt_len = pkt_len;
-    SCLogDebug("pkt len: %"PRIu32, pkt_len);
-
-    input += 4;
-    //input_len -= 4;
-
-    header->padding_len = *input;
-
-    input += 1;
-    //input_len -= 1;
-
-    SCLogDebug("padding: %u", header->padding_len);
-
-    header->msg_code = *input;
-
-    SCLogDebug("msg code: %u", header->msg_code);
-
-    if (header->msg_code == SSH_MSG_NEWKEYS) {
-        /* done */
-        SCLogDebug("done");
-        header->flags |= SSH_FLAG_PARSER_DONE;
-    } else {
-        /* not yet done */
-        SCLogDebug("not done");
-    }
-    SCReturnInt(0);
-}
-
-/** \internal
- *  \brief Function to parse the SSH field in packet received from the client
- *
- *  Input to this function is a byte buffer starting with SSH- up to at least
- *  a \r or \n character.
- *
- *  \param  ssh_state   Pointer the state in which the value to be stored
- *  \param  input       Pointer the received input data
- *  \param  input_len   Length in bytes of the received data
- */
-static int SSHParseRecord(SshState *state, SshHeader *header, const uint8_t *input, uint32_t input_len)
-{
-    SCEnter();
-    int ret = 0;
-
-    if (header->flags & SSH_FLAG_PARSER_DONE) {
-        SCReturnInt(0);
-    }
-
-    SCLogDebug("state %p, input %p,input_len %" PRIu32,
-               state, input, input_len);
-    //PrintRawDataFp(stdout, input, input_len);
-
-    if (!(header->flags & SSH_FLAG_VERSION_PARSED)) {
-        ret = SSHParseBanner(state, header, input, input_len);
-        if (ret < 0) {
-            SCLogDebug("Invalid version string");
-            SCReturnInt(-1);
-        } else if (header->flags & SSH_FLAG_VERSION_PARSED) {
-            SCLogDebug("Version string parsed, remaining length %d", ret);
-            input += input_len - ret;
-            input_len -= (input_len - ret);
-
-            uint32_t u = 0;
-            while (u < input_len && (input[u] == '\r' || input[u] == '\n')) {
-                u++;
-            }
-            SCLogDebug("skipping %u EOL bytes", u);
-            input += u;
-            input_len -= u;
-
-            if (input_len == 0)
-                SCReturnInt(0);
-
-        } else {
-            BUG_ON(1);// we only call this when we have enough data
-            SCLogDebug("Version string not parsed yet");
-            //pstate->parse_field = 0;
-            SCReturnInt(0);
-        }
-    } else {
-        SCLogDebug("Version string already parsed");
-    }
-
-    /* skip bytes from the current record if we have to */
-    if (header->record_left > 0) {
-        SCLogDebug("skipping bytes part of the current record");
-        if (header->record_left > input_len) {
-            header->record_left -= input_len;
-            SCLogDebug("all input skipped, %u left in record", header->record_left);
-            SCReturnInt(0);
-        } else {
-            input_len -= header->record_left;
-            input += header->record_left;
-            header->record_left = 0;
-
-            if (input_len == 0) {
-                SCLogDebug("all input skipped");
-                SCReturnInt(0);
-            }
-        }
-    }
-
-again:
-    /* input is too small, even when combined with stored bytes */
-    if (header->buf_offset + input_len < 6) {
-        memcpy(header->buf + header->buf_offset, input, input_len);
-        header->buf_offset += input_len;
-        SCReturnInt(0);
-
-    /* we have enough bytes to parse 6 bytes, lets see if we have
-     * previously stored some */
-    } else if (header->buf_offset > 0) {
-        uint8_t needed = 6 - header->buf_offset;
-
-        SCLogDebug("parse stored");
-        memcpy(header->buf + header->buf_offset, input, needed);
-        header->buf_offset = 6;
-
-        // parse the 6
-        if (SSHParseRecordHeader(state, header, header->buf, 6) < 0)
-            SCReturnInt(-1);
-        header->buf_offset = 0;
-
-        uint32_t record_left = header->pkt_len - 2;
-        input_len -= needed;
-        input += needed;
-
-        if (record_left > input_len) {
-            header->record_left = record_left - input_len;
-        } else {
-            input_len -= record_left;
-            if (input_len == 0)
-                SCReturnInt(0);
-
-            input += record_left;
-
-            SCLogDebug("we have %u left to parse", input_len);
-            goto again;
-
-        }
-
-    /* nothing stored, lets parse this directly */
-    } else {
-        SCLogDebug("parse direct");
-        //PrintRawDataFp(stdout, input, input_len);
-        if (SSHParseRecordHeader(state, header, input, 6) < 0)
-            SCReturnInt(-1);
-
-        uint32_t record_left = header->pkt_len - 2;
-        SCLogDebug("record left %u", record_left);
-        input_len -= 6;
-        input += 6;
-
-        if (record_left > input_len) {
-            header->record_left = record_left - input_len;
-        } else {
-            input_len -= record_left;
-            if (input_len == 0)
-                SCReturnInt(0);
-            input += record_left;
-            //PrintRawDataFp(stdout, input, input_len);
-
-            SCLogDebug("we have %u left to parse", input_len);
-            goto again;
-        }
-    }
-
-    SCReturnInt(0);
-}
-
-static int EnoughData(const uint8_t *input, uint32_t input_len)
-{
-    uint32_t u;
-    for (u = 0; u < input_len; u++) {
-        if (input[u] == '\r' || input[u] == '\n')
-            return TRUE;
-    }
-    return FALSE;
-}
-
-#define MAX_BANNER_LEN 256
-
-static int SSHParseData(SshState *state, SshHeader *header,
-                        const uint8_t *input, uint32_t input_len)
-{
-    /* we're looking for the banner */
-    if (!(header->flags & SSH_FLAG_VERSION_PARSED))
-    {
-        int banner_eol = EnoughData(input, input_len);
-
-        /* fast track normal case: no buffering */
-        if (header->banner_buffer == NULL && banner_eol)
-        {
-            SCLogDebug("enough data, parse now");
-            // parse now
-            int r = SSHParseRecord(state, header, input, input_len);
-            SCReturnInt(r);
-
-        /* banner EOL with existing buffer present. Time for magic. */
-        } else if (banner_eol) {
-            SCLogDebug("banner EOL with existing buffer");
-
-            uint32_t tocopy = MAX_BANNER_LEN - header->banner_len;
-            if (tocopy > input_len)
-                tocopy = input_len;
-
-            SCLogDebug("tocopy %u input_len %u", tocopy, input_len);
-            memcpy(header->banner_buffer + header->banner_len, input, tocopy);
-            header->banner_len += tocopy;
-
-            SCLogDebug("header->banner_len %u", header->banner_len);
-            int r = SSHParseRecord(state, header,
-                    header->banner_buffer, header->banner_len);
-            if (r == 0) {
-                input += tocopy;
-                input_len -= tocopy;
-                if (input_len > 0) {
-                    SCLogDebug("handling remaining data %u", input_len);
-                    r = SSHParseRecord(state, header, input, input_len);
-                }
-            }
-            SCReturnInt(r);
-
-        /* no banner EOL, so we need to buffer */
-        } else if (!banner_eol) {
-            if (header->banner_buffer == NULL) {
-                header->banner_buffer = SCMalloc(MAX_BANNER_LEN);
-                if (header->banner_buffer == NULL)
-                    SCReturnInt(-1);
-            }
-
-            uint32_t tocopy = MAX_BANNER_LEN - header->banner_len;
-            if (tocopy > input_len)
-                tocopy = input_len;
-            SCLogDebug("tocopy %u", tocopy);
-
-            memcpy(header->banner_buffer + header->banner_len, input, tocopy);
-            header->banner_len += tocopy;
-            SCLogDebug("header->banner_len %u", header->banner_len);
-        }
-
-    /* we have a banner, the rest is just records */
-    } else {
-        int r = SSHParseRecord(state, header, input, input_len);
-        SCReturnInt(r);
-    }
-
-    //PrintRawDataFp(stdout, input, input_len);
-    return 0;
-}
-
-static AppLayerResult SSHParseRequest(Flow *f, void *state, AppLayerParserState *pstate,
-                           const uint8_t *input, uint32_t input_len,
-                           void *local_data, const uint8_t flags)
-{
-    SshState *ssh_state = (SshState *)state;
-    SshHeader *ssh_header = &ssh_state->cli_hdr;
-
-    if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
-        SCReturnStruct(APP_LAYER_OK);
-    } else if (input == NULL || input_len == 0) {
-        SCReturnStruct(APP_LAYER_ERROR);
-    }
-
-    int r = SSHParseData(ssh_state, ssh_header, input, input_len);
-
-    if (ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE &&
-        ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE) {
-        AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_INSPECTION);
-        AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_REASSEMBLY);
-        AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_BYPASS_READY);
-    }
-
-    if (r < 0) {
-        SCReturnStruct(APP_LAYER_ERROR);
-    }
-    SCReturnStruct(APP_LAYER_OK);
-}
-
-static AppLayerResult SSHParseResponse(Flow *f, void *state, AppLayerParserState *pstate,
-                            const uint8_t *input, uint32_t input_len,
-                            void *local_data, const uint8_t flags)
-{
-    SshState *ssh_state = (SshState *)state;
-    SshHeader *ssh_header = &ssh_state->srv_hdr;
-
-    if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
-        SCReturnStruct(APP_LAYER_OK);
-    } else if (input == NULL || input_len == 0) {
-        SCReturnStruct(APP_LAYER_ERROR);
-    }
-
-    int r = SSHParseData(ssh_state, ssh_header, input, input_len);
-
-    if (ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE &&
-        ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE) {
-        AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_INSPECTION);
-        AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_REASSEMBLY);
-        AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_BYPASS_READY);
-    }
-
-    if (r < 0) {
-        SCReturnStruct(APP_LAYER_ERROR);
-    }
-    SCReturnStruct(APP_LAYER_OK);
-}
-
-/** \brief Function to allocates the SSH state memory
- */
-static void *SSHStateAlloc(void)
-{
-    void *s = SCMalloc(sizeof(SshState));
-    if (unlikely(s == NULL))
-        return NULL;
-
-    memset(s, 0, sizeof(SshState));
-    return s;
-}
-
-/** \brief Function to free the SSH state memory
- */
-static void SSHStateFree(void *state)
-{
-    SshState *s = (SshState *)state;
-    if (s->cli_hdr.proto_version != NULL)
-        SCFree(s->cli_hdr.proto_version);
-    if (s->cli_hdr.software_version != NULL)
-        SCFree(s->cli_hdr.software_version);
-    if (s->cli_hdr.banner_buffer != NULL)
-        SCFree(s->cli_hdr.banner_buffer);
-
-    if (s->srv_hdr.proto_version != NULL)
-        SCFree(s->srv_hdr.proto_version);
-    if (s->srv_hdr.software_version != NULL)
-        SCFree(s->srv_hdr.software_version);
-    if (s->srv_hdr.banner_buffer != NULL)
-        SCFree(s->srv_hdr.banner_buffer);
-
-    //AppLayerDecoderEventsFreeEvents(&s->decoder_events);
-
-    if (s->de_state != NULL) {
-        DetectEngineStateFree(s->de_state);
-    }
-
-    SCFree(s);
-}
-
-static int SSHSetTxDetectState(void *vtx, DetectEngineState *de_state)
-{
-    SshState *ssh_state = (SshState *)vtx;
-    ssh_state->de_state = de_state;
-    return 0;
-}
-
-static DetectEngineState *SSHGetTxDetectState(void *vtx)
-{
-    SshState *ssh_state = (SshState *)vtx;
-    return ssh_state->de_state;
-}
-
-static void SSHStateTransactionFree(void *state, uint64_t tx_id)
-{
-    /* do nothing */
-}
-
-static void *SSHGetTx(void *state, uint64_t tx_id)
-{
-    SshState *ssh_state = (SshState *)state;
-    return ssh_state;
-}
-
-static uint64_t SSHGetTxCnt(void *state)
-{
-    /* single tx */
-    return 1;
-}
-
-static void SSHSetTxLogged(void *state, void *tx, LoggerId logged)
-{
-    SshState *ssh_state = (SshState *)state;
-    if (ssh_state)
-        ssh_state->logged = logged;
-}
-
-static LoggerId SSHGetTxLogged(void *state, void *tx)
-{
-    SshState *ssh_state = (SshState *)state;
-    if (ssh_state) {
-        return ssh_state->logged;
-    }
-    return 0;
-}
-
-static uint64_t SSHGetTxDetectFlags(void *vtx, uint8_t dir)
-{
-    SshState *ssh_state = (SshState *)vtx;
-    if (dir & STREAM_TOSERVER) {
-        return ssh_state->detect_flags_ts;
-    } else {
-        return ssh_state->detect_flags_tc;
-    }
-}
-
-static void SSHSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t flags)
-{
-    SshState *ssh_state = (SshState *)vtx;
-    if (dir & STREAM_TOSERVER) {
-        ssh_state->detect_flags_ts = flags;
-    } else {
-        ssh_state->detect_flags_tc = flags;
-    }
-}
-
-static int SSHGetAlstateProgressCompletionStatus(uint8_t direction)
-{
-    return SSH_STATE_FINISHED;
-}
-
-static int SSHGetAlstateProgress(void *tx, uint8_t direction)
-{
-    SshState *ssh_state = (SshState *)tx;
-
-    if (ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE &&
-        ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE) {
-        return SSH_STATE_FINISHED;
-    }
-
-    if (direction == STREAM_TOSERVER) {
-        if (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED) {
-            return SSH_STATE_BANNER_DONE;
-        }
-    } else {
-        if (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED) {
-            return SSH_STATE_BANNER_DONE;
-        }
-    }
-
-    return SSH_STATE_IN_PROGRESS;
-}
 
 static int SSHRegisterPatternsForProtocolDetection(void)
 {
@@ -636,36 +81,9 @@ void RegisterSSHParsers(void)
             return;
     }
 
-    if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
-        AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SSH, STREAM_TOSERVER,
-                                     SSHParseRequest);
-        AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_SSH, STREAM_TOCLIENT,
-                                     SSHParseResponse);
-        AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_SSH, SSHStateAlloc, SSHStateFree);
-        AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP,
-                ALPROTO_SSH, STREAM_TOSERVER|STREAM_TOCLIENT);
-
-        AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_SSH, SSHStateTransactionFree);
-
-        AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_SSH,
-                                               SSHGetTxDetectState, SSHSetTxDetectState);
-
-        AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_SSH, SSHGetTx);
+    SCLogDebug("Registering Rust SSH parser.");
+    rs_ssh_register_parser();
 
-        AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_SSH, SSHGetTxCnt);
-
-        AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_SSH, SSHGetAlstateProgress);
-
-        AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_SSH, SSHGetTxLogged, SSHSetTxLogged);
-        AppLayerParserRegisterDetectFlagsFuncs(IPPROTO_TCP, ALPROTO_SSH,
-                SSHGetTxDetectFlags, SSHSetTxDetectFlags);
-
-        AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_SSH,
-                                                               SSHGetAlstateProgressCompletionStatus);
-    } else {
-//        SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
-//                  "still on.", proto_name);
-    }
 
 #ifdef UNITTESTS
     AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_SSH, SSHParserRegisterTests);
@@ -675,6 +93,49 @@ void RegisterSSHParsers(void)
 /* UNITTESTS */
 #ifdef UNITTESTS
 #include "flow-util.h"
+#include "stream-tcp-util.h"
+#include "util-unittest-helper.h"
+
+static int SSHParserTestUtilCheck(const char *protoexp, const char *softexp, void *tx, uint8_t flags) {
+    const uint8_t *protocol = NULL;
+    uint32_t p_len = 0;
+    const uint8_t *software = NULL;
+    uint32_t s_len = 0;
+
+    if (rs_ssh_tx_get_protocol(tx, &protocol, &p_len, flags) != 1) {
+        printf("Version string not parsed correctly return: ");
+        return 1;
+    }
+    if (protocol == NULL) {
+        printf("Version string not parsed correctly NULL: ");
+        return 1;
+    }
+
+    if (p_len != strlen(protoexp)) {
+        printf("Version string not parsed correctly length: ");
+        return 1;
+    }
+    if (memcmp(protocol, protoexp, strlen(protoexp)) != 0) {
+        printf("Version string not parsed correctly: ");
+        return 1;
+    }
+
+    if (softexp != NULL) {
+        if (rs_ssh_tx_get_software(tx, &software, &s_len, flags) != 1)
+            return 1;
+        if (software == NULL)
+            return 1;
+        if (s_len != strlen(softexp)) {
+            printf("Software string not parsed correctly length: ");
+            return 1;
+        }
+        if (memcmp(software, softexp, strlen(softexp)) != 0) {
+            printf("Software string not parsed correctly: ");
+            return 1;
+        }
+    }
+    return 0;
+}
 
 /** \test Send a version string in one chunk (client version str). */
 static int SSHParserTest01(void)
@@ -701,36 +162,20 @@ static int SSHParserTest01(void)
         goto end;
     }
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f.alstate;
     if (ssh_state == NULL) {
         printf("no ssh state: ");
         goto end;
     }
 
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->cli_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->cli_hdr.proto_version == NULL) {
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOSERVER) != SshStateBannerDone ) {
         printf("Client version string not parsed: ");
         goto end;
     }
 
-    if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
+    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
         goto end;
-    }
 
     result = 1;
 end:
@@ -768,36 +213,19 @@ static int SSHParserTest02(void)
         goto end;
     }
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f.alstate;
     if (ssh_state == NULL) {
         printf("no ssh state: ");
         goto end;
     }
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
 
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->cli_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->cli_hdr.proto_version == NULL) {
+    if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOSERVER) != SshStateBannerDone ) {
         printf("Client version string not parsed: ");
         goto end;
     }
-
-    if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
+    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
         goto end;
-    }
 
     result = 1;
 end:
@@ -835,24 +263,23 @@ static int SSHParserTest03(void)
         goto end;
     }
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f.alstate;
     if (ssh_state == NULL) {
         printf("no ssh state: ");
         goto end;
     }
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
 
-    if (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED) {
+    if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOSERVER) == SshStateBannerDone ) {
         printf("Client version string parsed? It's not a valid string: ");
         goto end;
     }
-
-    if (ssh_state->cli_hdr.proto_version != NULL) {
+    const uint8_t *dummy = NULL;
+    uint32_t dummy_len = 0;
+    if (rs_ssh_tx_get_protocol(tx, &dummy, &dummy_len, STREAM_TOSERVER) != 0)
         goto end;
-    }
-
-    if (ssh_state->cli_hdr.software_version != NULL) {
+    if (rs_ssh_tx_get_software(tx, &dummy, &dummy_len, STREAM_TOSERVER) != 0)
         goto end;
-    }
 
     result = 1;
 end:
@@ -888,36 +315,19 @@ static int SSHParserTest04(void)
         goto end;
     }
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f.alstate;
     if (ssh_state == NULL) {
         printf("no ssh state: ");
         goto end;
     }
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
 
-    if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->srv_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->srv_hdr.proto_version == NULL) {
+    if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOCLIENT) != SshStateBannerDone ) {
         printf("Client version string not parsed: ");
         goto end;
     }
-
-    if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
+    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT))
         goto end;
-    }
 
     result = 1;
 
@@ -955,36 +365,19 @@ static int SSHParserTest05(void)
         goto end;
     }
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f.alstate;
     if (ssh_state == NULL) {
         printf("no ssh state: ");
         goto end;
     }
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
 
-    if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->srv_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->srv_hdr.proto_version == NULL) {
+    if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOCLIENT) != SshStateBannerDone ) {
         printf("Client version string not parsed: ");
         goto end;
     }
-
-    if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
+    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT))
         goto end;
-    }
 
     result = 1;
 end:
@@ -1022,24 +415,24 @@ static int SSHParserTest06(void)
     }
     /* Ok, it returned an error. Let's make sure we didn't parse the string at all */
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f.alstate;
     if (ssh_state == NULL) {
         printf("no ssh state: ");
         goto end;
     }
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
 
-    if (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED) {
+    if ( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOCLIENT) == SshStateBannerDone ) {
         printf("Client version string parsed? It's not a valid string: ");
         goto end;
     }
-
-    if (ssh_state->srv_hdr.proto_version != NULL) {
+    const uint8_t *dummy = NULL;
+    uint32_t dummy_len = 0;
+    if (rs_ssh_tx_get_protocol(tx, &dummy, &dummy_len, STREAM_TOCLIENT) != 0)
         goto end;
-    }
-
-    if (ssh_state->srv_hdr.software_version != NULL) {
+    if (rs_ssh_tx_get_software(tx, &dummy, &dummy_len, STREAM_TOCLIENT) != 0)
         goto end;
-    }
+
 
     result = 1;
 end:
@@ -1050,312 +443,208 @@ end:
     return result;
 }
 
+#define MAX_SSH_TEST_SIZE 512
+
 static int SSHParserTest07(void)
 {
-    int result = 0;
-    Flow f;
-    uint8_t sshbuf1[] = "SSH-2.";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
-    uint8_t sshbuf2[] = { "0-MySSHClient-0.5.1\r\n"};
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
     TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+    Flow *f = NULL;
+    Packet *p = NULL;
 
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
+    char sshbufs[2][MAX_SSH_TEST_SIZE] = {"SSH-2.", "0-MySSHClient-0.5.1\r\n"};
 
-    StreamTcpInitConfig(TRUE);
+    memset(&tv, 0x00, sizeof(tv));
 
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
-
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
 
-    if (ssh_state->cli_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
-    if (ssh_state->cli_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
 
-    if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<2; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
+        seq += strlen(sshbufs[i]);
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOSERVER) != SshStateBannerDone );
 
-    result = 1;
+    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
 
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Send a version banner in three chunks. */
 static int SSHParserTest08(void)
 {
-    int result = 0;
-    Flow f;
-    uint8_t sshbuf1[] = "SSH-";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
-    uint8_t sshbuf2[] = "2.";
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
-    uint8_t sshbuf3[] = { "0-MySSHClient-0.5.1\r\n"};
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
     TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+    Flow *f = NULL;
+    Packet *p = NULL;
 
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
-
-    StreamTcpInitConfig(TRUE);
+    char sshbufs[3][MAX_SSH_TEST_SIZE] = {"SSH-", "2.", "0-MySSHClient-0.5.1\r\n"};
 
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
+    memset(&tv, 0x00, sizeof(tv));
 
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
 
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
-    if (ssh_state->cli_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
 
-    if (ssh_state->cli_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<3; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
+        seq += strlen(sshbufs[i]);
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOSERVER) != SshStateBannerDone );
 
-    if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
 
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 static int SSHParserTest09(void)
 {
-    int result = 0;
-    Flow f;
-    uint8_t sshbuf1[] = "SSH-2.";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
-    uint8_t sshbuf2[] = { "0-MySSHClient-0.5.1\r\n"};
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
     TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
-
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
+    Flow *f = NULL;
+    Packet *p = NULL;
 
-    StreamTcpInitConfig(TRUE);
-
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOCLIENT, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
+    char sshbufs[2][MAX_SSH_TEST_SIZE] = {"SSH-2.", "0-MySSHClient-0.5.1\r\n"};
 
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    memset(&tv, 0x00, sizeof(tv));
 
-    if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
 
-    if (ssh_state->srv_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
-    if (ssh_state->srv_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
 
-    if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<2; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
+        seq += strlen(sshbufs[i]);
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOCLIENT) != SshStateBannerDone );
 
-    result = 1;
+    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
 
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Send a version banner in three chunks. */
 static int SSHParserTest10(void)
 {
-    int result = 0;
-    Flow f;
-    uint8_t sshbuf1[] = "SSH-";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
-    uint8_t sshbuf2[] = "2.";
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
-    uint8_t sshbuf3[] = { "0-MySSHClient-0.5.1\r\n"};
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
     TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
-
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
+    Flow *f = NULL;
+    Packet *p = NULL;
 
-    StreamTcpInitConfig(TRUE);
+    char sshbufs[3][MAX_SSH_TEST_SIZE] = {"SSH-", "2.", "0-MySSHClient-0.5.1\r\n"};
 
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOCLIENT, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
+    memset(&tv, 0x00, sizeof(tv));
 
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
 
-    if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
-    if (ssh_state->srv_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
 
-    if (ssh_state->srv_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<3; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, (uint8_t *) sshbufs[i], strlen(sshbufs[i])) == -1);
+        seq += strlen(sshbufs[i]);
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_alstate_progress(tx, STREAM_TOCLIENT) != SshStateBannerDone );
 
-    if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
 
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Send a banner and record in three chunks. */
@@ -1391,41 +680,18 @@ static int SSHParserTest11(void)
         goto end;
     }
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f.alstate;
     if (ssh_state == NULL) {
         printf("no ssh state: ");
         goto end;
     }
-
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->cli_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->cli_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
-
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    if ( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateFinished ) {
         printf("Didn't detect the msg code of new keys (ciphered data starts): ");
         goto end;
     }
+    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
+        goto end;
 
     result = 1;
 end:
@@ -1477,41 +743,18 @@ static int SSHParserTest12(void)
         goto end;
     }
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f.alstate;
     if (ssh_state == NULL) {
         printf("no ssh state: ");
         goto end;
     }
-
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->cli_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->cli_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
-
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    if ( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateFinished ) {
         printf("Didn't detect the msg code of new keys (ciphered data starts): ");
         goto end;
     }
+    if (SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER))
+        goto end;
 
     result = 1;
 end:
@@ -1525,697 +768,435 @@ end:
 /** \test Send a banner and 2 records record in four chunks. */
 static int SSHParserTest13(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 17};
-    uint32_t sshlen2 = sizeof(sshbuf2);
     uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 21};
-    uint32_t sshlen3 = sizeof(sshbuf3);
-    TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
-    uint32_t u;
 
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
+    uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3};
+    uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3)};
 
-    StreamTcpInitConfig(TRUE);
+    memset(&tv, 0x00, sizeof(tv));
 
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    for (u = 0; u < sshlen2; u++) {
-        r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, &sshbuf2[u], 1);
-        if (r != 0) {
-            printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-            goto end;
-        }
-    }
-    for (u = 0; u < sshlen3; u++) {
-        r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, &sshbuf3[u], 1);
-        if (r != 0) {
-            printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-            goto end;
-        }
-    }
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
-
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
+
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
+
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
+
+    uint32_t seq = 2;
+    for (int i=0; i<3; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (ssh_state->cli_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->cli_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateFinished );
 
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
-        goto end;
-    }
+    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
 
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Send a banner and 2 records record in four chunks. */
 static int SSHParserTest14(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00};
-    uint32_t sshlen2 = sizeof(sshbuf2);
-
     uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
-    uint32_t sshlen3 = sizeof(sshbuf3);
     uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00};
-    uint32_t sshlen4 = sizeof(sshbuf4);
-
     /* first byte of this record in sshbuf4 */
     uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 21};
-    uint32_t sshlen5 = sizeof(sshbuf5);
-    TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
+    uint8_t* sshbufs[5] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4, sshbuf5};
+    uint32_t sshlens[5] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3), sizeof(sshbuf4), sizeof(sshbuf5)};
 
-    StreamTcpInitConfig(TRUE);
+    memset(&tv, 0x00, sizeof(tv));
 
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf5, sshlen5);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
 
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
 
-    if (ssh_state->cli_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->cli_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<5; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateFinished );
 
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
-        goto end;
-    }
+    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
 
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Send a banner and 2 records record in four chunks. */
 static int SSHParserTest15(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00};
-    uint32_t sshlen2 = sizeof(sshbuf2);
-
     uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
-    uint32_t sshlen3 = sizeof(sshbuf3);
     uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00};
-    uint32_t sshlen4 = sizeof(sshbuf4);
-
-    /* first byte of this record in sshbuf4 */
     uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 20, 0x00, 0x00, 0x00, 0x02, 0x01, 21};
-    uint32_t sshlen5 = sizeof(sshbuf5);
-    TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
+    uint8_t* sshbufs[5] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4, sshbuf5};
+    uint32_t sshlens[5] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2), sizeof(sshbuf3), sizeof(sshbuf4), sizeof(sshbuf5)};
 
-    StreamTcpInitConfig(TRUE);
+    memset(&tv, 0x00, sizeof(tv));
 
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf5, sshlen5);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
 
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
 
-    if (ssh_state->cli_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<5; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.client, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (ssh_state->cli_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateFinished );
 
-    if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOSERVER));
 
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
-        goto end;
-    }
-
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Send toserver a banner and record in three chunks. */
 static int SSHParserTest16(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
     uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00};
-    uint32_t sshlen3 = sizeof(sshbuf3);
-    TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
-
-    StreamTcpInitConfig(TRUE);
-
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOCLIENT, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
+    uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3};
+    uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3)};
 
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    memset(&tv, 0x00, sizeof(tv));
 
-    if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
 
-    if (ssh_state->srv_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
-    if (ssh_state->srv_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
 
-    if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<3; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished );
 
-    if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
-        goto end;
-    }
+    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
 
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Send toserver a banner and 2 records record in four chunks. */
 static int SSHParserTest17(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
     uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 17, 0x00};
-    uint32_t sshlen3 = sizeof(sshbuf3);
     uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
-    uint32_t sshlen4 = sizeof(sshbuf4);
-    TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
+    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
+    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3), sizeof(sshbuf4)};
 
-    StreamTcpInitConfig(TRUE);
+    memset(&tv, 0x00, sizeof(tv));
 
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOCLIENT, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
 
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
-    if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
 
-    if (ssh_state->srv_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<4; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (ssh_state->srv_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished );
 
-    if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    FAIL_IF(SSHParserTestUtilCheck("2.0", "MySSHClient-0.5.1", tx, STREAM_TOCLIENT));
 
-    if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
-        goto end;
-    }
-
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test 2 directional test */
 static int SSHParserTest18(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
 
     uint8_t server1[] = "SSH-2.0-OpenSSH_4.7p1 Debian-8ubuntu3\r\n";
-    uint32_t serverlen1 = sizeof(server1) - 1;
-
     uint8_t sshbuf1[] = "SSH-";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n";
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
-
     uint8_t server2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
-    uint32_t serverlen2 = sizeof(server2) - 1;
-
-    uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
-    uint32_t sshlen3 = sizeof(sshbuf3);
-
-
-    TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
-
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
-
-    StreamTcpInitConfig(TRUE);
-
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOCLIENT, server1, serverlen1);
-    if (r != 0) {
-        printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            server2, serverlen2);
-    if (r != 0) {
-        printf("toclient chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
-
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
-        goto end;
-    }
-
-    if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
-        goto end;
-    }
-
-    if (!(AppLayerParserStateIssetFlag(f.alparser, APP_LAYER_PARSER_NO_INSPECTION))) {
-        printf("detection not disabled: ");
-        goto end;
-    }
-
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
-}
-
-/** \test Really long banner handling: bannel exactly 255 */
-static int SSHParserTest19(void)
-{
-    int result = 0;
-    Flow f;
-    uint8_t sshbuf1[] = "SSH-";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
-    uint8_t sshbuf2[] = "2.0-";
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8
-    uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
-                        "abcdefghijklmnopqrstuvwxyz"//60
-                        "abcdefghijklmnopqrstuvwxyz"
-                        "abcdefghijklmnopqrstuvwxyz"//112
-                        "abcdefghijklmnopqrstuvwxyz"
-                        "abcdefghijklmnopqrstuvwxyz"//164
-                        "abcdefghijklmnopqrstuvwxyz"
-                        "abcdefghijklmnopqrstuvwxyz"//216
-                        "abcdefghijklmnopqrstuvwxyz"//242
-                        "abcdefghijkl\r";//255
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
-
-    uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
-    uint32_t sshlen4 = sizeof(sshbuf4);
-    TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
-
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
-
-    StreamTcpInitConfig(TRUE);
-
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOCLIENT, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
+    uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00 };
 
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
 
-    if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
+    memset(&tv, 0x00, sizeof(tv));
+
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
+
+    uint8_t* sshbufs[5] = {server1, sshbuf1, sshbuf2, server2, sshbuf3};
+    uint32_t sshlens[5] = {sizeof(server1) - 1, sizeof(sshbuf1) - 1, sizeof(sshbuf2) -1, sizeof(server2) - 1, sizeof(sshbuf3)};
+    bool sshdirs[5] = {true, false, false, true, false};
+
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
+
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
+
+    uint32_t seqcli = 2;
+    uint32_t seqsrv = 2;
+    for (int i=0; i<5; i++) {
+        if (sshdirs[i]) {
+            FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seqsrv, sshbufs[i], sshlens[i]) == -1);
+            seqsrv += sshlens[i];
+            FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn,  &ssn.server, p, UPDATE_DIR_PACKET) < 0);
+        } else {
+            FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx,  &ssn.client, seqcli, sshbufs[i], sshlens[i]) == -1);
+            seqcli += sshlens[i];
+            FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.client, p, UPDATE_DIR_PACKET) < 0);
+        }
     }
 
-    if (ssh_state->srv_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished );
 
-    if (ssh_state->srv_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    FAIL_IF(!(AppLayerParserStateIssetFlag(f->alparser, APP_LAYER_PARSER_NO_INSPECTION)));
 
-    char *name = SCCalloc(1, 256);
-    if (name == NULL)
-        goto end;
-    strlcpy(name, (char *)sshbuf3, 256);
-    name[strlen(name) - 1] = '\0'; // strip \r
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
+}
 
-    if (strcmp((char*)ssh_state->srv_hdr.software_version, name) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+/** \test Really long banner handling: bannel exactly 255 */
+static int SSHParserTest19(void)
+{
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
 
-    if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    uint8_t sshbuf1[] = "SSH-";
+    uint8_t sshbuf2[] = "2.0-";
+    uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
+                        "abcdefghijklmnopqrstuvwxyz"//60
+                        "abcdefghijklmnopqrstuvwxyz"
+                        "abcdefghijklmnopqrstuvwxyz"//112
+                        "abcdefghijklmnopqrstuvwxyz"
+                        "abcdefghijklmnopqrstuvwxyz"//164
+                        "abcdefghijklmnopqrstuvwxyz"
+                        "abcdefghijklmnopqrstuvwxyz"//216
+                        "abcdefghijklmnopqrstuvwxyz"//242
+                        "abcdefghijkl\r";//255
+    uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00};
 
-    if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
-        goto end;
+    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
+    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4)};
+
+    memset(&tv, 0x00, sizeof(tv));
+
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
+
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
+
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
+
+    uint32_t seq = 2;
+    for (int i=0; i<4; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished );
+
+    sshbuf3[sizeof(sshbuf3) - 2] = 0;
+    FAIL_IF(SSHParserTestUtilCheck("2.0", (char *)sshbuf3, tx, STREAM_TOCLIENT));
+
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Really long banner handling: banner exactly 255,
  *        followed by malformed record */
 static int SSHParserTest20(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "2.0-";
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8
     uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
                         "abcdefghijklmnopqrstuvwxyz"//60
                         "abcdefghijklmnopqrstuvwxyz"
@@ -2226,97 +1207,64 @@ static int SSHParserTest20(void)
                         "abcdefghijklmnopqrstuvwxyz"//216
                         "abcdefghijklmnopqrstuvwxyz"//242
                         "abcdefghijklm\r";//256
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
     uint8_t sshbuf4[] = {'a','b','c','d','e','f', '\r',
                          0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00};
-    uint32_t sshlen4 = sizeof(sshbuf4) - 1;
-
-    TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
+    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
+    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1};
 
-    StreamTcpInitConfig(TRUE);
+    memset(&tv, 0x00, sizeof(tv));
 
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOCLIENT, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    SCLogDebug("chunk 4:");
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
 
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
-    if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
 
-    if (ssh_state->srv_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<4; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (ssh_state->srv_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished );
 
-    if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    FAIL_IF(SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOCLIENT));
 
-    if ((ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("detected the msg code of new keys (ciphered data starts): ");
-        goto end;
-    }
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Fragmented banner handling: chunk has final part of bannel plus
  *        a record. */
 static int SSHParserTest21(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "2.0-";
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8
     uint8_t sshbuf3[] = "abcdefghijklmnopqrstuvwxyz"
                         "abcdefghijklmnopqrstuvwxyz"//60
                         "abcdefghijklmnopqrstuvwxyz"
@@ -2326,97 +1274,64 @@ static int SSHParserTest21(void)
                         "abcdefghijklmnopqrstuvwxyz"
                         "abcdefghijklmnopqrstuvwxyz"//216
                         "abcdefghijklmnopqrstuvwxy";//241
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
     uint8_t sshbuf4[] = {'l','i','b','s','s','h', '\r',
                          0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00};
-    uint32_t sshlen4 = sizeof(sshbuf4) - 1;
-    TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
-
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
 
-    StreamTcpInitConfig(TRUE);
+    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
+    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4)};
 
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOCLIENT, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    SCLogDebug("chunk 4:");
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
+    memset(&tv, 0x00, sizeof(tv));
 
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
 
-    if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
-    if (ssh_state->srv_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+    p = PacketGetFromAlloc();
+    FAIL_IF(unlikely(p == NULL));
+    p->proto = IPPROTO_TCP;
+    p->flow = f;
 
-    if (ssh_state->srv_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<4; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished );
 
-    if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
-        goto end;
-    }
+    FAIL_IF(SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOCLIENT));
 
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Fragmented banner handling: chunk has final part of bannel plus
  *        a record. */
 static int SSHParserTest22(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "2.0-";
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1; // 8
     uint8_t sshbuf3[] = {
         'l', 'i', 'b', 's', 's', 'h', '\r', //7
 
@@ -2439,99 +1354,66 @@ static int SSHParserTest22(void)
         0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //150
 
         0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //200
-
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //250
-
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00, 0x00, //300
-    };
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
-    TcpSession ssn;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
-
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    f.alproto = ALPROTO_SSH;
-
-    StreamTcpInitConfig(TRUE);
-
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOCLIENT, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-#if 0
-    SCLogDebug("chunk 4:");
-    r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOCLIENT, sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-#endif
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
-
-    if (!(ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->srv_hdr.software_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
-
-    if (ssh_state->srv_hdr.proto_version == NULL) {
-        printf("Client version string not parsed: ");
-        goto end;
-    }
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //200
+
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00, //250
+
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 17, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x06, 0x01, 21, 0x00, 0x00, 0x00, 0x00, //300
+        };
+
+
+        uint8_t* sshbufs[3] = {sshbuf1, sshbuf2, sshbuf3};
+        uint32_t sshlens[3] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1};
+
+        memset(&tv, 0x00, sizeof(tv));
+
+        StreamTcpUTInit(&ra_ctx);
+        StreamTcpUTInitInline();
+        StreamTcpUTSetupSession(&ssn);
+        StreamTcpUTSetupStream(&ssn.server, 1);
+        StreamTcpUTSetupStream(&ssn.client, 1);
+
+        f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+        FAIL_IF_NULL(f);
+        f->protoctx = &ssn;
+        f->proto = IPPROTO_TCP;
+        f->alproto = ALPROTO_SSH;
+
+        p = PacketGetFromAlloc();
+        FAIL_IF(unlikely(p == NULL));
+        p->proto = IPPROTO_TCP;
+        p->flow = f;
+
+        uint32_t seq = 2;
+        for (int i=0; i<3; i++) {
+            FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+            seq += sshlens[i];
+            FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
+        }
 
-    if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) {
-        printf("Client version string not parsed correctly: ");
-        goto end;
-    }
+        void *ssh_state = f->alstate;
+        FAIL_IF_NULL(ssh_state);
+        void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+        FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOCLIENT) != SshStateFinished );
 
-    if ( !(ssh_state->srv_hdr.flags & SSH_FLAG_PARSER_DONE)) {
-        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
-        goto end;
-    }
+        FAIL_IF(SSHParserTestUtilCheck("2.0", "libssh", tx, STREAM_TOCLIENT));
 
-    result = 1;
-end:
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    return result;
+        UTHFreePacket(p);
+        StreamTcpUTClearSession(&ssn);
+        StreamTcpUTDeinit(ra_ctx);
+        UTHFreeFlow(f);
+        PASS;
 }
 
 /** \test Send a version string in one chunk (client version str). */
@@ -2593,21 +1475,18 @@ static int SSHParserTest24(void)
         goto end;
     }
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f.alstate;
     if (ssh_state == NULL) {
         printf("no ssh state: ");
         goto end;
     }
-
-    if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        printf("Client version string not parsed: ");
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    if ( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) != SshStateBannerDone ) {
+        printf("Didn't detect the msg code of new keys (ciphered data starts): ");
         goto end;
     }
-
-    if (ssh_state->cli_hdr.software_version) {
-        printf("Client version string should not be parsed: ");
+    if (SSHParserTestUtilCheck("2.0", NULL, tx, STREAM_TOSERVER))
         goto end;
-    }
 
     result = 1;
 end:
@@ -2640,11 +1519,13 @@ static int SSHParserTest25(void)
                                 STREAM_TOSERVER | STREAM_EOF, sshbuf, sshlen);
     FAIL_IF(r != -1);
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f.alstate;
     FAIL_IF_NULL(ssh_state);
-
-    FAIL_IF_NOT(!(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED));
-    FAIL_IF(ssh_state->cli_hdr.software_version);
+    void * tx = rs_ssh_state_get_tx(ssh_state, 0);
+    FAIL_IF( rs_ssh_tx_get_flags(tx, STREAM_TOSERVER) == SshStateBannerDone );
+    const uint8_t *dummy = NULL;
+    uint32_t dummy_len = 0;
+    FAIL_IF (rs_ssh_tx_get_software(tx, &dummy, &dummy_len, STREAM_TOCLIENT) != 0);
 
     AppLayerParserThreadCtxFree(alp_tctx);
     StreamTcpFreeConfig(TRUE);
index ef988b3c0d318eff6c95cad2db76318f3682c781..119ae032834802f3d88d1f065f0a393386ebbc6a 100644 (file)
 #ifndef __APP_LAYER_SSH_H__
 #define __APP_LAYER_SSH_H__
 
-/* header flag */
-#define SSH_FLAG_VERSION_PARSED              0x01
-
-/* This flags indicate that the rest of the communication
- * must be ciphered, so the parsing finish here */
-#define SSH_FLAG_PARSER_DONE                 0x02
-
-/* MSG_CODE */
-#define SSH_MSG_NEWKEYS                      21
-
-/** From SSH-TRANSP rfc
-
-    SSH Bunary packet structure:
-      uint32    packet_length
-      byte      padding_length
-      byte[n1]  payload; n1 = packet_length - padding_length - 1
-      byte[n2]  random padding; n2 = padding_length
-      byte[m]   mac (Message Authentication Code - MAC); m = mac_length
-
-    So we are going to do a header struct to store
-    the lenghts and msg_code (inside payload, if any)
-*/
-
-typedef struct SshHeader_ {
-    uint32_t pkt_len;
-    uint8_t padding_len;
-    uint8_t msg_code;
-    uint16_t banner_len;
-    uint8_t buf[6];
-    uint8_t buf_offset;
-    uint8_t flags;
-    uint32_t record_left;
-    uint8_t *proto_version;
-    uint8_t *software_version;
-    uint8_t *banner_buffer;
-} SshHeader;
-
-enum {
-    SSH_STATE_IN_PROGRESS,
-    SSH_STATE_BANNER_DONE,
-    SSH_STATE_FINISHED,
-};
-
-/** structure to store the SSH state values */
-typedef struct SshState_ {
-    SshHeader srv_hdr;
-    SshHeader cli_hdr;
-
-    /* specifies which loggers are done logging */
-    uint32_t logged;
-
-    uint64_t detect_flags_ts;
-    uint64_t detect_flags_tc;
-
-    DetectEngineState *de_state;
-} SshState;
-
 void RegisterSSHParsers(void);
 void SSHParserRegisterTests(void);
 
index f9d8ae5728e5e1a2858d03bca32a6f55843aef18..30a503ff2c747abff4c2ee8e21c3f8eeda36212a 100644 (file)
@@ -51,6 +51,7 @@
 #include "app-layer-parser.h"
 #include "app-layer-ssh.h"
 #include "detect-ssh-proto-version.h"
+#include "rust.h"
 
 #include "stream-tcp.h"
 
@@ -109,34 +110,35 @@ static int DetectSshVersionMatch (DetectEngineThreadCtx *det_ctx,
     SCLogDebug("lets see");
 
     DetectSshVersionData *ssh = (DetectSshVersionData *)m;
-    SshState *ssh_state = (SshState *)state;
-    if (ssh_state == NULL) {
+    if (state == NULL) {
         SCLogDebug("no ssh state, no match");
         SCReturnInt(0);
     }
 
     int ret = 0;
-    if ((flags & STREAM_TOCLIENT) && (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) {
-            SCLogDebug("looking for ssh server protoversion 2 compat");
-            if (strncmp((char *) ssh_state->srv_hdr.proto_version, "2", 1) == 0 ||
-                strncmp((char *) ssh_state->srv_hdr.proto_version, "2.", 2) == 0 ||
-                strncmp((char *) ssh_state->srv_hdr.proto_version, "1.99", 4) == 0)
+    const uint8_t *protocol = NULL;
+    uint32_t b_len = 0;
+
+    if (rs_ssh_tx_get_protocol(txv, &protocol, &b_len, flags) != 1)
+        SCReturnInt(0);
+    if (protocol == NULL || b_len == 0)
+        SCReturnInt(0);
+
+    if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) {
+        SCLogDebug("looking for ssh protoversion 2 compat");
+        if (protocol[0] == '2') {
+            ret = 1;
+        } else if (b_len >= 4) {
+            if (memcmp(protocol, "1.99", 4) == 0)    {
                 ret = 1;
-        } else {
-            SCLogDebug("looking for ssh server protoversion %s length %"PRIu16"", ssh->ver, ssh->len);
-            ret = (strncmp((char *) ssh_state->srv_hdr.proto_version, (char *) ssh->ver, ssh->len) == 0)? 1 : 0;
+            }
         }
-    } else if ((flags & STREAM_TOSERVER) && (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) {
-            SCLogDebug("looking for client ssh client protoversion 2 compat");
-            if (strncmp((char *) ssh_state->cli_hdr.proto_version, "2", 1) == 0 ||
-                strncmp((char *) ssh_state->cli_hdr.proto_version, "2.", 2) == 0 ||
-                strncmp((char *) ssh_state->cli_hdr.proto_version, "1.99", 4) == 0)
+    } else {
+        SCLogDebug("looking for ssh protoversion %s length %"PRIu16"", ssh->ver, ssh->len);
+        if (b_len == ssh->len) {
+            if (memcmp(protocol, ssh->ver, ssh->len) == 0) {
                 ret = 1;
-        } else {
-            SCLogDebug("looking for ssh client protoversion %s length %"PRIu16"", ssh->ver, ssh->len);
-            ret = (strncmp((char *) ssh_state->cli_hdr.proto_version, (char *) ssh->ver, ssh->len) == 0)? 1 : 0;
+            }
         }
     }
     SCReturnInt(ret);
@@ -331,46 +333,52 @@ static int DetectSshVersionTestParse03 (void)
 
 
 #include "stream-tcp-reassemble.h"
+#include "stream-tcp-util.h"
 
 /** \test Send a get request in three chunks + more data. */
 static int DetectSshVersionTestDetect01(void)
 {
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-1.";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "10-PuTTY_2.123" ;
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
     uint8_t sshbuf3[] = "\n";
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
     uint8_t sshbuf4[] = "whatever...";
-    uint32_t sshlen4 = sizeof(sshbuf4) - 1;
-    TcpSession ssn;
-    Packet *p = NULL;
-    Signature *s = NULL;
-    ThreadVars th_v;
-    DetectEngineThreadCtx *det_ctx = NULL;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&th_v, 0, sizeof(th_v));
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
+    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
+    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1};
+
+    memset(&tv, 0x00, sizeof(tv));
+
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
+
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
     p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
-    FAIL_IF_NULL(p);
+    FAIL_IF(unlikely(p == NULL));
+    p->flow = f;
 
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    p->flow = &f;
-    p->flowflags |= FLOW_PKT_TOSERVER;
-    p->flowflags |= FLOW_PKT_ESTABLISHED;
-    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
-    f.alproto = ALPROTO_SSH;
-    f.proto = IPPROTO_TCP;
+    Signature *s = NULL;
+    ThreadVars th_v;
+    DetectEngineThreadCtx *det_ctx = NULL;
 
-    StreamTcpInitConfig(TRUE);
+    memset(&th_v, 0, sizeof(th_v));
 
     DetectEngineCtx *de_ctx = DetectEngineCtxInit();
-    FAIL_IF_NULL (de_ctx);
+    FAIL_IF_NULL(de_ctx);
+
     de_ctx->flags |= DE_QUIET;
 
     s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:1.10; sid:1;)");
@@ -379,276 +387,183 @@ static int DetectSshVersionTestDetect01(void)
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
 
-    SCLogDebug("==> 1");
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    FAIL_IF(r != 0);
-
-    SCLogDebug("==> 2");
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    FAIL_IF(r != 0);
-
-    SCLogDebug("==> 3");
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf3, sshlen3);
-    FAIL_IF(r != 0);
-
-    SCLogDebug("==> 4");
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf4, sshlen4);
-    FAIL_IF(r != 0);
+    uint32_t seq = 2;
+    for (int i=0; i<4; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
+    }
 
-    SshState *ssh_state = f.alstate;
+    void *ssh_state = f->alstate;
     FAIL_IF_NULL(ssh_state);
 
     /* do detect */
     SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
 
-    FAIL_IF(!(PacketAlertCheck(p, 1)));
+    FAIL_IF(PacketAlertCheck(p, 1));
 
     DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
     DetectEngineCtxFree(de_ctx);
 
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-    UTHFreePackets(&p, 1);
-    AppLayerParserThreadCtxFree(alp_tctx);
-
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
     PASS;
 }
 
 /** \test Send a get request in three chunks + more data. */
 static int DetectSshVersionTestDetect02(void)
 {
-    int result = 0;
-    Flow f;
-    uint8_t sshbuf1[] = "SSH-1.";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
-    uint8_t sshbuf2[] = "99-PuTTY_2.123" ;
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
-    uint8_t sshbuf3[] = "\n";
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
-    uint8_t sshbuf4[] = "whatever...";
-    uint32_t sshlen4 = sizeof(sshbuf4) - 1;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
     TcpSession ssn;
+    Flow *f = NULL;
     Packet *p = NULL;
-    Signature *s = NULL;
-    ThreadVars th_v;
-    DetectEngineThreadCtx *det_ctx = NULL;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&th_v, 0, sizeof(th_v));
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
+    uint8_t sshbuf1[] = "SSH-1.99-Pu";
+    uint8_t sshbuf2[] = "TTY_2.123" ;
+    uint8_t sshbuf3[] = "\n";
+    uint8_t sshbuf4[] = "whatever...";
+
+    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
+    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1};
+
+    memset(&tv, 0x00, sizeof(tv));
+
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
+
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
     p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+    FAIL_IF(unlikely(p == NULL));
+    p->flow = f;
 
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    p->flow = &f;
-    p->flowflags |= FLOW_PKT_TOSERVER;
-    p->flowflags |= FLOW_PKT_ESTABLISHED;
-    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
-    f.alproto = ALPROTO_SSH;
-    f.proto = IPPROTO_TCP;
+    Signature *s = NULL;
+    ThreadVars th_v;
+    DetectEngineThreadCtx *det_ctx = NULL;
 
-    StreamTcpInitConfig(TRUE);
+    memset(&th_v, 0, sizeof(th_v));
 
     DetectEngineCtx *de_ctx = DetectEngineCtxInit();
-    if (de_ctx == NULL) {
-        goto end;
-    }
+    FAIL_IF_NULL(de_ctx);
 
     de_ctx->flags |= DE_QUIET;
 
     s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)");
-    if (s == NULL) {
-        goto end;
-    }
+    FAIL_IF_NULL(s);
 
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
 
-    FLOWLOCK_WRLOCK(&f);
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<4; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-    FLOWLOCK_UNLOCK(&f);
-
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
 
     /* do detect */
     SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
 
-    if ( !(PacketAlertCheck(p, 1))) {
-        printf("Error, the sig should match: ");
-        goto end;
-    }
-
-    result = 1;
-end:
-    SigGroupCleanup(de_ctx);
-    SigCleanSignatures(de_ctx);
+    FAIL_IF(PacketAlertCheck(p, 1));
 
     DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
     DetectEngineCtxFree(de_ctx);
 
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-
-    UTHFreePackets(&p, 1);
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Send a get request in three chunks + more data. */
 static int DetectSshVersionTestDetect03(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-1.";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "7-PuTTY_2.123" ;
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
     uint8_t sshbuf3[] = "\n";
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
     uint8_t sshbuf4[] = "whatever...";
-    uint32_t sshlen4 = sizeof(sshbuf4) - 1;
-    TcpSession ssn;
-    Packet *p = NULL;
-    Signature *s = NULL;
-    ThreadVars th_v;
-    DetectEngineThreadCtx *det_ctx = NULL;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&th_v, 0, sizeof(th_v));
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
+    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
+    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1};
+
+    memset(&tv, 0x00, sizeof(tv));
+
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
+
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
     p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+    FAIL_IF(unlikely(p == NULL));
+    p->flow = f;
 
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    p->flow = &f;
-    p->flowflags |= FLOW_PKT_TOSERVER;
-    p->flowflags |= FLOW_PKT_ESTABLISHED;
-    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
-    f.alproto = ALPROTO_SSH;
-    f.proto = IPPROTO_TCP;
+    Signature *s = NULL;
+    ThreadVars th_v;
+    DetectEngineThreadCtx *det_ctx = NULL;
 
-    StreamTcpInitConfig(TRUE);
+    memset(&th_v, 0, sizeof(th_v));
 
     DetectEngineCtx *de_ctx = DetectEngineCtxInit();
-    if (de_ctx == NULL) {
-        goto end;
-    }
+    FAIL_IF_NULL(de_ctx);
 
     de_ctx->flags |= DE_QUIET;
 
     s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)");
-    if (s == NULL) {
-        goto end;
-    }
+    FAIL_IF_NULL(s);
 
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
 
-    FLOWLOCK_WRLOCK(&f);
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<4; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-    FLOWLOCK_UNLOCK(&f);
-
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
 
     /* do detect */
     SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
 
-    if (PacketAlertCheck(p, 1)) {
-        printf("Error, 1.7 version is not 2 compat, so the sig should not match: ");
-        goto end;
-    }
-
-    result = 1;
-end:
-    SigGroupCleanup(de_ctx);
-    SigCleanSignatures(de_ctx);
+    FAIL_IF(PacketAlertCheck(p, 1));
 
     DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
     DetectEngineCtxFree(de_ctx);
 
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-
-    UTHFreePackets(&p, 1);
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 #endif /* UNITTESTS */
index 2f0de667fc407e1b04a20e5df4de572ad0bcf09c..d8eec71d4e56fcab9ffeb330da96f86cffd7cb37 100644 (file)
@@ -46,6 +46,7 @@
 #include "app-layer-parser.h"
 #include "app-layer-ssh.h"
 #include "detect-ssh-proto.h"
+#include "rust.h"
 
 #define KEYWORD_NAME "ssh.proto"
 #define KEYWORD_NAME_LEGACY "ssh_proto"
@@ -63,27 +64,17 @@ static InspectionBuffer *GetSshData(DetectEngineThreadCtx *det_ctx,
     InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
 
     if (buffer->inspect == NULL) {
-        uint8_t *protocol = NULL;
-        SshState *ssh_state = (SshState *) txv;
+        const uint8_t *protocol = NULL;
+        uint32_t b_len = 0;
 
-        if (flow_flags & STREAM_TOSERVER)
-            protocol = ssh_state->cli_hdr.proto_version;
-        else if (flow_flags & STREAM_TOCLIENT)
-            protocol = ssh_state->srv_hdr.proto_version;
-
-        if (protocol == NULL) {
-            SCLogDebug("SSL protocol not set");
+        if (rs_ssh_tx_get_protocol(txv, &protocol, &b_len, flow_flags) != 1)
             return NULL;
-        }
-
-        uint32_t data_len = strlen((char *)protocol);
-        uint8_t *data = protocol;
-        if (data == NULL || data_len == 0) {
-            SCLogDebug("SSL protocol not present");
+        if (protocol == NULL || b_len == 0) {
+            SCLogDebug("SSH protocol not set");
             return NULL;
         }
 
-        InspectionBufferSetup(buffer, data, data_len);
+        InspectionBufferSetup(buffer, protocol, b_len);
         InspectionBufferApplyTransforms(buffer, transforms);
     }
 
@@ -113,16 +104,16 @@ void DetectSshProtocolRegister(void)
 
     DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOSERVER, 2,
             PrefilterGenericMpmRegister, GetSshData,
-                       ALPROTO_SSH, SSH_STATE_BANNER_DONE),
+                       ALPROTO_SSH, SshStateBannerDone),
     DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOCLIENT, 2,
             PrefilterGenericMpmRegister, GetSshData,
-                       ALPROTO_SSH, SSH_STATE_BANNER_DONE),
+                       ALPROTO_SSH, SshStateBannerDone),
 
     DetectAppLayerInspectEngineRegister2(BUFFER_NAME,
-            ALPROTO_SSH, SIG_FLAG_TOSERVER, SSH_STATE_BANNER_DONE,
+            ALPROTO_SSH, SIG_FLAG_TOSERVER, SshStateBannerDone,
             DetectEngineInspectBufferGeneric, GetSshData);
     DetectAppLayerInspectEngineRegister2(BUFFER_NAME,
-            ALPROTO_SSH, SIG_FLAG_TOCLIENT, SSH_STATE_BANNER_DONE,
+            ALPROTO_SSH, SIG_FLAG_TOCLIENT, SshStateBannerDone,
             DetectEngineInspectBufferGeneric, GetSshData);
 
     DetectBufferTypeSetDescriptionByName(BUFFER_NAME, BUFFER_DESC);
index d5736e84099f789808a20e48830df9677948efcb..e24a241910db057b8aa0e555626ea993c95a271e 100644 (file)
@@ -55,6 +55,7 @@
 #include "app-layer-parser.h"
 #include "app-layer-ssh.h"
 #include "detect-ssh-software-version.h"
+#include "rust.h"
 
 #include "stream-tcp.h"
 
@@ -103,10 +104,10 @@ void DetectSshSoftwareVersionRegister(void)
     g_ssh_banner_list_id = DetectBufferTypeRegister("ssh_banner");
 
     DetectAppLayerInspectEngineRegister("ssh_banner",
-            ALPROTO_SSH, SIG_FLAG_TOSERVER, SSH_STATE_BANNER_DONE,
+            ALPROTO_SSH, SIG_FLAG_TOSERVER, SshStateBannerDone,
             InspectSshBanner);
     DetectAppLayerInspectEngineRegister("ssh_banner",
-            ALPROTO_SSH, SIG_FLAG_TOCLIENT, SSH_STATE_BANNER_DONE,
+            ALPROTO_SSH, SIG_FLAG_TOCLIENT, SshStateBannerDone,
             InspectSshBanner);
 }
 
@@ -128,20 +129,25 @@ static int DetectSshSoftwareVersionMatch (DetectEngineThreadCtx *det_ctx,
     SCEnter();
 
     DetectSshSoftwareVersionData *ssh = (DetectSshSoftwareVersionData *)m;
-    SshState *ssh_state = (SshState *)state;
-    if (ssh_state == NULL) {
+    if (state == NULL) {
         SCLogDebug("no ssh state, no match");
         SCReturnInt(0);
     }
 
     int ret = 0;
-    if ((flags & STREAM_TOCLIENT) && (ssh_state->srv_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        SCLogDebug("looking for ssh server softwareversion %s length %"PRIu16" on %s", ssh->software_ver, ssh->len, ssh_state->srv_hdr.software_version);
-        ret = (strncmp((char *) ssh_state->srv_hdr.software_version, (char *) ssh->software_ver, ssh->len) == 0)? 1 : 0;
-    } else if ((flags & STREAM_TOSERVER) && (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) {
-        SCLogDebug("looking for ssh client softwareversion %s length %"PRIu16" on %s", ssh->software_ver, ssh->len, ssh_state->cli_hdr.software_version);
-        ret = (strncmp((char *) ssh_state->cli_hdr.software_version, (char *) ssh->software_ver, ssh->len) == 0)? 1 : 0;
+    const uint8_t *software = NULL;
+    uint32_t b_len = 0;
+
+    if (rs_ssh_tx_get_software(txv, &software, &b_len, flags) != 1)
+        SCReturnInt(0);
+    if (software == NULL || b_len == 0)
+        SCReturnInt(0);
+    if (b_len == ssh->len) {
+        if (memcmp(software, ssh->software_ver, ssh->len) == 0) {
+            ret = 1;
+        }
     }
+
     SCReturnInt(ret);
 }
 
@@ -312,357 +318,237 @@ static int DetectSshSoftwareVersionTestParse03 (void)
 
 
 #include "stream-tcp-reassemble.h"
+#include "stream-tcp-util.h"
 
 /** \test Send a get request in three chunks + more data. */
 static int DetectSshSoftwareVersionTestDetect01(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-1.";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "10-PuTTY_2.123" ;
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
     uint8_t sshbuf3[] = "\n";
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
     uint8_t sshbuf4[] = "whatever...";
-    uint32_t sshlen4 = sizeof(sshbuf4) - 1;
-    TcpSession ssn;
-    Packet *p = NULL;
-    Signature *s = NULL;
-    ThreadVars th_v;
-    DetectEngineThreadCtx *det_ctx = NULL;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&th_v, 0, sizeof(th_v));
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
+    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
+    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1};
+
+    memset(&tv, 0x00, sizeof(tv));
+
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
+
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
     p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+    FAIL_IF(unlikely(p == NULL));
+    p->flow = f;
 
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    p->flow = &f;
-    p->flowflags |= FLOW_PKT_TOSERVER;
-    p->flowflags |= FLOW_PKT_ESTABLISHED;
-    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
-    f.alproto = ALPROTO_SSH;
-    f.proto = IPPROTO_TCP;
+    Signature *s = NULL;
+    ThreadVars th_v;
+    DetectEngineThreadCtx *det_ctx = NULL;
 
-    StreamTcpInitConfig(TRUE);
+    memset(&th_v, 0, sizeof(th_v));
 
     DetectEngineCtx *de_ctx = DetectEngineCtxInit();
-    if (de_ctx == NULL) {
-        goto end;
-    }
+    FAIL_IF_NULL(de_ctx);
 
     de_ctx->flags |= DE_QUIET;
 
     s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:PuTTY_2.123; sid:1;)");
-    if (s == NULL) {
-        goto end;
-    }
+    FAIL_IF_NULL(s);
 
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
 
-    FLOWLOCK_WRLOCK(&f);
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<4; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-    FLOWLOCK_UNLOCK(&f);
-
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
 
     /* do detect */
     SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
 
-    if ( !(PacketAlertCheck(p, 1))) {
-        printf("Error, the sig should match: ");
-        goto end;
-    }
-
-    result = 1;
-end:
-    SigGroupCleanup(de_ctx);
-    SigCleanSignatures(de_ctx);
+    FAIL_IF(PacketAlertCheck(p, 1));
 
     DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
     DetectEngineCtxFree(de_ctx);
 
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-
-    UTHFreePackets(&p, 1);
-
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Send a get request in three chunks + more data. */
 static int DetectSshSoftwareVersionTestDetect02(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-1.99-Pu";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "TTY_2.123" ;
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
     uint8_t sshbuf3[] = "\n";
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
     uint8_t sshbuf4[] = "whatever...";
-    uint32_t sshlen4 = sizeof(sshbuf4) - 1;
-    TcpSession ssn;
-    Packet *p = NULL;
-    Signature *s = NULL;
-    ThreadVars th_v;
-    DetectEngineThreadCtx *det_ctx = NULL;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&th_v, 0, sizeof(th_v));
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
+    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
+    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1};
+
+    memset(&tv, 0x00, sizeof(tv));
+
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
+
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
     p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+    FAIL_IF(unlikely(p == NULL));
+    p->flow = f;
 
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    p->flow = &f;
-    p->flowflags |= FLOW_PKT_TOSERVER;
-    p->flowflags |= FLOW_PKT_ESTABLISHED;
-    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
-    f.alproto = ALPROTO_SSH;
-    f.proto = IPPROTO_TCP;
+    Signature *s = NULL;
+    ThreadVars th_v;
+    DetectEngineThreadCtx *det_ctx = NULL;
 
-    StreamTcpInitConfig(TRUE);
+    memset(&th_v, 0, sizeof(th_v));
 
     DetectEngineCtx *de_ctx = DetectEngineCtxInit();
-    if (de_ctx == NULL) {
-        goto end;
-    }
+    FAIL_IF_NULL(de_ctx);
 
     de_ctx->flags |= DE_QUIET;
 
     s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:PuTTY_2.123; sid:1;)");
-    if (s == NULL) {
-        goto end;
-    }
+    FAIL_IF_NULL(s);
 
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
 
-    FLOWLOCK_WRLOCK(&f);
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<4; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-    FLOWLOCK_UNLOCK(&f);
-
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
 
     /* do detect */
     SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
 
-    if ( !(PacketAlertCheck(p, 1))) {
-        printf("Error, the sig should match: ");
-        goto end;
-    }
-
-    result = 1;
-end:
-    SigGroupCleanup(de_ctx);
-    SigCleanSignatures(de_ctx);
+    FAIL_IF(PacketAlertCheck(p, 1));
 
     DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
     DetectEngineCtxFree(de_ctx);
 
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-
-    UTHFreePackets(&p, 1);
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 /** \test Send a get request in three chunks + more data. */
 static int DetectSshSoftwareVersionTestDetect03(void)
 {
-    int result = 0;
-    Flow f;
+    TcpReassemblyThreadCtx *ra_ctx = NULL;
+    ThreadVars tv;
+    TcpSession ssn;
+    Flow *f = NULL;
+    Packet *p = NULL;
+
     uint8_t sshbuf1[] = "SSH-1.";
-    uint32_t sshlen1 = sizeof(sshbuf1) - 1;
     uint8_t sshbuf2[] = "7-PuTTY_2.123" ;
-    uint32_t sshlen2 = sizeof(sshbuf2) - 1;
     uint8_t sshbuf3[] = "\n";
-    uint32_t sshlen3 = sizeof(sshbuf3) - 1;
     uint8_t sshbuf4[] = "whatever...";
-    uint32_t sshlen4 = sizeof(sshbuf4) - 1;
-    TcpSession ssn;
-    Packet *p = NULL;
-    Signature *s = NULL;
-    ThreadVars th_v;
-    DetectEngineThreadCtx *det_ctx = NULL;
-    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
 
-    memset(&th_v, 0, sizeof(th_v));
-    memset(&f, 0, sizeof(f));
-    memset(&ssn, 0, sizeof(ssn));
+    uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4};
+    uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1};
+
+    memset(&tv, 0x00, sizeof(tv));
+
+    StreamTcpUTInit(&ra_ctx);
+    StreamTcpUTInitInline();
+    StreamTcpUTSetupSession(&ssn);
+    StreamTcpUTSetupStream(&ssn.server, 1);
+    StreamTcpUTSetupStream(&ssn.client, 1);
+
+    f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222);
+    FAIL_IF_NULL(f);
+    f->protoctx = &ssn;
+    f->proto = IPPROTO_TCP;
+    f->alproto = ALPROTO_SSH;
 
     p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+    FAIL_IF(unlikely(p == NULL));
+    p->flow = f;
 
-    FLOW_INITIALIZE(&f);
-    f.protoctx = (void *)&ssn;
-    p->flow = &f;
-    p->flowflags |= FLOW_PKT_TOSERVER;
-    p->flowflags |= FLOW_PKT_ESTABLISHED;
-    p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
-    f.alproto = ALPROTO_SSH;
-    f.proto = IPPROTO_TCP;
+    Signature *s = NULL;
+    ThreadVars th_v;
+    DetectEngineThreadCtx *det_ctx = NULL;
 
-    StreamTcpInitConfig(TRUE);
+    memset(&th_v, 0, sizeof(th_v));
 
     DetectEngineCtx *de_ctx = DetectEngineCtxInit();
-    if (de_ctx == NULL) {
-        goto end;
-    }
+    FAIL_IF_NULL(de_ctx);
 
     de_ctx->flags |= DE_QUIET;
 
     s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:lalala-3.1.4; sid:1;)");
-    if (s == NULL) {
-        goto end;
-    }
+    FAIL_IF_NULL(s);
 
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
 
-    FLOWLOCK_WRLOCK(&f);
-    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH,
-                                STREAM_TOSERVER, sshbuf1, sshlen1);
-    if (r != 0) {
-        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf2, sshlen2);
-    if (r != 0) {
-        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
+    uint32_t seq = 2;
+    for (int i=0; i<4; i++) {
+        FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1);
+        seq += sshlens[i];
+        FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0);
     }
 
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf3, sshlen3);
-    if (r != 0) {
-        printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-
-    r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER,
-                            sshbuf4, sshlen4);
-    if (r != 0) {
-        printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
-        FLOWLOCK_UNLOCK(&f);
-        goto end;
-    }
-    FLOWLOCK_UNLOCK(&f);
-
-    SshState *ssh_state = f.alstate;
-    if (ssh_state == NULL) {
-        printf("no ssh state: ");
-        goto end;
-    }
+    void *ssh_state = f->alstate;
+    FAIL_IF_NULL(ssh_state);
 
     /* do detect */
     SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
 
-    if (PacketAlertCheck(p, 1)) {
-        printf("Error, 1.7 version is not 2 compat, so the sig should not match: ");
-        goto end;
-    }
-
-    result = 1;
-end:
-    SigGroupCleanup(de_ctx);
-    SigCleanSignatures(de_ctx);
+    FAIL_IF(PacketAlertCheck(p, 1));
 
     DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
     DetectEngineCtxFree(de_ctx);
 
-    StreamTcpFreeConfig(TRUE);
-    FLOW_DESTROY(&f);
-
-    UTHFreePackets(&p, 1);
-    if (alp_tctx != NULL)
-        AppLayerParserThreadCtxFree(alp_tctx);
-    return result;
+    UTHFreePacket(p);
+    StreamTcpUTClearSession(&ssn);
+    StreamTcpUTDeinit(ra_ctx);
+    UTHFreeFlow(f);
+    PASS;
 }
 
 #endif /* UNITTESTS */
index 4d1b8d9eb16d51ab9efab4b9a55b056b51ece368..9765e3844e3fa6bffd1478ced071a88e46af7f03 100644 (file)
@@ -46,6 +46,7 @@
 #include "app-layer-parser.h"
 #include "app-layer-ssh.h"
 #include "detect-ssh-software.h"
+#include "rust.h"
 
 #define KEYWORD_NAME "ssh.software"
 #define KEYWORD_NAME_LEGACY "ssh_software"
@@ -63,27 +64,17 @@ static InspectionBuffer *GetSshData(DetectEngineThreadCtx *det_ctx,
     InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
 
     if (buffer->inspect == NULL) {
-        uint8_t *software = NULL;
-        SshState *ssh_state = (SshState *) txv;
+        const uint8_t *software = NULL;
+        uint32_t b_len = 0;
 
-        if (flow_flags & STREAM_TOSERVER)
-            software = ssh_state->cli_hdr.software_version;
-        else if (flow_flags & STREAM_TOCLIENT)
-            software = ssh_state->srv_hdr.software_version;
-
-        if (software == NULL) {
-            SCLogDebug("SSL software version not set");
+        if (rs_ssh_tx_get_software(txv, &software, &b_len, flow_flags) != 1)
             return NULL;
-        }
-
-        uint32_t data_len = strlen((char *)software);
-        uint8_t *data = software;
-        if (data == NULL || data_len == 0) {
-            SCLogDebug("SSL software version not present");
+        if (software == NULL || b_len == 0) {
+            SCLogDebug("SSH software version not set");
             return NULL;
         }
 
-        InspectionBufferSetup(buffer, data, data_len);
+        InspectionBufferSetup(buffer, software, b_len);
         InspectionBufferApplyTransforms(buffer, transforms);
     }
 
@@ -113,16 +104,16 @@ void DetectSshSoftwareRegister(void)
 
     DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOSERVER, 2,
             PrefilterGenericMpmRegister, GetSshData,
-                       ALPROTO_SSH, SSH_STATE_BANNER_DONE),
+                       ALPROTO_SSH, SshStateBannerDone),
     DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOCLIENT, 2,
             PrefilterGenericMpmRegister, GetSshData,
-                       ALPROTO_SSH, SSH_STATE_BANNER_DONE),
+                       ALPROTO_SSH, SshStateBannerDone),
 
     DetectAppLayerInspectEngineRegister2(BUFFER_NAME,
-            ALPROTO_SSH, SIG_FLAG_TOSERVER, SSH_STATE_BANNER_DONE,
+            ALPROTO_SSH, SIG_FLAG_TOSERVER, SshStateBannerDone,
             DetectEngineInspectBufferGeneric, GetSshData);
     DetectAppLayerInspectEngineRegister2(BUFFER_NAME,
-            ALPROTO_SSH, SIG_FLAG_TOCLIENT, SSH_STATE_BANNER_DONE,
+            ALPROTO_SSH, SIG_FLAG_TOCLIENT, SshStateBannerDone,
             DetectEngineInspectBufferGeneric, GetSshData);
 
     DetectBufferTypeSetDescriptionByName(BUFFER_NAME, BUFFER_DESC);
index e72c1701c308d80765ee42a469c63ba3c92b7b24..13406df1571f1c3f525ef4546c25bd4bbe3a0a46 100644 (file)
@@ -61,6 +61,7 @@
 #include "output-json-http.h"
 #include "output-json-tls.h"
 #include "output-json-ssh.h"
+#include "rust.h"
 #include "output-json-smtp.h"
 #include "output-json-email-common.h"
 #include "output-json-nfs.h"
@@ -145,14 +146,13 @@ static void AlertJsonTls(const Flow *f, json_t *js)
 
 static void AlertJsonSsh(const Flow *f, json_t *js)
 {
-    SshState *ssh_state = (SshState *)FlowGetAppState(f);
+    void *ssh_state = FlowGetAppState(f);
     if (ssh_state) {
-        json_t *tjs = json_object();
+        void *tx_ptr = rs_ssh_state_get_tx(ssh_state, 0);
+        json_t *tjs = rs_ssh_log_json(tx_ptr);
         if (unlikely(tjs == NULL))
             return;
 
-        JsonSshLogJSON(tjs, ssh_state);
-
         json_object_set_new(js, "ssh", tjs);
     }
 
index 348cae8359284c94d182d137bec1967d9f9a4aef..9906e88cc98ddbce7ab711296ee9d3f84777737a 100644 (file)
@@ -49,6 +49,7 @@
 
 #include "output-json.h"
 #include "output-json-ssh.h"
+#include "rust.h"
 
 #define MODULE_NAME "LogSshLog"
 
@@ -64,62 +65,30 @@ typedef struct JsonSshLogThread_ {
 } JsonSshLogThread;
 
 
-void JsonSshLogJSON(json_t *tjs, SshState *ssh_state)
-{
-    json_t *cjs = json_object();
-    if (cjs != NULL) {
-        json_object_set_new(cjs, "proto_version",
-                SCJsonString((char *)ssh_state->cli_hdr.proto_version));
-
-        json_object_set_new(cjs, "software_version",
-                SCJsonString((char *)ssh_state->cli_hdr.software_version));
-    }
-    json_object_set_new(tjs, "client", cjs);
-
-    json_t *sjs = json_object();
-    if (sjs != NULL) {
-        json_object_set_new(sjs, "proto_version",
-                SCJsonString((char *)ssh_state->srv_hdr.proto_version));
-
-        json_object_set_new(sjs, "software_version",
-                SCJsonString((char *)ssh_state->srv_hdr.software_version));
-    }
-    json_object_set_new(tjs, "server", sjs);
-
-}
-
 static int JsonSshLogger(ThreadVars *tv, void *thread_data, const Packet *p,
                          Flow *f, void *state, void *txptr, uint64_t tx_id)
 {
     JsonSshLogThread *aft = (JsonSshLogThread *)thread_data;
     OutputSshCtx *ssh_ctx = aft->sshlog_ctx;
 
-    SshState *ssh_state = (SshState *)state;
-    if (unlikely(ssh_state == NULL)) {
+    if (unlikely(state == NULL)) {
         return 0;
     }
 
-    if (ssh_state->cli_hdr.software_version == NULL ||
-        ssh_state->srv_hdr.software_version == NULL)
-        return 0;
-
     json_t *js = CreateJSONHeader(p, LOG_DIR_FLOW, "ssh");
     if (unlikely(js == NULL))
         return 0;
 
     JsonAddCommonOptions(&ssh_ctx->cfg, p, f, js);
 
-    json_t *tjs = json_object();
-    if (tjs == NULL) {
-        free(js);
-        return 0;
-    }
-
     /* reset */
     MemBufferReset(aft->buffer);
 
-    JsonSshLogJSON(tjs, ssh_state);
-
+    json_t *tjs = rs_ssh_log_json(txptr);
+    if (unlikely(tjs == NULL)) {
+        free(js);
+        return 0;
+    }
     json_object_set_new(js, "ssh", tjs);
 
     OutputJSONBuffer(js, ssh_ctx->file_ctx, &aft->buffer);
@@ -261,13 +230,13 @@ void JsonSshLogRegister (void)
     OutputRegisterTxModuleWithProgress(LOGGER_JSON_SSH,
         "JsonSshLog", "ssh-json-log",
         OutputSshLogInit, ALPROTO_SSH, JsonSshLogger,
-        SSH_STATE_BANNER_DONE, SSH_STATE_BANNER_DONE,
+        SshStateBannerDone, SshStateBannerDone,
         JsonSshLogThreadInit, JsonSshLogThreadDeinit, NULL);
 
     /* also register as child of eve-log */
     OutputRegisterTxSubModuleWithProgress(LOGGER_JSON_SSH,
         "eve-log", "JsonSshLog", "eve-log.ssh",
         OutputSshLogInitSub, ALPROTO_SSH, JsonSshLogger,
-        SSH_STATE_BANNER_DONE, SSH_STATE_BANNER_DONE,
+        SshStateBannerDone, SshStateBannerDone,
         JsonSshLogThreadInit, JsonSshLogThreadDeinit, NULL);
 }
index aa939b09346fc57b9a8113877e57343749067c63..d0f9d3fc7dde74152e001c7036b66dc69e4db026 100644 (file)
@@ -26,8 +26,4 @@
 
 void JsonSshLogRegister(void);
 
-#include "app-layer-ssh.h"
-
-void JsonSshLogJSON(json_t *js, SshState *tx);
-
 #endif /* __OUTPUT_JSON_SSH_H__ */
index a7709b635fa095635ea9f93fc252da252cfa03d2..283d5f72e0be5ba155b465917a1a15b8edaf1a66 100644 (file)
@@ -823,8 +823,8 @@ static OutputInitResult OutputLuaLogInit(ConfNode *conf)
         } else if (opts.alproto == ALPROTO_SSH) {
             om->TxLogFunc = LuaTxLogger;
             om->alproto = ALPROTO_SSH;
-            om->tc_log_progress = SSH_STATE_BANNER_DONE;
-            om->ts_log_progress = SSH_STATE_BANNER_DONE;
+            om->tc_log_progress = SshStateBannerDone;
+            om->ts_log_progress = SshStateBannerDone;
             AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SSH);
         } else if (opts.alproto == ALPROTO_SMTP) {
             om->TxLogFunc = LuaTxLogger;
index 9370798a8a06014b90c5d9c87e30eb97c56ff91a..4b52ece34c1889fa1977728eb22e9a3a7640f213 100644 (file)
@@ -47,6 +47,7 @@
 #include "util-proto-name.h"
 #include "util-logopenfile.h"
 #include "util-time.h"
+#include "rust.h"
 
 #ifdef HAVE_LUA
 
@@ -63,14 +64,17 @@ static int GetServerProtoVersion(lua_State *luastate, const Flow *f)
     void *state = FlowGetAppState(f);
     if (state == NULL)
         return LuaCallbackError(luastate, "error: no app layer state");
+    const uint8_t *protocol = NULL;
+    uint32_t b_len = 0;
 
-    SshState *ssh_state = (SshState *)state;
-
-    if (ssh_state->srv_hdr.proto_version == NULL)
+    void *tx = rs_ssh_state_get_tx(state, 0);
+    if (rs_ssh_tx_get_protocol(tx, &protocol, &b_len, STREAM_TOCLIENT) != 1)
+        return LuaCallbackError(luastate, "error: no server proto version");
+    if (protocol == NULL || b_len == 0) {
         return LuaCallbackError(luastate, "error: no server proto version");
+    }
 
-    return LuaPushStringBuffer(luastate, ssh_state->srv_hdr.proto_version,
-                               strlen((char *)ssh_state->srv_hdr.proto_version));
+    return LuaPushStringBuffer(luastate, protocol, b_len);
 }
 
 static int SshGetServerProtoVersion(lua_State *luastate)
@@ -95,13 +99,17 @@ static int GetServerSoftwareVersion(lua_State *luastate, const Flow *f)
     if (state == NULL)
         return LuaCallbackError(luastate, "error: no app layer state");
 
-    SshState *ssh_state = (SshState *)state;
+    const uint8_t *software = NULL;
+    uint32_t b_len = 0;
 
-    if (ssh_state->srv_hdr.software_version == NULL)
+    void *tx = rs_ssh_state_get_tx(state, 0);
+    if (rs_ssh_tx_get_software(tx, &software, &b_len, STREAM_TOCLIENT) != 1)
+        return LuaCallbackError(luastate, "error: no server software version");
+    if (software == NULL || b_len == 0) {
         return LuaCallbackError(luastate, "error: no server software version");
+    }
 
-    return LuaPushStringBuffer(luastate, ssh_state->srv_hdr.software_version,
-                               strlen((char *)ssh_state->srv_hdr.software_version));
+    return LuaPushStringBuffer(luastate, software, b_len);
 }
 
 static int SshGetServerSoftwareVersion(lua_State *luastate)
@@ -126,13 +134,17 @@ static int GetClientProtoVersion(lua_State *luastate, const Flow *f)
     if (state == NULL)
         return LuaCallbackError(luastate, "error: no app layer state");
 
-    SshState *ssh_state = (SshState *)state;
+    const uint8_t *protocol = NULL;
+    uint32_t b_len = 0;
 
-    if (ssh_state->cli_hdr.proto_version == NULL)
+    void *tx = rs_ssh_state_get_tx(state, 0);
+    if (rs_ssh_tx_get_protocol(tx, &protocol, &b_len, STREAM_TOSERVER) != 1)
         return LuaCallbackError(luastate, "error: no client proto version");
+    if (protocol == NULL || b_len == 0) {
+        return LuaCallbackError(luastate, "error: no client proto version");
+    }
 
-    return LuaPushStringBuffer(luastate, ssh_state->cli_hdr.proto_version,
-                               strlen((char *)ssh_state->cli_hdr.proto_version));
+    return LuaPushStringBuffer(luastate, protocol, b_len);
 }
 
 static int SshGetClientProtoVersion(lua_State *luastate)
@@ -157,13 +169,17 @@ static int GetClientSoftwareVersion(lua_State *luastate, const Flow *f)
     if (state == NULL)
         return LuaCallbackError(luastate, "error: no app layer state");
 
-    SshState *ssh_state = (SshState *)state;
+    const uint8_t *software = NULL;
+    uint32_t b_len = 0;
 
-    if (ssh_state->cli_hdr.software_version == NULL)
+    void *tx = rs_ssh_state_get_tx(state, 0);
+    if (rs_ssh_tx_get_software(tx, &software, &b_len, STREAM_TOSERVER) != 1)
+        return LuaCallbackError(luastate, "error: no client software version");
+    if (software == NULL || b_len == 0) {
         return LuaCallbackError(luastate, "error: no client software version");
+    }
 
-    return LuaPushStringBuffer(luastate, ssh_state->cli_hdr.software_version,
-                               strlen((char *)ssh_state->cli_hdr.software_version));
+    return LuaPushStringBuffer(luastate, software, b_len);
 }
 
 static int SshGetClientSoftwareVersion(lua_State *luastate)