]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
proto-detect: improve midstream support
authorVictor Julien <victor@inliniac.net>
Thu, 21 Mar 2019 12:57:50 +0000 (13:57 +0100)
committerVictor Julien <victor@inliniac.net>
Wed, 27 Mar 2019 10:12:13 +0000 (11:12 +0100)
When Suricata picks up a flow it assumes the first packet is
toserver. In a perfect world without packet loss and where all
sessions neatly start after Suricata itself started, this would be
true. However, in reality we have to account for packet loss and
Suricata starting to get packets for flows already active be for
Suricata is (re)started.

The protocol records on the wire would often be able to tell us more
though. For example in SMB1 and SMB2 records there is a flag that
indicates whether the record is a request or a response. This patch
is enabling the procotol detection engine to utilize this information
to 'reverse' the flow.

There are three ways in which this is supported in this patch:

1. patterns for detection are registered per direction. If the proto
   was not recognized in the traffic direction, and midstream is
   enabled, the pattern set for the opposing direction is also
   evaluated. If that matches, the flow is considered to be in the
   wrong direction and is reversed.

2. probing parsers now have a way to feed back their understanding
   of the flow direction. They are now passed the direction as
   Suricata sees the traffic when calling the probing parsers. The
   parser can then see if its own observation matches that, and
   pass back it's own view to the caller.

3. a new pattern + probing parser set up: probing parsers can now
   be registered with a pattern, so that when the pattern matches
   the probing parser is called as well. The probing parser can
   then provide the protocol detection engine with the direction
   of the traffic.

The process of reversing takes a multi step approach as well:

a. reverse the current packets direction
b. reverse most of the flows direction sensitive flags
c. tag the flow as 'reversed'. This is because the 5 tuple is
   *not* reversed, since it is immutable after the flows creation.

Most of the currently registered parsers benefit already:

- HTTP/SMTP/FTP/TLS patterns are registered per direction already
  so they will benefit from the pattern midstream logic in (1)
  above.

- the Rust based SMB parser uses a mix of pattern + probing parser
  as described in (3) above.

- the NFS detection is purely done by probing parser and is updated
  to consider the direction in that parser.

Other protocols, such as DNS, are still to do.

Ticket: #2572

27 files changed:
rust/src/applayertemplate/template.rs
rust/src/dhcp/dhcp.rs
rust/src/ikev2/ikev2.rs
rust/src/krb/krb5.rs
rust/src/nfs/nfs.rs
rust/src/ntp/ntp.rs
rust/src/parser.rs
rust/src/smb/smb.rs
rust/src/smb/smb2_records.rs
src/app-layer-detect-proto.c
src/app-layer-detect-proto.h
src/app-layer-dnp3.c
src/app-layer-dns-tcp-rust.c
src/app-layer-dns-tcp.c
src/app-layer-dns-udp-rust.c
src/app-layer-dns-udp.c
src/app-layer-enip.c
src/app-layer-modbus.c
src/app-layer-nfs-tcp.c
src/app-layer-nfs-udp.c
src/app-layer-protos.h
src/app-layer-smb-tcp-rust.c
src/app-layer-smb.c
src/app-layer-ssl.c
src/app-layer-template.c
src/app-layer-tftp.c
src/app-layer.c

index 2c96a29f7a6f2f9a674167a785aba5522207e558..fdfab3cd5a68937608682cf62f318ccad0192219 100644 (file)
@@ -257,8 +257,10 @@ export_tx_set_detect_state!(
 #[no_mangle]
 pub extern "C" fn rs_template_probing_parser(
     _flow: *const Flow,
+    _direction: u8,
     input: *const libc::uint8_t,
     input_len: u32,
+    _rdir: *mut u8
 ) -> AppProto {
     // Need at least 2 bytes.
     if input_len > 1 && input != std::ptr::null_mut() {
index 5139ec6a02559f2c290dc84e2764ca1ed8a7ad6c..5f22f8c2bc01a97366e25d146289da85e03deea2 100644 (file)
@@ -219,8 +219,11 @@ impl DHCPState {
 
 #[no_mangle]
 pub extern "C" fn rs_dhcp_probing_parser(_flow: *const Flow,
+                                         _direction: u8,
                                          input: *const libc::uint8_t,
-                                         input_len: u32) -> AppProto {
+                                         input_len: u32,
+                                         _rdir: *mut u8) -> AppProto
+{
     if input_len < DHCP_MIN_FRAME_LEN {
         return ALPROTO_UNKNOWN;
     }
index dfff4f10b5aaf93fa4dc499238c90765f7a29ac4..dcbf581f26a73a2388a9e6c88958c04a993a2929 100644 (file)
@@ -624,7 +624,11 @@ pub extern "C" fn rs_ikev2_state_get_event_info(event_name: *const libc::c_char,
 static mut ALPROTO_IKEV2 : AppProto = ALPROTO_UNKNOWN;
 
 #[no_mangle]
-pub extern "C" fn rs_ikev2_probing_parser(_flow: *const Flow, input:*const libc::uint8_t, input_len: u32) -> AppProto {
+pub extern "C" fn rs_ikev2_probing_parser(_flow: *const Flow,
+        _direction: u8,
+        input:*const libc::uint8_t, input_len: u32,
+        _rdir: *mut u8) -> AppProto
+{
     let slice = build_slice!(input,input_len as usize);
     let alproto = unsafe{ ALPROTO_IKEV2 };
     match parse_ikev2_header(slice) {
index 65d3a4ce51da15c591100f2de4a88c520361af43..bab1e584eca5d678db53f5b9e82ff4ea37847407 100644 (file)
@@ -405,7 +405,11 @@ pub extern "C" fn rs_krb5_state_get_event_info(event_name: *const libc::c_char,
 static mut ALPROTO_KRB5 : AppProto = ALPROTO_UNKNOWN;
 
 #[no_mangle]
-pub extern "C" fn rs_krb5_probing_parser(_flow: *const Flow, input:*const libc::uint8_t, input_len: u32) -> AppProto {
+pub extern "C" fn rs_krb5_probing_parser(_flow: *const Flow,
+        _direction: u8,
+        input:*const libc::uint8_t, input_len: u32,
+        _rdir: *mut u8) -> AppProto
+{
     let slice = build_slice!(input,input_len as usize);
     let alproto = unsafe{ ALPROTO_KRB5 };
     if slice.len() <= 10 { return unsafe{ALPROTO_FAILED}; }
@@ -439,14 +443,19 @@ pub extern "C" fn rs_krb5_probing_parser(_flow: *const Flow, input:*const libc::
 }
 
 #[no_mangle]
-pub extern "C" fn rs_krb5_probing_parser_tcp(_flow: *const Flow, input:*const libc::uint8_t, input_len: u32) -> AppProto {
+pub extern "C" fn rs_krb5_probing_parser_tcp(_flow: *const Flow,
+        direction: u8,
+        input:*const libc::uint8_t, input_len: u32,
+        rdir: *mut u8) -> AppProto
+{
     let slice = build_slice!(input,input_len as usize);
     if slice.len() <= 14 { return unsafe{ALPROTO_FAILED}; }
     match be_u32(slice) {
         Ok((rem, record_mark)) => {
             // protocol implementations forbid very large requests
             if record_mark > 16384 { return unsafe{ALPROTO_FAILED}; }
-            return rs_krb5_probing_parser(_flow, rem.as_ptr(), rem.len() as u32);
+            return rs_krb5_probing_parser(_flow, direction,
+                    rem.as_ptr(), rem.len() as u32, rdir);
         },
         Err(nom::Err::Incomplete(_)) => {
             return ALPROTO_UNKNOWN;
index bf9efb1866ac47fec80d86387bee3f6ce4ee67d0..d50162deb2d2d09a5800d387fdcc5d3cc1ae0aee 100644 (file)
@@ -1681,6 +1681,26 @@ pub extern "C" fn rs_nfs_init(context: &'static mut SuricataFileContext)
     }
 }
 
+fn nfs_probe_dir(i: &[u8], rdir: *mut u8) -> i8 {
+    match parse_rpc_packet_header(i) {
+        Ok((_, ref hdr)) => {
+            let dir = if hdr.msgtype == 0 {
+                STREAM_TOSERVER
+            } else {
+                STREAM_TOCLIENT
+            };
+            unsafe { *rdir = dir };
+            return 1;
+        },
+        Err(nom::Err::Incomplete(_)) => {
+            return 0;
+        },
+        Err(_) => {
+            return -1;
+        },
+    }
+}
+
 pub fn nfs_probe(i: &[u8], direction: u8) -> i8 {
     if direction == STREAM_TOCLIENT {
         match parse_rpc_reply(i) {
@@ -1772,6 +1792,33 @@ pub fn nfs_probe_udp(i: &[u8], direction: u8) -> i8 {
     }
 }
 
+/// MIDSTREAM
+#[no_mangle]
+pub extern "C" fn rs_nfs_probe_ms(input: *const libc::uint8_t,
+        len: libc::uint32_t, rdir: *mut u8) -> libc::int8_t
+{
+    let slice: &[u8] = unsafe {
+        std::slice::from_raw_parts(input as *mut u8, len as usize)
+    };
+    let mut direction : u8 = 0;
+    match nfs_probe_dir(slice, &mut direction) {
+        1 => {
+            let r = nfs_probe(slice, direction);
+            if r == 1 {
+                unsafe { *rdir = direction; }
+                return 1;
+            }
+            return r;
+        },
+        0 => {
+            return 0;
+        },
+        _ => {
+            return -1;
+        }
+    }
+}
+
 /// TOSERVER probe function
 #[no_mangle]
 pub extern "C" fn rs_nfs_probe_ts(input: *const libc::uint8_t, len: libc::uint32_t)
index aeda23512c03ac995f76fb3dda1acfad2517069e..0d449f3f9525d23631c9ff74c2903d4c98e2774f 100644 (file)
@@ -343,7 +343,11 @@ pub extern "C" fn rs_ntp_state_get_event_info(event_name: *const libc::c_char,
 static mut ALPROTO_NTP : AppProto = ALPROTO_UNKNOWN;
 
 #[no_mangle]
-pub extern "C" fn ntp_probing_parser(_flow: *const Flow, input:*const u8, input_len: u32) -> AppProto {
+pub extern "C" fn ntp_probing_parser(_flow: *const Flow,
+        _direction: u8,
+        input:*const u8, input_len: u32,
+        _rdir: *mut u8) -> AppProto
+{
     let slice: &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, input_len as usize) };
     let alproto = unsafe{ ALPROTO_NTP };
     match parse_ntp(slice) {
index 90964b9695c19eab2ee65996c83dde886b951449..ab2efec6956de3a2a5b4043f73df1b458412c9d7 100644 (file)
@@ -125,7 +125,7 @@ pub type ParseFn      = extern "C" fn (flow: *const Flow,
                                        input_len: u32,
                                        data: *const c_void,
                                        flags: u8) -> i32;
-pub type ProbeFn      = extern "C" fn (flow: *const Flow,input:*const u8, input_len: u32) -> AppProto;
+pub type ProbeFn      = extern "C" fn (flow: *const Flow,direction: u8,input:*const u8, input_len: u32, rdir: *mut u8) -> AppProto;
 pub type StateAllocFn = extern "C" fn () -> *mut c_void;
 pub type StateFreeFn  = extern "C" fn (*mut c_void);
 pub type StateTxFreeFn  = extern "C" fn (*mut c_void, u64);
index 6752aa3752f4ca83a9b0ea080525e9c429e10a3d..6242d512ef3d0139a5245b1ab43ece48451bbd13 100644 (file)
@@ -1870,17 +1870,68 @@ pub extern "C" fn rs_smb_parse_response_tcp_gap(
 // probing parser
 // return 1 if found, 0 is not found
 #[no_mangle]
-pub extern "C" fn rs_smb_probe_tcp(input: *const libc::uint8_t, len: libc::uint32_t)
-                               -> libc::int8_t
+pub extern "C" fn rs_smb_probe_tcp(direction: libc::uint8_t,
+        input: *const libc::uint8_t, len: libc::uint32_t,
+        rdir: *mut libc::uint8_t)
+    -> libc::int8_t
 {
     let slice = build_slice!(input, len as usize);
     match search_smb_record(slice) {
-        Ok((_, _)) => {
+        Ok((_, ref data)) => {
             SCLogDebug!("smb found");
-            return 1;
+            match parse_smb_version(data) {
+                Ok((_, ref smb)) => {
+                    SCLogDebug!("SMB {:?}", smb);
+                    if smb.version == 0xff_u8 { // SMB1
+                        SCLogDebug!("SMBv1 record");
+                        match parse_smb_record(data) {
+                            Ok((_, ref smb_record)) => {
+                                if smb_record.flags & 0x80 != 0 {
+                                    SCLogDebug!("RESPONSE {:02x}", smb_record.flags);
+                                    if direction & STREAM_TOSERVER != 0 {
+                                        unsafe { *rdir = STREAM_TOCLIENT; }
+                                    }
+                                } else {
+                                    SCLogDebug!("REQUEST {:02x}", smb_record.flags);
+                                    if direction & STREAM_TOCLIENT != 0 {
+                                        unsafe { *rdir = STREAM_TOSERVER; }
+                                    }
+                                }
+                                return 1;
+                            },
+                            _ => { },
+                        }
+                    } else if smb.version == 0xfe_u8 { // SMB2
+                        SCLogDebug!("SMB2 record");
+                        match parse_smb2_record_direction(data) {
+                            Ok((_, ref smb_record)) => {
+                                if direction & STREAM_TOSERVER != 0 {
+                                    SCLogDebug!("direction STREAM_TOSERVER smb_record {:?}", smb_record);
+                                    if !smb_record.request {
+                                        unsafe { *rdir = STREAM_TOCLIENT; }
+                                    }
+                                } else {
+                                    SCLogDebug!("direction STREAM_TOCLIENT smb_record {:?}", smb_record);
+                                    if smb_record.request {
+                                        unsafe { *rdir = STREAM_TOSERVER; }
+                                    }
+                                }
+                            },
+                            _ => {},
+                        }
+                    }
+                    else if smb.version == 0xfd_u8 { // SMB3 transform
+                        SCLogDebug!("SMB3 record");
+                    }
+                    return 1;
+                },
+                    _ => {
+                        SCLogDebug!("smb not found in {:?}", slice);
+                    },
+            }
         },
         _ => {
-            SCLogDebug!("smb not found in {:?}", slice);
+            SCLogDebug!("no dice");
         },
     }
     match parse_nbss_record_partial(slice) {
index 910bc0a49f79bbd2f8ecb17a983167e626886375..cf4dd0680a652c49a0af61c636af3614ca11d6b5 100644 (file)
@@ -32,6 +32,21 @@ named!(pub parse_smb2_sec_blob<Smb2SecBlobRecord>,
             })
 ));
 
+#[derive(Debug,PartialEq)]
+pub struct Smb2RecordDir<> {
+    pub request: bool,
+}
+
+named!(pub parse_smb2_record_direction<Smb2RecordDir>,
+    do_parse!(
+            _server_component: tag!(b"\xfeSMB")
+        >>  _skip: take!(12)
+        >>  flags: le_u8
+        >> (Smb2RecordDir {
+                request: flags & 0x01 == 0,
+           })
+));
+
 #[derive(Debug,PartialEq)]
 pub struct Smb2Record<'a> {
     pub direction: u8,    // 0 req, 1 res
index afd4ee83d6d36c19dff7bbda4fd231158305c89a..84b671ce4ad7ecbffb3c7e8b0bc2a1164d52196c 100644 (file)
@@ -110,14 +110,18 @@ typedef struct AppLayerProtoDetectProbingParser_ {
 
 typedef struct AppLayerProtoDetectPMSignature_ {
     AppProto alproto;
+    uint8_t direction;  /**< direction for midstream */
     SigIntId id;
     /* \todo Change this into a non-pointer */
     DetectContentData *cd;
+    uint16_t pp_min_depth;
+    uint16_t pp_max_depth;
+    ProbingParserFPtr PPFunc;
     struct AppLayerProtoDetectPMSignature_ *next;
 } AppLayerProtoDetectPMSignature;
 
 typedef struct AppLayerProtoDetectPMCtx_ {
-    uint16_t max_len;
+    uint16_t pp_max_len;
     uint16_t min_len;
     MpmCtx mpm_ctx;
 
@@ -176,87 +180,105 @@ static void AppLayerProtoDetectPEGetIpprotos(AppProto alproto,
 /***** Static Internal Calls: Protocol Retrieval *****/
 
 /** \internal
- *  \brief Handle SPM search for Signature */
-static AppProto AppLayerProtoDetectPMMatchSignature(const AppLayerProtoDetectPMSignature *s,
-                                                    AppLayerProtoDetectThreadCtx *tctx,
-                                                    uint8_t *buf, uint16_t buflen,
-                                                    uint8_t ipproto)
+ *  \brief Handle SPM search for Signature
+ *  \param buflen full size of the input buffer
+ *  \param searchlen pattern matching portion of buffer */
+static AppProto AppLayerProtoDetectPMMatchSignature(
+        const AppLayerProtoDetectPMSignature *s,
+        AppLayerProtoDetectThreadCtx *tctx,
+        Flow *f, uint8_t direction,
+        uint8_t *buf, uint16_t buflen, uint16_t searchlen,
+        bool *rflow)
 {
     SCEnter();
-    AppProto proto = ALPROTO_UNKNOWN;
-    uint8_t *found = NULL;
 
-    if (s->cd->offset > buflen) {
-        SCLogDebug("s->co->offset (%"PRIu16") > buflen (%"PRIu16")",
-                   s->cd->offset, buflen);
-        goto end;
+    if (s->cd->offset > searchlen) {
+        SCLogDebug("s->co->offset (%"PRIu16") > searchlen (%"PRIu16")",
+                   s->cd->offset, searchlen);
+        SCReturnUInt(ALPROTO_UNKNOWN);
     }
-
-    if (s->cd->depth > buflen) {
-        SCLogDebug("s->co->depth (%"PRIu16") > buflen (%"PRIu16")",
-                   s->cd->depth, buflen);
-        goto end;
+    if (s->cd->depth > searchlen) {
+        SCLogDebug("s->co->depth (%"PRIu16") > searchlen (%"PRIu16")",
+                   s->cd->depth, searchlen);
+        SCReturnUInt(ALPROTO_UNKNOWN);
     }
 
     uint8_t *sbuf = buf + s->cd->offset;
-    uint16_t sbuflen = s->cd->depth - s->cd->offset;
+    uint16_t ssearchlen = s->cd->depth - s->cd->offset;
     SCLogDebug("s->co->offset (%"PRIu16") s->cd->depth (%"PRIu16")",
                s->cd->offset, s->cd->depth);
 
-    found = SpmScan(s->cd->spm_ctx, tctx->spm_thread_ctx, sbuf, sbuflen);
-    if (found != NULL)
-        proto = s->alproto;
-
- end:
-    SCReturnUInt(proto);
-}
-
-/** \internal
- *  \brief Run Pattern Sigs against buffer
- *  \param pm_results[out] AppProto array of size ALPROTO_MAX */
-static AppProto AppLayerProtoDetectPMGetProto(AppLayerProtoDetectThreadCtx *tctx,
-                                              Flow *f,
-                                              uint8_t *buf, uint16_t buflen,
-                                              uint8_t direction,
-                                              uint8_t ipproto,
-                                              AppProto *pm_results)
-{
-    SCEnter();
-
-    pm_results[0] = ALPROTO_UNKNOWN;
-
-    AppLayerProtoDetectPMCtx *pm_ctx;
-    MpmThreadCtx *mpm_tctx;
-    uint16_t pm_matches = 0;
-    uint8_t cnt;
-    uint16_t searchlen;
-
-    if (f->protomap >= FLOW_PROTO_DEFAULT)
-        return ALPROTO_UNKNOWN;
+    uint8_t *found = SpmScan(s->cd->spm_ctx, tctx->spm_thread_ctx,
+            sbuf, ssearchlen);
+    if (found == NULL) {
+        SCReturnUInt(ALPROTO_UNKNOWN);
+    }
 
-    if (direction & STREAM_TOSERVER) {
-        pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[0];
-        mpm_tctx = &tctx->mpm_tctx[f->protomap][0];
+    SCLogDebug("matching, s->direction %s, our dir %s",
+            (s->direction & STREAM_TOSERVER) ? "toserver" : "toclient",
+            (direction & STREAM_TOSERVER) ? "toserver" : "toclient");
+    if (s->PPFunc == NULL) {
+        if ((direction & (STREAM_TOSERVER|STREAM_TOCLIENT)) == s->direction) {
+            SCLogDebug("direction is correct");
+        } else {
+            SCLogDebug("direction is wrong, rflow = true");
+            *rflow = true;
+        }
+    /* validate using Probing Parser */
     } else {
-        pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[1];
-        mpm_tctx = &tctx->mpm_tctx[f->protomap][1];
+        if (s->pp_min_depth > buflen) {
+            SCLogDebug("PP can't be run yet as pp_min_depth %u > buflen %u",
+                    s->pp_min_depth, buflen);
+            SCReturnInt(ALPROTO_UNKNOWN);
+        }
+
+        uint8_t rdir = 0;
+        AppProto r = s->PPFunc(f, direction, buf, buflen, &rdir);
+        if (r == s->alproto) {
+            SCLogDebug("found %s/%u, rdir %02x reverse_flow? %s",
+                    AppProtoToString(r), r, rdir,
+                    (rdir && direction != rdir) ? "true" : "false");
+            *rflow = (rdir && direction != rdir);
+            SCReturnUInt(s->alproto);
+        } else if (r == ALPROTO_FAILED) {
+            SCReturnUInt(ALPROTO_FAILED);
+        } else {
+            /* unknown: lets see if we will try again later */
+            if (s->pp_max_depth < buflen) {
+                SCLogDebug("depth reached and answer inconclusive: fail");
+                SCReturnUInt(ALPROTO_FAILED);
+            }
+            SCReturnUInt(ALPROTO_UNKNOWN);
+        }
     }
-    if (pm_ctx->mpm_ctx.pattern_cnt == 0)
-        goto end;
+    SCReturnUInt(s->alproto);
+}
 
-    searchlen = buflen;
-    if (searchlen > pm_ctx->max_len)
-        searchlen = pm_ctx->max_len;
+/**
+ *  \retval 0 no matches
+ *  \retval -1 no matches, mpm depth reached
+ */
+static inline int PMGetProtoInspect(
+        AppLayerProtoDetectThreadCtx *tctx,
+        AppLayerProtoDetectPMCtx *pm_ctx,
+        MpmThreadCtx *mpm_tctx,
+        Flow *f, uint8_t *buf, uint16_t buflen,
+        uint8_t direction, AppProto *pm_results, bool *rflow)
+{
+    int pm_matches = 0;
 
-    uint32_t search_cnt = 0;
+    uint16_t searchlen = MIN(buflen, pm_ctx->mpm_ctx.maxdepth);
+    SCLogDebug("searchlen %u buflen %u", searchlen, buflen);
 
     /* do the mpm search */
-    search_cnt = mpm_table[pm_ctx->mpm_ctx.mpm_type].Search(&pm_ctx->mpm_ctx,
-                                                            mpm_tctx,
-                                                            &tctx->pmq,
-                                                            buf, searchlen);
-    if (search_cnt == 0)
-        goto end;
+    uint32_t search_cnt = mpm_table[pm_ctx->mpm_ctx.mpm_type].Search(
+            &pm_ctx->mpm_ctx, mpm_tctx, &tctx->pmq,
+            buf, searchlen);
+    if (search_cnt == 0) {
+        if (buflen >= pm_ctx->mpm_ctx.maxdepth)
+            return -1;
+        return 0;
+    }
 
     /* alproto bit field */
     uint8_t pm_results_bf[(ALPROTO_MAX / 8) + 1];
@@ -265,14 +287,14 @@ static AppProto AppLayerProtoDetectPMGetProto(AppLayerProtoDetectThreadCtx *tctx
     /* loop through unique pattern id's. Can't use search_cnt here,
      * as that contains all matches, tctx->pmq.pattern_id_array_cnt
      * contains only *unique* matches. */
-    for (cnt = 0; cnt < tctx->pmq.rule_id_array_cnt; cnt++) {
+    for (uint32_t cnt = 0; cnt < tctx->pmq.rule_id_array_cnt; cnt++) {
         const AppLayerProtoDetectPMSignature *s = pm_ctx->map[tctx->pmq.rule_id_array[cnt]];
         while (s != NULL) {
             AppProto proto = AppLayerProtoDetectPMMatchSignature(s,
-                    tctx, buf, searchlen, ipproto);
+                    tctx, f, direction, buf, buflen, searchlen, rflow);
 
             /* store each unique proto once */
-            if (proto != ALPROTO_UNKNOWN &&
+            if (AppProtoIsValid(proto) &&
                 !(pm_results_bf[proto / 8] & (1 << (proto % 8))) )
             {
                 pm_results[pm_matches++] = proto;
@@ -281,12 +303,99 @@ static AppProto AppLayerProtoDetectPMGetProto(AppLayerProtoDetectThreadCtx *tctx
             s = s->next;
         }
     }
-
- end:
+    if (pm_matches == 0 && buflen >= pm_ctx->pp_max_len) {
+        pm_matches = -2;
+    }
     PmqReset(&tctx->pmq);
-    if (buflen >= pm_ctx->max_len)
+    return pm_matches;
+}
+
+/** \internal
+ *  \brief Run Pattern Sigs against buffer
+ *  \param direction direction for the patterns
+ *  \param pm_results[out] AppProto array of size ALPROTO_MAX */
+static AppProto AppLayerProtoDetectPMGetProto(
+        AppLayerProtoDetectThreadCtx *tctx,
+        Flow *f, uint8_t *buf, uint16_t buflen,
+        uint8_t direction, AppProto *pm_results, bool *rflow)
+{
+    SCEnter();
+
+    pm_results[0] = ALPROTO_UNKNOWN;
+
+    AppLayerProtoDetectPMCtx *pm_ctx;
+    MpmThreadCtx *mpm_tctx;
+    int m = -1;
+
+    if (f->protomap >= FLOW_PROTO_DEFAULT)
+        return ALPROTO_FAILED;
+
+    if (direction & STREAM_TOSERVER) {
+        pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[0];
+        mpm_tctx = &tctx->mpm_tctx[f->protomap][0];
+    } else {
+        pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[1];
+        mpm_tctx = &tctx->mpm_tctx[f->protomap][1];
+    }
+    if (likely(pm_ctx->mpm_ctx.pattern_cnt > 0)) {
+        m = PMGetProtoInspect(tctx,
+                pm_ctx, mpm_tctx,
+                f, buf, buflen, direction,
+                pm_results, rflow);
+
+    }
+    /* pattern found, yay */
+    if (m > 0) {
         FLOW_SET_PM_DONE(f, direction);
-    SCReturnUInt(pm_matches);
+        SCReturnUInt((uint16_t)m);
+
+    /* handle non-found in non-midstream case */
+    } else if (!stream_config.midstream) {
+        /* we can give up if mpm gave no results and its search depth
+         * was reached. */
+        if (m < 0) {
+            FLOW_SET_PM_DONE(f, direction);
+            SCReturnUInt(0);
+        } else if (m == 0) {
+            SCReturnUInt(0);
+        }
+        SCReturnUInt((uint16_t)m);
+
+    /* handle non-found in midstream case */
+    } else if (m <= 0) {
+        if (direction & STREAM_TOSERVER) {
+            pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[1];
+            mpm_tctx = &tctx->mpm_tctx[f->protomap][1];
+        } else {
+            pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[0];
+            mpm_tctx = &tctx->mpm_tctx[f->protomap][0];
+        }
+        SCLogDebug("no matches and in midstream mode, lets try the "
+                   "*patterns for the other side");
+
+        int om = -1;
+        if (likely(pm_ctx->mpm_ctx.pattern_cnt > 0)) {
+            om = PMGetProtoInspect(tctx,
+                    pm_ctx, mpm_tctx,
+                    f, buf, buflen, direction,
+                    pm_results, rflow);
+        }
+        /* found! */
+        if (om > 0) {
+            FLOW_SET_PM_DONE(f, direction);
+            SCReturnUInt((uint16_t)om);
+
+        /* both sides failed */
+        } else if (om < 0 && m && m < 0) {
+            FLOW_SET_PM_DONE(f, direction);
+            SCReturnUInt(0);
+
+        /* one side still uncertain */
+        } else if (om == 0 || m == 0) {
+            SCReturnUInt(0);
+        }
+    }
+    SCReturnUInt(0);
 }
 
 static AppLayerProtoDetectProbingParserPort *AppLayerProtoDetectGetProbingParsers(AppLayerProtoDetectProbingParser *pp,
@@ -343,8 +452,9 @@ static AppProto AppLayerProtoDetectPEGetProto(Flow *f, uint8_t ipproto,
  *
  */
 static AppProto AppLayerProtoDetectPPGetProto(Flow *f,
-                                              uint8_t *buf, uint32_t buflen,
-                                              uint8_t ipproto, uint8_t direction)
+        uint8_t *buf, uint32_t buflen,
+        uint8_t ipproto, uint8_t direction,
+        bool *reverse_flow)
 {
     const AppLayerProtoDetectProbingParserPort *pp_port_dp = NULL;
     const AppLayerProtoDetectProbingParserPort *pp_port_sp = NULL;
@@ -411,6 +521,7 @@ static AppProto AppLayerProtoDetectPPGetProto(Flow *f,
     }
 
     /* run the parser(s) */
+    uint8_t rdir = 0;
     pe = pe1;
     while (pe != NULL) {
         if ((buflen < pe->min_depth)  ||
@@ -420,9 +531,9 @@ static AppProto AppLayerProtoDetectPPGetProto(Flow *f,
         }
 
         if (direction & STREAM_TOSERVER && pe->ProbingParserTs != NULL) {
-            alproto = pe->ProbingParserTs(f, buf, buflen);
+            alproto = pe->ProbingParserTs(f, direction, buf, buflen, &rdir);
         } else if (pe->ProbingParserTc != NULL) {
-            alproto = pe->ProbingParserTc(f, buf, buflen);
+            alproto = pe->ProbingParserTc(f, direction, buf, buflen, &rdir);
         }
         if (alproto != ALPROTO_UNKNOWN && alproto != ALPROTO_FAILED)
             goto end;
@@ -441,9 +552,9 @@ static AppProto AppLayerProtoDetectPPGetProto(Flow *f,
         }
 
         if (direction & STREAM_TOSERVER && pe->ProbingParserTs != NULL) {
-            alproto = pe->ProbingParserTs(f, buf, buflen);
+            alproto = pe->ProbingParserTs(f, direction, buf, buflen, &rdir);
         } else if (pe->ProbingParserTc != NULL) {
-            alproto = pe->ProbingParserTc(f, buf, buflen);
+            alproto = pe->ProbingParserTc(f, direction, buf, buflen, &rdir);
         }
         if (alproto != ALPROTO_UNKNOWN && alproto != ALPROTO_FAILED)
             goto end;
@@ -472,6 +583,12 @@ static AppProto AppLayerProtoDetectPPGetProto(Flow *f,
     }
 
  end:
+    if (alproto != ALPROTO_FAILED && alproto != ALPROTO_UNKNOWN &&
+        rdir == (direction & (STREAM_TOSERVER|STREAM_TOCLIENT))) {
+        SCLogDebug("PP found %u, is reverse flow", alproto);
+        *reverse_flow = true;
+    }
+
     SCLogDebug("%s, mask is now %08x",
             (direction & STREAM_TOSERVER) ? "toserver":"toclient", alproto_masks[0]);
     SCReturnUInt(alproto);
@@ -1269,45 +1386,46 @@ static void AppLayerProtoDetectPMFreeSignature(AppLayerProtoDetectPMSignature *s
 }
 
 static int AppLayerProtoDetectPMAddSignature(AppLayerProtoDetectPMCtx *ctx, DetectContentData *cd,
-                                             AppProto alproto)
+                                             AppProto alproto, uint8_t direction,
+                                             ProbingParserFPtr PPFunc,
+                                             uint16_t pp_min_depth, uint16_t pp_max_depth)
 {
     SCEnter();
 
-    int ret = 0;
-    AppLayerProtoDetectPMSignature *s = SCMalloc(sizeof(*s));
+    AppLayerProtoDetectPMSignature *s = SCCalloc(1, sizeof(*s));
     if (unlikely(s == NULL))
-        goto error;
-    memset(s, 0, sizeof(*s));
+        SCReturnInt(-1);
 
     s->alproto = alproto;
+    s->direction = direction;
     s->cd = cd;
+    s->PPFunc = PPFunc;
+    s->pp_min_depth = pp_min_depth;
+    s->pp_max_depth = pp_max_depth;
 
     /* prepend to the list */
     s->next = ctx->head;
     ctx->head = s;
 
-    goto end;
- error:
-    ret = -1;
- end:
-    SCReturnInt(ret);
+    SCReturnInt(0);
 }
 
 static int AppLayerProtoDetectPMRegisterPattern(uint8_t ipproto, AppProto alproto,
                                                 const char *pattern,
                                                 uint16_t depth, uint16_t offset,
                                                 uint8_t direction,
-                                                uint8_t is_cs)
+                                                uint8_t is_cs,
+                                                ProbingParserFPtr PPFunc,
+                                                uint16_t pp_min_depth, uint16_t pp_max_depth)
 {
     SCEnter();
 
     AppLayerProtoDetectCtxIpproto *ctx_ipp = &alpd_ctx.ctx_ipp[FlowGetProtoMapping(ipproto)];
     AppLayerProtoDetectPMCtx *ctx_pm = NULL;
-    DetectContentData *cd;
     int ret = 0;
 
-    cd = DetectContentParseEncloseQuotes(alpd_ctx.spm_global_thread_ctx,
-                                         pattern);
+    DetectContentData *cd = DetectContentParseEncloseQuotes(
+            alpd_ctx.spm_global_thread_ctx, pattern);
     if (cd == NULL)
         goto error;
     cd->depth = depth;
@@ -1330,13 +1448,14 @@ static int AppLayerProtoDetectPMRegisterPattern(uint8_t ipproto, AppProto alprot
     else
         ctx_pm = (AppLayerProtoDetectPMCtx *)&ctx_ipp->ctx_pm[1];
 
-    if (depth > ctx_pm->max_len)
-        ctx_pm->max_len = depth;
+    if (pp_max_depth > ctx_pm->pp_max_len)
+        ctx_pm->pp_max_len = pp_max_depth;
     if (depth < ctx_pm->min_len)
         ctx_pm->min_len = depth;
 
     /* Finally turn it into a signature and add to the ctx. */
-    AppLayerProtoDetectPMAddSignature(ctx_pm, cd, alproto);
+    AppLayerProtoDetectPMAddSignature(ctx_pm, cd, alproto, direction,
+            PPFunc, pp_min_depth, pp_max_depth);
 
     goto end;
  error:
@@ -1350,7 +1469,8 @@ static int AppLayerProtoDetectPMRegisterPattern(uint8_t ipproto, AppProto alprot
 AppProto AppLayerProtoDetectGetProto(AppLayerProtoDetectThreadCtx *tctx,
                                      Flow *f,
                                      uint8_t *buf, uint32_t buflen,
-                                     uint8_t ipproto, uint8_t direction)
+                                     uint8_t ipproto, uint8_t direction,
+                                     bool *reverse_flow)
 {
     SCEnter();
     SCLogDebug("buflen %u for %s direction", buflen,
@@ -1362,10 +1482,8 @@ AppProto AppLayerProtoDetectGetProto(AppLayerProtoDetectThreadCtx *tctx,
     if (!FLOW_IS_PM_DONE(f, direction)) {
         AppProto pm_results[ALPROTO_MAX];
         uint16_t pm_matches = AppLayerProtoDetectPMGetProto(tctx, f,
-                                                   buf, buflen,
-                                                   direction,
-                                                   ipproto,
-                                                   pm_results);
+                buf, buflen, direction,
+                pm_results, reverse_flow);
         if (pm_matches > 0) {
             alproto = pm_results[0];
 
@@ -1381,10 +1499,15 @@ AppProto AppLayerProtoDetectGetProto(AppLayerProtoDetectThreadCtx *tctx,
     }
 
     if (!FLOW_IS_PP_DONE(f, direction)) {
+        bool rflow = false;
         alproto = AppLayerProtoDetectPPGetProto(f, buf, buflen,
-                                                ipproto, direction);
+                                                ipproto, direction,
+                                                &rflow);
         if (alproto != ALPROTO_UNKNOWN)
             goto end;
+        if (rflow) {
+            *reverse_flow = true;
+        }
     }
 
     /* Look if flow can be found in expectation list */
@@ -1582,12 +1705,24 @@ int AppLayerProtoDetectPMRegisterPatternCS(uint8_t ipproto, AppProto alproto,
                                            uint8_t direction)
 {
     SCEnter();
-    int r = 0;
-    r = AppLayerProtoDetectPMRegisterPattern(ipproto, alproto,
-                                                     pattern,
-                                                     depth, offset,
-                                                     direction,
-                                                     1 /* case-sensitive */);
+    int r = AppLayerProtoDetectPMRegisterPattern(ipproto, alproto,
+            pattern, depth, offset,
+            direction, 1 /* case-sensitive */,
+            NULL, 0, 0);
+    SCReturnInt(r);
+}
+
+int AppLayerProtoDetectPMRegisterPatternCSwPP(uint8_t ipproto, AppProto alproto,
+        const char *pattern, uint16_t depth, uint16_t offset,
+        uint8_t direction,
+        ProbingParserFPtr PPFunc,
+        uint16_t pp_min_depth, uint16_t pp_max_depth)
+{
+    SCEnter();
+    int r = AppLayerProtoDetectPMRegisterPattern(ipproto, alproto,
+            pattern, depth, offset,
+            direction, 1 /* case-sensitive */,
+            PPFunc, pp_min_depth, pp_max_depth);
     SCReturnInt(r);
 }
 
@@ -1597,12 +1732,10 @@ int AppLayerProtoDetectPMRegisterPatternCI(uint8_t ipproto, AppProto alproto,
                                            uint8_t direction)
 {
     SCEnter();
-    int r = 0;
-    r = AppLayerProtoDetectPMRegisterPattern(ipproto, alproto,
-                                                     pattern,
-                                                     depth, offset,
-                                                     direction,
-                                                     0 /* !case-sensitive */);
+    int r = AppLayerProtoDetectPMRegisterPattern(ipproto, alproto,
+            pattern, depth, offset,
+            direction, 0 /* !case-sensitive */,
+            NULL, 0, 0);
     SCReturnInt(r);
 }
 
@@ -1983,32 +2116,18 @@ static int AppLayerProtoDetectTest01(void)
     AppLayerProtoDetectUnittestCtxBackup();
     AppLayerProtoDetectSetup();
 
-    const char *buf;
-    int r = 0;
-
-    buf = "HTTP";
+    const char *buf = "HTTP";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
     buf = "GET";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOSERVER);
 
     AppLayerProtoDetectPrepareState();
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 1) {
-        printf("Failure - "
-               "alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 1\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
-        printf("Failure - "
-               "alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 1);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1);
 
-    r = 1;
-
- end:
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 static int AppLayerProtoDetectTest02(void)
@@ -2016,47 +2135,24 @@ static int AppLayerProtoDetectTest02(void)
     AppLayerProtoDetectUnittestCtxBackup();
     AppLayerProtoDetectSetup();
 
-    int r = 0;
-
     const char *buf = "HTTP";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
     buf = "ftp";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
 
     AppLayerProtoDetectPrepareState();
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_FTP\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1].alproto != ALPROTO_HTTP\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL);
 
-    r = 1;
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP);
 
- end:
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 static int AppLayerProtoDetectTest03(void)
@@ -2065,18 +2161,14 @@ static int AppLayerProtoDetectTest03(void)
     AppLayerProtoDetectSetup();
 
     uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n";
-    const char *buf;
-    int r = 0;
-    Flow f;
     AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
-
+    memset(pm_results, 0, sizeof(pm_results));
+    Flow f;
     memset(&f, 0x00, sizeof(f));
     f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
 
-    memset(pm_results, 0, sizeof(pm_results));
 
-    buf = "HTTP";
+    const char *buf = "HTTP";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
     buf = "220 ";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
@@ -2084,54 +2176,28 @@ static int AppLayerProtoDetectTest03(void)
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
-        goto end;
-    }
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    FAIL_IF_NULL(alpd_tctx);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_FTP\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1].alproto != ALPROTO_HTTP\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP);
 
+    bool rflow = false;
     uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                                 &f,
-                                                 l7data, sizeof(l7data),
+                                                 &f, l7data, sizeof(l7data),
                                                  STREAM_TOCLIENT,
-                                                 IPPROTO_TCP,
-                                                 pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
-        printf("cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
-        goto end;
-    }
-
-    r = 1;
+                                                 pm_results, &rflow);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_HTTP);
 
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 static int AppLayerProtoDetectTest04(void)
@@ -2140,67 +2206,38 @@ static int AppLayerProtoDetectTest04(void)
     AppLayerProtoDetectSetup();
 
     uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n";
-    const char *buf;
-    int r = 0;
     Flow f;
-    AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
-
     memset(&f, 0x00, sizeof(f));
-    f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
-
+    AppProto pm_results[ALPROTO_MAX];
     memset(pm_results, 0, sizeof(pm_results));
+    f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
 
-    buf = "200 ";
+    const char *buf = "200 ";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 13, 0, STREAM_TOCLIENT);
 
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    FAIL_IF_NULL(alpd_tctx);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_HTTP\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP);
 
+    bool rdir = false;
     uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                                 &f,
-                                                 l7data, sizeof(l7data),
-                                                 STREAM_TOCLIENT,
-                                                 IPPROTO_TCP,
-                                                 pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
-        printf("cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
-        goto end;
-    }
+            &f, l7data, sizeof(l7data), STREAM_TOCLIENT,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_HTTP);
 
-    r = 1;
-
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 static int AppLayerProtoDetectTest05(void)
@@ -2209,18 +2246,13 @@ static int AppLayerProtoDetectTest05(void)
     AppLayerProtoDetectSetup();
 
     uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n<HTML><BODY>Blahblah</BODY></HTML>";
-    const char *buf;
-    int r = 0;
-    Flow f;
     AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
-
+    memset(pm_results, 0, sizeof(pm_results));
+    Flow f;
     memset(&f, 0x00, sizeof(f));
     f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
 
-    memset(pm_results, 0, sizeof(pm_results));
-
-    buf = "HTTP";
+    const char *buf = "HTTP";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
     buf = "220 ";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
@@ -2228,54 +2260,28 @@ static int AppLayerProtoDetectTest05(void)
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    FAIL_IF_NULL(alpd_tctx);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_FTP\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1].alproto != ALPROTO_HTTP\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP);
 
+    bool rdir = false;
     uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                                 &f,
-                                                 l7data, sizeof(l7data),
-                                                 STREAM_TOCLIENT,
-                                                 IPPROTO_TCP,
-                                                 pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
-        printf("cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
-        goto end;
-    }
+            &f, l7data, sizeof(l7data),
+            STREAM_TOCLIENT,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_HTTP);
 
-    r = 1;
-
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 static int AppLayerProtoDetectTest06(void)
@@ -2284,16 +2290,13 @@ static int AppLayerProtoDetectTest06(void)
     AppLayerProtoDetectSetup();
 
     uint8_t l7data[] = "220 Welcome to the OISF FTP server\r\n";
-    const char *buf;
-    int r = 0;
-    Flow f;
     AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
-
+    memset(pm_results, 0, sizeof(pm_results));
+    Flow f;
     memset(&f, 0x00, sizeof(f));
     f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
 
-    buf = "HTTP";
+    const char *buf = "HTTP";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
     buf = "220 ";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
@@ -2301,54 +2304,27 @@ static int AppLayerProtoDetectTest06(void)
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
-        goto end;
-    }
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    FAIL_IF_NULL(alpd_tctx);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_FTP\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1].alproto != ALPROTO_HTTP\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP);
 
+    bool rdir = false;
     uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                                 &f,
-                                                 l7data, sizeof(l7data),
-                                                 STREAM_TOCLIENT,
-                                                 IPPROTO_TCP,
-                                                 pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_FTP) {
-        printf("cnt != 1 && pm_results[0] != AlPROTO_FTP\n");
-        goto end;
-    }
+            &f, l7data, sizeof(l7data), STREAM_TOCLIENT,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_FTP);
 
-    r = 1;
-
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 static int AppLayerProtoDetectTest07(void)
@@ -2357,67 +2333,36 @@ static int AppLayerProtoDetectTest07(void)
     AppLayerProtoDetectSetup();
 
     uint8_t l7data[] = "220 Welcome to the OISF HTTP/FTP server\r\n";
-    const char *buf;
-    int r = 0;
     Flow f;
-    AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
-
     memset(&f, 0x00, sizeof(f));
     f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
-
+    AppProto pm_results[ALPROTO_MAX];
     memset(pm_results, 0, sizeof(pm_results));
 
-    buf = "HTTP";
+    const char *buf = "HTTP";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
 
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
-        goto end;
-    }
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_HTTP\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP);
 
+    bool rdir = false;
     uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                                 &f,
-                                                 l7data, sizeof(l7data),
-                                                 STREAM_TOCLIENT,
-                                                 IPPROTO_TCP,
-                                                 pm_results);
-    if (cnt != 0) {
-        printf("cnt != 0\n");
-        goto end;
-    }
+            &f, l7data, sizeof(l7data), STREAM_TOCLIENT,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 0);
 
-    r = 1;
-
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 static int AppLayerProtoDetectTest08(void)
@@ -2445,67 +2390,38 @@ static int AppLayerProtoDetectTest08(void)
         0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32,
         0x00
     };
-    const char *buf;
-    int r = 0;
-    Flow f;
     AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
-
+    memset(pm_results, 0, sizeof(pm_results));
+    Flow f;
     memset(&f, 0x00, sizeof(f));
     f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
 
-    memset(pm_results, 0, sizeof(pm_results));
-
-    buf = "|ff|SMB";
+    const char *buf = "|ff|SMB";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB, buf, 8, 4, STREAM_TOCLIENT);
 
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    FAIL_IF_NULL(alpd_tctx);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_SMB) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_SMB\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_SMB);
 
+    bool rdir = false;
     uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                                 &f,
-                                                 l7data, sizeof(l7data),
-                                                 STREAM_TOCLIENT,
-                                                 IPPROTO_TCP,
-                                                 pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_SMB) {
-        printf("cnt != 1 && pm_results[0] != AlPROTO_SMB\n");
-        goto end;
-    }
+            &f, l7data, sizeof(l7data), STREAM_TOCLIENT,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_SMB);
 
-    r = 1;
-
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 static int AppLayerProtoDetectTest09(void)
@@ -2529,67 +2445,38 @@ static int AppLayerProtoDetectTest09(void)
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x02, 0x02
     };
-    const char *buf;
-    int r = 0;
-    Flow f;
     AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
-
+    memset(pm_results, 0, sizeof(pm_results));
+    Flow f;
     memset(&f, 0x00, sizeof(f));
     f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
 
-    memset(pm_results, 0, sizeof(pm_results));
-
-    buf = "|fe|SMB";
+    const char *buf = "|fe|SMB";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB2, buf, 8, 4, STREAM_TOCLIENT);
 
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
-        goto end;
-    }
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    FAIL_IF_NULL(alpd_tctx);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_SMB2) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_SMB2\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_SMB2);
 
+    bool rdir = false;
     uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                                 &f,
-                                                 l7data, sizeof(l7data),
-                                                 STREAM_TOCLIENT,
-                                                 IPPROTO_TCP,
-                                                 pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_SMB2) {
-        printf("cnt != 1 && pm_results[0] != AlPROTO_SMB2\n");
-        goto end;
-    }
-
-    r = 1;
+            &f, l7data, sizeof(l7data), STREAM_TOCLIENT,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_SMB2);
 
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 static int AppLayerProtoDetectTest10(void)
@@ -2608,67 +2495,38 @@ static int AppLayerProtoDetectTest10(void)
         0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
         0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00
     };
-    const char *buf;
-    int r = 0;
-    Flow f;
     AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
-
+    memset(pm_results, 0, sizeof(pm_results));
+    Flow f;
     memset(&f, 0x00, sizeof(f));
     f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
 
-    memset(pm_results, 0, sizeof(pm_results));
-
-    buf = "|05 00|";
+    const char *buf = "|05 00|";
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_DCERPC, buf, 4, 0, STREAM_TOCLIENT);
 
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    FAIL_IF_NULL(alpd_tctx);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_DCERPC) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_DCERPC\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_DCERPC);
 
+    bool rdir = false;
     uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                                 &f,
-                                                 l7data, sizeof(l7data),
-                                                 STREAM_TOCLIENT,
-                                                 IPPROTO_TCP,
-                                                 pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_DCERPC) {
-        printf("cnt != 1 && pm_results[0] != AlPROTO_DCERPC\n");
-        goto end;
-    }
+            &f, l7data, sizeof(l7data), STREAM_TOCLIENT,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_DCERPC);
 
-    r = 1;
-
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 /**
@@ -2682,16 +2540,12 @@ static int AppLayerProtoDetectTest11(void)
 
     uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n";
     uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n";
-    int r = 0;
-    Flow f;
     AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
-
+    memset(pm_results, 0, sizeof(pm_results));
+    Flow f;
     memset(&f, 0x00, sizeof(f));
     f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
 
-    memset(pm_results, 0, sizeof(pm_results));
-
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOSERVER);
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "GET", 3, 0, STREAM_TOSERVER);
     AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, "PUT", 3, 0, STREAM_TOSERVER);
@@ -2704,71 +2558,41 @@ static int AppLayerProtoDetectTest11(void)
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 7) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 7\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
-        goto end;
-    }
-
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP)
-        {
-            printf("failure 1\n");
-            goto end;
-        }
-
-    memset(pm_results, 0, sizeof(pm_results));
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    FAIL_IF_NULL(alpd_tctx);
+
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 7);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map == NULL);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL);
+
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP);
+
+    bool rdir = false;
     uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                                 &f,
-                                                 l7data, sizeof(l7data),
-                                                 STREAM_TOSERVER,
-                                                 IPPROTO_TCP,
-                                                 pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
-        printf("l7data - cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
-        goto end;
-    }
+            &f, l7data, sizeof(l7data), STREAM_TOSERVER,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_HTTP);
 
     memset(pm_results, 0, sizeof(pm_results));
     cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                        &f,
-                                        l7data_resp, sizeof(l7data_resp),
-                                        STREAM_TOCLIENT,
-                                        IPPROTO_TCP,
-                                        pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
-        printf("l7data_resp - cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
-        goto end;
-    }
+            &f, l7data_resp, sizeof(l7data_resp), STREAM_TOCLIENT,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_HTTP);
 
-    r = 1;
-
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 /**
@@ -2832,12 +2656,9 @@ static int AppLayerProtoDetectTest13(void)
 
     uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n";
     uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n";
-    int r = 0;
-    Flow f;
     AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
-    uint32_t cnt;
 
+    Flow f;
     memset(&f, 0x00, sizeof(f));
     f.protomap = FlowGetProtoMapping(IPPROTO_TCP);
 
@@ -2853,62 +2674,37 @@ static int AppLayerProtoDetectTest13(void)
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP)
-        {
-            printf("failure 1\n");
-            goto end;
-        }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP);
 
     memset(pm_results, 0, sizeof(pm_results));
-    cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                        &f,
-                                        l7data, sizeof(l7data),
-                                        STREAM_TOSERVER,
-                                        IPPROTO_TCP,
-                                        pm_results);
-    if (cnt != 0) {
-        printf("l7data - cnt != 0\n");
-        goto end;
-    }
+    bool rdir = false;
+    uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
+            &f, l7data, sizeof(l7data), STREAM_TOSERVER,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 0);
 
     memset(pm_results, 0, sizeof(pm_results));
     cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                        &f,
-                                        l7data_resp, sizeof(l7data_resp),
-                                        STREAM_TOCLIENT,
-                                        IPPROTO_TCP,
-                                        pm_results);
-    if (cnt != 0) {
-        printf("l7data_resp - cnt != 0\n");
-        goto end;
-    }
+            &f, l7data_resp, sizeof(l7data_resp), STREAM_TOCLIENT,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 0);
 
-    r = 1;
-
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 /**
@@ -2923,12 +2719,9 @@ static int AppLayerProtoDetectTest14(void)
 
     uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n";
     uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n";
-    int r = 0;
-    Flow f;
     AppProto pm_results[ALPROTO_MAX];
-    AppLayerProtoDetectThreadCtx *alpd_tctx;
     uint32_t cnt;
-
+    Flow f;
     memset(&f, 0x00, sizeof(f));
     f.protomap = FlowGetProtoMapping(IPPROTO_UDP);
 
@@ -2944,62 +2737,40 @@ static int AppLayerProtoDetectTest14(void)
     AppLayerProtoDetectPrepareState();
     /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
      * it sets internal structures which depends on the above function. */
-    alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread();
+    FAIL_IF_NULL(alpd_tctx);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7\n");
-        goto end;
-    }
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1) {
-        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1\n");
-        goto end;
-    }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1);
 
-    if (alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP ||
-        alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP)
-        {
-            printf("failure 1\n");
-            goto end;
-        }
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP);
+    FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP);
 
     memset(pm_results, 0, sizeof(pm_results));
+    bool rdir = false;
     cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                        &f,
-                                        l7data, sizeof(l7data),
-                                        STREAM_TOSERVER,
-                                        IPPROTO_UDP,
-                                        pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
-        printf("l7data - cnt != 0\n");
-        goto end;
-    }
+            &f, l7data, sizeof(l7data), STREAM_TOSERVER,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_HTTP);
 
     memset(pm_results, 0, sizeof(pm_results));
     cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
-                                        &f,
-                                        l7data_resp, sizeof(l7data_resp),
-                                        STREAM_TOCLIENT,
-                                        IPPROTO_UDP,
-                                        pm_results);
-    if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
-        printf("l7data_resp - cnt != 0\n");
-        goto end;
-    }
+            &f, l7data_resp, sizeof(l7data_resp), STREAM_TOCLIENT,
+            pm_results, &rdir);
+    FAIL_IF(cnt != 1);
+    FAIL_IF(pm_results[0] != ALPROTO_HTTP);
 
-    r = 1;
-
- end:
-    if (alpd_tctx != NULL)
-        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
+    AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
     AppLayerProtoDetectDeSetup();
     AppLayerProtoDetectUnittestCtxRestore();
-    return r;
+    PASS;
 }
 
 typedef struct AppLayerProtoDetectPPTestDataElement_ {
@@ -3121,9 +2892,9 @@ static int AppLayerProtoDetectPPTestData(AppLayerProtoDetectProbingParser *pp,
     return result;
 }
 
-static uint16_t ProbingParserDummyForTesting(Flow *f,
+static uint16_t ProbingParserDummyForTesting(Flow *f, uint8_t direction,
                                              uint8_t *input,
-                                             uint32_t input_len)
+                                             uint32_t input_len, uint8_t *rdir)
 {
     return 0;
 }
index 0f0e3a97d539f375d605a498c5180c059f070fae..779f50a8838b02a11525781bd8dcb98473601221 100644 (file)
@@ -27,8 +27,9 @@
 
 typedef struct AppLayerProtoDetectThreadCtx_ AppLayerProtoDetectThreadCtx;
 
-typedef AppProto (*ProbingParserFPtr)(Flow *f,
-                                      uint8_t *input, uint32_t input_len);
+typedef AppProto (*ProbingParserFPtr)(Flow *f, uint8_t dir,
+                                      uint8_t *input, uint32_t input_len,
+                                      uint8_t *rdir);
 
 /***** Protocol Retrieval *****/
 
@@ -41,13 +42,15 @@ typedef AppProto (*ProbingParserFPtr)(Flow *f,
  * \param buflen The length of the above buffer.
  * \param ipproto The ip protocol.
  * \param direction The direction bitfield - STREAM_TOSERVER/STREAM_TOCLIENT.
+ * \param[out] reverse_flow true if flow is detected to be reversed
  *
  * \retval The app layer protocol.
  */
 AppProto AppLayerProtoDetectGetProto(AppLayerProtoDetectThreadCtx *tctx,
                                      Flow *f,
                                      uint8_t *buf, uint32_t buflen,
-                                     uint8_t ipproto, uint8_t direction);
+                                     uint8_t ipproto, uint8_t direction,
+                                     bool *reverse_flow);
 
 /***** State Preparation *****/
 
@@ -84,9 +87,14 @@ int AppLayerProtoDetectPPParseConfPorts(const char *ipproto_name,
  * \brief Registers a case-sensitive pattern for protocol detection.
  */
 int AppLayerProtoDetectPMRegisterPatternCS(uint8_t ipproto, AppProto alproto,
-                                           const char *pattern,
-                                           uint16_t depth, uint16_t offset,
-                                           uint8_t direction);
+        const char *pattern, uint16_t depth, uint16_t offset,
+        uint8_t direction);
+int AppLayerProtoDetectPMRegisterPatternCSwPP(uint8_t ipproto, AppProto alproto,
+        const char *pattern, uint16_t depth, uint16_t offset,
+        uint8_t direction,
+        ProbingParserFPtr PPFunc,
+        uint16_t pp_min_depth, uint16_t pp_max_depth);
+
 /**
  * \brief Registers a case-insensitive pattern for protocol detection.
  */
index 61945a4046267edc16033d2a688c4e5ec941c01b..7f27ce4f543bc9b048330fc4f10cf87fb1cdfea0 100644 (file)
@@ -264,7 +264,9 @@ static int DNP3ContainsBanner(const uint8_t *input, uint32_t len)
 /**
  * \brief DNP3 probing parser.
  */
-static uint16_t DNP3ProbingParser(Flow *f, uint8_t *input, uint32_t len)
+static uint16_t DNP3ProbingParser(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t len,
+        uint8_t *rdir)
 {
     DNP3LinkHeader *hdr = (DNP3LinkHeader *)input;
 
@@ -2041,27 +2043,28 @@ static int DNP3ProbingParserTest(void)
         0x05, 0x64, 0x05, 0xc9, 0x03, 0x00, 0x04, 0x00,
         0xbd, 0x71
     };
+    uint8_t rdir = 0;
 
     /* Valid frame. */
-    FAIL_IF(DNP3ProbingParser(NULL, pkt, sizeof(pkt)) != ALPROTO_DNP3);
+    FAIL_IF(DNP3ProbingParser(NULL, STREAM_TOSERVER, pkt, sizeof(pkt), &rdir) != ALPROTO_DNP3);
 
     /* Send too little bytes. */
-    FAIL_IF(DNP3ProbingParser(NULL, pkt, sizeof(DNP3LinkHeader) - 1) != ALPROTO_UNKNOWN);
+    FAIL_IF(DNP3ProbingParser(NULL, STREAM_TOSERVER, pkt, sizeof(DNP3LinkHeader) - 1, &rdir) != ALPROTO_UNKNOWN);
 
     /* Bad start bytes. */
     pkt[0] = 0x06;
-    FAIL_IF(DNP3ProbingParser(NULL, pkt, sizeof(pkt)) != ALPROTO_FAILED);
+    FAIL_IF(DNP3ProbingParser(NULL, STREAM_TOSERVER, pkt, sizeof(pkt), &rdir) != ALPROTO_FAILED);
 
     /* Restore start byte. */
     pkt[0] = 0x05;
 
     /* Set the length to a value less than the minimum length of 5. */
     pkt[2] = 0x03;
-    FAIL_IF(DNP3ProbingParser(NULL, pkt, sizeof(pkt)) != ALPROTO_FAILED);
+    FAIL_IF(DNP3ProbingParser(NULL, STREAM_TOSERVER, pkt, sizeof(pkt), &rdir) != ALPROTO_FAILED);
 
     /* Send a banner. */
     char mybanner[] = "Welcome to DNP3 SCADA.";
-    FAIL_IF(DNP3ProbingParser(NULL, (uint8_t *)mybanner, sizeof(mybanner)) != ALPROTO_DNP3);
+    FAIL_IF(DNP3ProbingParser(NULL, STREAM_TOSERVER, (uint8_t *)mybanner, sizeof(mybanner), &rdir) != ALPROTO_DNP3);
 
     PASS;
 }
index 1ecb34a04b32f8dc8103ef1ad8d9b6de16ed0f6b..85e4c61a30f30c44f0b163ede0df5a6de23a1af5 100644 (file)
@@ -52,7 +52,8 @@ static int RustDNSTCPParseResponse(Flow *f, void *state,
             local_data);
 }
 
-static uint16_t RustDNSTCPProbe(Flow *f, uint8_t *input, uint32_t len)
+static uint16_t RustDNSTCPProbe(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t len, uint8_t *rdir)
 {
     SCLogDebug("RustDNSTCPProbe");
     if (len == 0 || len < sizeof(DNSHeader)) {
index 8492de6b44e9335bebc4d78a4f291680d281e878..fcc477ee2948769d3d02a5f812b7782f7148997f 100644 (file)
@@ -63,7 +63,9 @@ struct DNSTcpHeader_ {
 } __attribute__((__packed__));
 typedef struct DNSTcpHeader_ DNSTcpHeader;
 
-static uint16_t DNSTcpProbingParser(Flow *f, uint8_t *input, uint32_t ilen);
+static uint16_t DNSTcpProbingParser(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t ilen,
+        uint8_t *rdir);
 
 /** \internal
  *  \param input_len at least enough for the DNSTcpHeader
@@ -317,7 +319,8 @@ static int DNSTCPRequestParse(Flow *f, void *dstate,
 
     /* Clear gap state. */
     if (dns_state->gap_ts) {
-        if (DNSTcpProbingParser(f, input, input_len) == ALPROTO_DNS) {
+        if (DNSTcpProbingParser(f, STREAM_TOSERVER,
+                    input, input_len, NULL) == ALPROTO_DNS) {
             SCLogDebug("New data probed as DNS, clearing gap state.");
             BufferReset(dns_state);
             dns_state->gap_ts = 0;
@@ -557,7 +560,8 @@ static int DNSTCPResponseParse(Flow *f, void *dstate,
 
     /* Clear gap state. */
     if (dns_state->gap_tc) {
-        if (DNSTcpProbingParser(f, input, input_len) == ALPROTO_DNS) {
+        if (DNSTcpProbingParser(f, STREAM_TOCLIENT,
+                    input, input_len, NULL) == ALPROTO_DNS) {
             SCLogDebug("New data probed as DNS, clearing gap state.");
             BufferReset(dns_state);
             dns_state->gap_tc = 0;
@@ -639,7 +643,9 @@ bad_data:
     SCReturnInt(-1);
 }
 
-static uint16_t DNSTcpProbingParser(Flow *f, uint8_t *input, uint32_t ilen)
+static uint16_t DNSTcpProbingParser(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t ilen,
+        uint8_t *rdir)
 {
     if (ilen == 0 || ilen < sizeof(DNSTcpHeader)) {
         SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSTcpHeader));
@@ -679,7 +685,9 @@ static uint16_t DNSTcpProbingParser(Flow *f, uint8_t *input, uint32_t ilen)
  * This is a minimal parser that just checks that the input contains enough
  * data for a TCP DNS response.
  */
-static uint16_t DNSTcpProbeResponse(Flow *f, uint8_t *input, uint32_t len)
+static uint16_t DNSTcpProbeResponse(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t len,
+        uint8_t *rdir)
 {
     if (len == 0 || len < sizeof(DNSTcpHeader)) {
         return ALPROTO_UNKNOWN;
index 0411b9febbcd8a7091b294df91dfd54a237d295a..b3e5c08ab24d0d52faa7000abed67c15fd0a752e 100644 (file)
@@ -50,7 +50,8 @@ static int RustDNSUDPParseResponse(Flow *f, void *state,
             local_data);
 }
 
-static uint16_t DNSUDPProbe(Flow *f, uint8_t *input, uint32_t len)
+static uint16_t DNSUDPProbe(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t len, uint8_t *rdir)
 {
     if (len == 0 || len < sizeof(DNSHeader)) {
         return ALPROTO_UNKNOWN;
index 9c59e63e1bf230aa8e2ab10be049fdb4862a815f..4e28133002b03fa9e91b10defe18442e44711fb1 100644 (file)
@@ -331,7 +331,8 @@ insufficient_data:
     SCReturnInt(-1);
 }
 
-static uint16_t DNSUdpProbingParser(Flow *f, uint8_t *input, uint32_t ilen)
+static uint16_t DNSUdpProbingParser(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t ilen, uint8_t *rdir)
 {
     if (ilen == 0 || ilen < sizeof(DNSHeader)) {
         SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSHeader));
index 743a84b26289a07c5bdd7d7b3af1423ca9c560de..6a377f2f28e9ae6c6b9f267085a410f4c65692f0 100644 (file)
@@ -359,7 +359,8 @@ static int ENIPParse(Flow *f, void *state, AppLayerParserState *pstate,
 
 
 
-static uint16_t ENIPProbingParser(Flow *f, uint8_t *input, uint32_t input_len)
+static uint16_t ENIPProbingParser(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t input_len, uint8_t *rdir)
 {
     // SCLogDebug("ENIPProbingParser %d", input_len);
     if (input_len < sizeof(ENIPEncapHdr))
index c945b4c579d9a5d200a0b03112ba046723665483..8e660de2c0463a7ad6799785fc1949d66ecbfe8c 100644 (file)
@@ -1427,8 +1427,10 @@ static void ModbusStateFree(void *state)
 }
 
 static uint16_t ModbusProbingParser(Flow *f,
+                                    uint8_t direction,
                                     uint8_t     *input,
-                                    uint32_t    input_len)
+                                    uint32_t    input_len,
+                                    uint8_t *rdir)
 {
     ModbusHeader *header = (ModbusHeader *) input;
 
index abd816b3eae9af0b368288af78a30ea4b148f137..45dbaa5a909c8040a68988b424cba694fd4e7968 100644 (file)
@@ -112,30 +112,21 @@ static AppLayerDecoderEvents *NFSTCPGetEvents(void *state, uint64_t id)
  * \retval ALPROTO_NFS if it looks like echo, otherwise
  *     ALPROTO_UNKNOWN.
  */
-static AppProto NFSTCPProbingParserTS(Flow *f, uint8_t *input, uint32_t input_len)
+static AppProto NFSTCPProbingParser(Flow *f,
+        uint8_t direction,
+        uint8_t *input, uint32_t input_len,
+        uint8_t *rdir)
 {
     if (input_len < NFSTCP_MIN_FRAME_LEN) {
         return ALPROTO_UNKNOWN;
     }
 
-    int8_t r = rs_nfs_probe_ts(input, input_len);
-    if (r == 1) {
-        return ALPROTO_NFS;
-    } else if (r == -1) {
-        return ALPROTO_FAILED;
-    }
-
-    SCLogDebug("Protocol not detected as ALPROTO_NFS.");
-    return ALPROTO_UNKNOWN;
-}
-
-static AppProto NFSTCPProbingParserTC(Flow *f, uint8_t *input, uint32_t input_len)
-{
-    if (input_len < NFSTCP_MIN_FRAME_LEN) {
-        return ALPROTO_UNKNOWN;
+    int8_t r = 0;
+    if (direction & STREAM_TOSERVER) {
+        r = rs_nfs_probe_ts(input, input_len);
+    } else {
+        r = rs_nfs_probe_tc(input, input_len);
     }
-
-    int8_t r = rs_nfs_probe_tc(input, input_len);
     if (r == 1) {
         return ALPROTO_NFS;
     } else if (r == -1) {
@@ -287,21 +278,22 @@ void RegisterNFSTCPParsers(void)
             SCLogDebug("Unittest mode, registering default configuration.");
             AppLayerProtoDetectPPRegister(IPPROTO_TCP, NFSTCP_DEFAULT_PORT,
                 ALPROTO_NFS, 0, NFSTCP_MIN_FRAME_LEN, STREAM_TOSERVER,
-                NFSTCPProbingParserTS, NFSTCPProbingParserTC);
+                NFSTCPProbingParser, NFSTCPProbingParser);
 
         }
         else {
 
             if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
                     proto_name, ALPROTO_NFS, 0, NFSTCP_MIN_FRAME_LEN,
-                    NFSTCPProbingParserTS, NFSTCPProbingParserTC)) {
+                    NFSTCPProbingParser, NFSTCPProbingParser)) {
                 SCLogDebug("No NFSTCP app-layer configuration, enabling NFSTCP"
                     " detection TCP detection on port %s.",
                     NFSTCP_DEFAULT_PORT);
+                /* register 'midstream' probing parsers if midstream is enabled. */
                 AppLayerProtoDetectPPRegister(IPPROTO_TCP,
                     NFSTCP_DEFAULT_PORT, ALPROTO_NFS, 0,
                     NFSTCP_MIN_FRAME_LEN, STREAM_TOSERVER,
-                    NFSTCPProbingParserTS, NFSTCPProbingParserTC);
+                    NFSTCPProbingParser, NFSTCPProbingParser);
             }
 
         }
index 50cce89790483c3f0581541fb3ae9bd095aceb3d..5cc58965a3137d91886bb95f7438c3cfb355ebbf 100644 (file)
@@ -109,7 +109,8 @@ static AppLayerDecoderEvents *NFSGetEvents(void *state, uint64_t id)
  * \retval ALPROTO_NFS if it looks like echo, otherwise
  *     ALPROTO_UNKNOWN.
  */
-static AppProto NFSProbingParserTS(Flow *f, uint8_t *input, uint32_t input_len)
+static AppProto NFSProbingParser(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t input_len, uint8_t *rdir)
 {
     SCLogDebug("probing");
     if (input_len < NFS_MIN_FRAME_LEN) {
@@ -117,28 +118,12 @@ static AppProto NFSProbingParserTS(Flow *f, uint8_t *input, uint32_t input_len)
         return ALPROTO_UNKNOWN;
     }
 
-    int8_t r = rs_nfs_probe_udp_ts(input, input_len);
-    if (r == 1) {
-        SCLogDebug("nfs");
-        return ALPROTO_NFS;
-    } else if (r == -1) {
-        SCLogDebug("failed");
-        return ALPROTO_FAILED;
-    }
-
-    SCLogDebug("Protocol not detected as ALPROTO_NFS.");
-    return ALPROTO_UNKNOWN;
-}
-
-static AppProto NFSProbingParserTC(Flow *f, uint8_t *input, uint32_t input_len)
-{
-    SCLogDebug("probing");
-    if (input_len < NFS_MIN_FRAME_LEN) {
-        SCLogDebug("unknown");
-        return ALPROTO_UNKNOWN;
-    }
+    int8_t r = 0;
+    if (direction & STREAM_TOSERVER)
+        r = rs_nfs_probe_udp_ts(input, input_len);
+    else
+        r = rs_nfs_probe_udp_tc(input, input_len);
 
-    int8_t r = rs_nfs_probe_tc(input, input_len);
     if (r == 1) {
         SCLogDebug("nfs");
         return ALPROTO_NFS;
@@ -280,21 +265,21 @@ void RegisterNFSUDPParsers(void)
             SCLogDebug("Unittest mode, registering default configuration.");
             AppLayerProtoDetectPPRegister(IPPROTO_UDP, NFS_DEFAULT_PORT,
                 ALPROTO_NFS, 0, NFS_MIN_FRAME_LEN, STREAM_TOSERVER,
-                NFSProbingParserTS, NFSProbingParserTC);
+                NFSProbingParser, NFSProbingParser);
 
         }
         else {
 
             if (!AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP,
                     proto_name, ALPROTO_NFS, 0, NFS_MIN_FRAME_LEN,
-                    NFSProbingParserTS, NFSProbingParserTC)) {
+                    NFSProbingParser, NFSProbingParser)) {
                 SCLogDebug("No NFS app-layer configuration, enabling NFS"
                     " detection TCP detection on port %s.",
                     NFS_DEFAULT_PORT);
                 AppLayerProtoDetectPPRegister(IPPROTO_UDP,
                     NFS_DEFAULT_PORT, ALPROTO_NFS, 0,
                     NFS_MIN_FRAME_LEN, STREAM_TOSERVER,
-                    NFSProbingParserTS, NFSProbingParserTC);
+                    NFSProbingParser, NFSProbingParser);
             }
 
         }
index 855a1b339751297c50d16df9d47ceb20fc921935..0d1d8bff6b792a2c33d4a6a6cd20ab438bcba4d2 100644 (file)
@@ -68,6 +68,11 @@ enum AppProtoEnum {
 /* not using the enum as that is a unsigned int, so 4 bytes */
 typedef uint16_t AppProto;
 
+static inline bool AppProtoIsValid(AppProto a)
+{
+    return ((a > ALPROTO_UNKNOWN && a < ALPROTO_FAILED));
+}
+
 /**
  * \brief Maps the ALPROTO_*, to its string equivalent.
  *
index 3681c124beb69b569defa00e4435ea265982ffee..176af62e9af3abb159975baeb200b8aa5df19358 100644 (file)
@@ -78,8 +78,8 @@ static int RustSMBTCPParseResponse(Flow *f, void *state,
     return res;
 }
 
-static uint16_t RustSMBTCPProbe(Flow *f,
-        uint8_t *input, uint32_t len)
+static uint16_t RustSMBTCPProbe(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t len, uint8_t *rdir)
 {
     SCLogDebug("RustSMBTCPProbe");
 
@@ -87,7 +87,7 @@ static uint16_t RustSMBTCPProbe(Flow *f,
         return ALPROTO_UNKNOWN;
     }
 
-    const int r = rs_smb_probe_tcp(input, len);
+    const int r = rs_smb_probe_tcp(direction, input, len, rdir);
     switch (r) {
         case 1:
             return ALPROTO_SMB;
@@ -184,22 +184,28 @@ static int RustSMBRegisterPatternsForProtocolDetection(void)
 {
     int r = 0;
     /* SMB1 */
-    r |= AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB,
-            "|ff|SMB", 8, 4, STREAM_TOSERVER);
-    r |= AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB,
-            "|ff|SMB", 8, 4, STREAM_TOCLIENT);
+    r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_SMB,
+            "|ff|SMB", 8, 4, STREAM_TOSERVER, RustSMBTCPProbe,
+            MIN_REC_SIZE, MIN_REC_SIZE);
+    r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_SMB,
+            "|ff|SMB", 8, 4, STREAM_TOCLIENT, RustSMBTCPProbe,
+            MIN_REC_SIZE, MIN_REC_SIZE);
 
     /* SMB2/3 */
-    r |= AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB,
-            "|fe|SMB", 8, 4, STREAM_TOSERVER);
-    r |= AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB,
-            "|fe|SMB", 8, 4, STREAM_TOCLIENT);
+    r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_SMB,
+            "|fe|SMB", 8, 4, STREAM_TOSERVER, RustSMBTCPProbe,
+            MIN_REC_SIZE, MIN_REC_SIZE);
+    r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_SMB,
+            "|fe|SMB", 8, 4, STREAM_TOCLIENT, RustSMBTCPProbe,
+            MIN_REC_SIZE, MIN_REC_SIZE);
 
     /* SMB3 encrypted records */
-    r |= AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB,
-            "|fd|SMB", 8, 4, STREAM_TOSERVER);
-    r |= AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB,
-            "|fd|SMB", 8, 4, STREAM_TOCLIENT);
+    r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_SMB,
+            "|fd|SMB", 8, 4, STREAM_TOSERVER, RustSMBTCPProbe,
+            MIN_REC_SIZE, MIN_REC_SIZE);
+    r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_SMB,
+            "|fd|SMB", 8, 4, STREAM_TOCLIENT, RustSMBTCPProbe,
+            MIN_REC_SIZE, MIN_REC_SIZE);
     return r == 0 ? 0 : -1;
 }
 
index 994d7a25e737b307737eed548587418e40fc37e7..3fb3dc3e7ddfb41e92c6352ba41f1af73e45a6f1 100644 (file)
@@ -1512,7 +1512,8 @@ static int SMBGetAlstateProgress(void *tx, uint8_t direction)
 
 #define SMB_PROBING_PARSER_MIN_DEPTH 8
 
-static uint16_t SMBProbingParser(Flow *f, uint8_t *input, uint32_t ilen)
+static uint16_t SMBProbingParser(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t ilen, uint8_t *rdir)
 {
     int32_t len;
     int32_t input_len = ilen;
@@ -2298,10 +2299,11 @@ static int SMBParserTest05(void)
     AppLayerProtoDetectPrepareState();
     alpd_tctx = AppLayerProtoDetectGetCtxThread();
 
+    bool reverse_flow = false;
     alproto = AppLayerProtoDetectGetProto(alpd_tctx,
                                           &f,
                                           smbbuf1, smblen1,
-                                          IPPROTO_TCP, STREAM_TOSERVER);
+                                          IPPROTO_TCP, STREAM_TOSERVER, &reverse_flow);
     if (alproto != ALPROTO_UNKNOWN) {
         printf("alproto is %"PRIu16 ".  Should be ALPROTO_UNKNOWN\n",
                alproto);
@@ -2311,7 +2313,7 @@ static int SMBParserTest05(void)
     alproto = AppLayerProtoDetectGetProto(alpd_tctx,
                                           &f,
                                           smbbuf2, smblen2,
-                                          IPPROTO_TCP, STREAM_TOSERVER);
+                                          IPPROTO_TCP, STREAM_TOSERVER, &reverse_flow);
     if (alproto != ALPROTO_SMB) {
         printf("alproto is %"PRIu16 ".  Should be ALPROTO_SMB\n",
                alproto);
@@ -2382,10 +2384,11 @@ static int SMBParserTest06(void)
     AppLayerProtoDetectPrepareState();
     alpd_tctx = AppLayerProtoDetectGetCtxThread();
 
+    bool reverse_flow = false;
     alproto = AppLayerProtoDetectGetProto(alpd_tctx,
                                           &f,
                                           smbbuf1, smblen1,
-                                          IPPROTO_TCP, STREAM_TOSERVER);
+                                          IPPROTO_TCP, STREAM_TOSERVER, &reverse_flow);
     if (alproto != ALPROTO_UNKNOWN) {
         printf("alproto is %"PRIu16 ".  Should be ALPROTO_UNKNOWN\n",
                alproto);
@@ -2395,7 +2398,7 @@ static int SMBParserTest06(void)
     alproto = AppLayerProtoDetectGetProto(alpd_tctx,
                                           &f,
                                           smbbuf2, smblen2,
-                                          IPPROTO_TCP, STREAM_TOSERVER);
+                                          IPPROTO_TCP, STREAM_TOSERVER, &reverse_flow);
     if (alproto != ALPROTO_SMB) {
         printf("alproto is %"PRIu16 ".  Should be ALPROTO_SMB\n",
                alproto);
index 94c1e86dd75a3de6d8f556d69de8b1b05bccf2bb..d73658b64d22f96e80611f510344d98172c72467 100644 (file)
@@ -2624,7 +2624,8 @@ static void SSLStateTransactionFree(void *state, uint64_t tx_id)
     /* do nothing */
 }
 
-static uint16_t SSLProbingParser(Flow *f, uint8_t *input, uint32_t ilen)
+static AppProto SSLProbingParser(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t ilen, uint8_t *rdir)
 {
     /* probably a rst/fin sending an eof */
     if (ilen == 0)
index 22f9628c839ed3f9a45f63e14f53703c062d0661..299b1ee7780ceeddc4705436506eb32cf7fd7f19 100644 (file)
@@ -194,7 +194,8 @@ static AppLayerDecoderEvents *TemplateGetEvents(void *statev, uint64_t tx_id)
  * \retval ALPROTO_TEMPLATE if it looks like template, otherwise
  *     ALPROTO_UNKNOWN.
  */
-static AppProto TemplateProbingParser(Flow *f, uint8_t *input, uint32_t input_len)
+static AppProto TemplateProbingParser(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t input_len, uint8_t *rdir)
 {
     /* Very simple test - if there is input, this is template. */
     if (input_len >= TEMPLATE_MIN_FRAME_LEN) {
index 733baadbed978bf975b6a2edac910294c3cdf7ba..737205c47b94e75a28db7e8b2496173fa4009204 100644 (file)
@@ -112,7 +112,8 @@ static AppLayerDecoderEvents *TFTPGetEvents(void *state, uint64_t tx_id)
  * \retval ALPROTO_TFTP if it looks like echo, otherwise
  *     ALPROTO_UNKNOWN.
  */
-static AppProto TFTPProbingParser(Flow *f, uint8_t *input, uint32_t input_len)
+static AppProto TFTPProbingParser(Flow *f, uint8_t direction,
+        uint8_t *input, uint32_t input_len, uint8_t *rdir)
 {
     /* Very simple test - if there is input, this is tftp.
      * Also check if it's starting by a zero */
index 7de157cf43ed63b77fefd25b5370e3317ca4aee6..761f9497b78f60829db24fdac83a689a59969355 100644 (file)
@@ -323,11 +323,13 @@ static int TCPProtoDetect(ThreadVars *tv,
     }
 #endif
 
+    bool reverse_flow = false;
     PACKET_PROFILING_APP_PD_START(app_tctx);
     *alproto = AppLayerProtoDetectGetProto(app_tctx->alpd_tctx,
             f, data, data_len,
-            IPPROTO_TCP, flags);
+            IPPROTO_TCP, flags, &reverse_flow);
     PACKET_PROFILING_APP_PD_END(app_tctx);
+    SCLogDebug("alproto %u rev %s", *alproto, reverse_flow ? "true" : "false");
 
     if (*alproto != ALPROTO_UNKNOWN) {
         if (*alproto_otherdir != ALPROTO_UNKNOWN && *alproto_otherdir != *alproto) {
@@ -353,6 +355,15 @@ static int TCPProtoDetect(ThreadVars *tv,
         TcpSessionSetReassemblyDepth(ssn,
                 AppLayerParserGetStreamDepth(f));
         FlagPacketFlow(p, f, flags);
+        /* if protocol detection indicated that we need to reverse
+         * the direction of the flow, do it now. We flip the flow,
+         * packet and the direction flags */
+        if (reverse_flow) {
+            SCLogDebug("reversing flow after proto detect told us so");
+            PacketSwap(p);
+            FlowSwap(f);
+            SWAP_FLAGS(flags, STREAM_TOSERVER, STREAM_TOCLIENT);
+        }
 
         /* account flow if we have both sides */
         if (*alproto_otherdir != ALPROTO_UNKNOWN) {
@@ -680,15 +691,23 @@ int AppLayerHandleUdp(ThreadVars *tv, AppLayerThreadCtx *tctx, Packet *p, Flow *
         SCLogDebug("Detecting AL proto on udp mesg (len %" PRIu32 ")",
                    p->payload_len);
 
+        bool reverse_flow = false;
         PACKET_PROFILING_APP_PD_START(tctx);
         f->alproto = AppLayerProtoDetectGetProto(tctx->alpd_tctx,
                                   f, p->payload, p->payload_len,
-                                  IPPROTO_UDP, flags);
+                                  IPPROTO_UDP, flags, &reverse_flow);
         PACKET_PROFILING_APP_PD_END(tctx);
 
         if (f->alproto != ALPROTO_UNKNOWN) {
             AppLayerIncFlowCounter(tv, f);
 
+            if (reverse_flow) {
+                SCLogDebug("reversing flow after proto detect told us so");
+                PacketSwap(p);
+                FlowSwap(f);
+                SWAP_FLAGS(flags, STREAM_TOSERVER, STREAM_TOCLIENT);
+            }
+
             PACKET_PROFILING_APP_START(tctx, f->alproto);
             r = AppLayerParserParse(tv, tctx->alp_tctx, f, f->alproto,
                                     flags, p->payload, p->payload_len);