diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index a8e6238c57..dbac47db7e 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -1831,6 +1831,9 @@ static int HTPCallbackRequest(htp_connp_t *connp) { } } + /* request done, do raw reassembly now to inspect state and stream + * at the same time. */ + AppLayerTriggerRawStreamReassembly(hstate->f); SCReturnInt(HOOK_OK); } @@ -1886,6 +1889,9 @@ static int HTPCallbackResponse(htp_connp_t *connp) { htp_tx_destroy(tx); } + /* response done, do raw reassembly now to inspect state and stream + * at the same time. */ + AppLayerTriggerRawStreamReassembly(hstate->f); SCReturnInt(HOOK_OK); } diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 6a360334b0..261214fe45 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -1218,6 +1218,30 @@ AppLayerDecoderEvents *AppLayerGetDecoderEventsForFlow(Flow *f) return NULL; } +/** + * \brief Trigger "raw" stream reassembly from the app layer. + * + * This way HTTP for example, can trigger raw stream inspection right + * when the full request body is received. This is often smaller than + * our raw reassembly size limit. + * + * \param f flow, for access the stream state + */ +void AppLayerTriggerRawStreamReassembly(Flow *f) { + SCEnter(); + +#ifdef DEBUG + BUG_ON(f == NULL); +#endif + + if (f != NULL && f->protoctx != NULL) { + TcpSession *ssn = (TcpSession *)f->protoctx; + StreamTcpReassembleTriggerRawReassembly(ssn); + } + + SCReturn; +} + void RegisterAppLayerParsers(void) { /** \todo move to general init function */ diff --git a/src/app-layer-parser.h b/src/app-layer-parser.h index 362f7f1392..b00070e519 100644 --- a/src/app-layer-parser.h +++ b/src/app-layer-parser.h @@ -291,4 +291,6 @@ uint16_t AppLayerGetStateVersion(Flow *f); FileContainer *AppLayerGetFilesFromFlow(Flow *, uint8_t); AppLayerDecoderEvents *AppLayerGetDecoderEventsForFlow(Flow *); +void AppLayerTriggerRawStreamReassembly(Flow *); + #endif /* __APP_LAYER_PARSER_H__ */ diff --git a/src/stream-tcp-private.h b/src/stream-tcp-private.h index a937500d0a..e69b556836 100644 --- a/src/stream-tcp-private.h +++ b/src/stream-tcp-private.h @@ -118,6 +118,9 @@ enum #define STREAMTCP_FLAG_CLIENT_SACKOK 0x0800 /** Flag to indicate both sides of the session permit SACK (SYN + SYN/ACK) */ #define STREAMTCP_FLAG_SACKOK 0x1000 +/** Flag for triggering RAW reassembly before the size limit is reached or + the stream reaches EOF. */ +#define STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY 0x2000 /* * Per STREAM flags diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index a151b2e5df..d175ceb28b 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -1733,6 +1733,12 @@ static int StreamTcpReassembleRawCheckLimit(TcpSession *ssn, TcpStream *stream, { SCEnter(); + if (ssn->flags & STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY) { + SCLogDebug("reassembling now as STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY is set"); + ssn->flags &= ~STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY; + SCReturnInt(1); + } + /* some states mean we reassemble no matter how much data we have */ if (ssn->state >= TCP_TIME_WAIT) SCReturnInt(1); @@ -3505,6 +3511,30 @@ TcpSegment* StreamTcpGetSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, return seg; } +/** + * \brief Trigger RAW stream reassembly + * + * Used by AppLayerTriggerRawStreamReassembly to trigger RAW stream + * reassembly from the applayer, for example upon completion of a + * HTTP request. + * + * Works by setting a flag in the TcpSession that is unset as soon + * as it's checked. Since everything happens when operating under + * a single lock period, no side effects are expected. + * + * \param ssn TcpSession + */ +void StreamTcpReassembleTriggerRawReassembly(TcpSession *ssn) { +#ifdef DEBUG + BUG_ON(ssn == NULL); +#endif + + if (ssn != NULL) { + SCLogDebug("flagged ssn %p for immediate raw reassembly", ssn); + ssn->flags |= STREAMTCP_FLAG_TRIGGER_RAW_REASSEMBLY; + } +} + #ifdef UNITTESTS /** unit tests and it's support functions below */ @@ -6011,9 +6041,10 @@ static int StreamTcpReassembleTest38 (void) { goto end; } - /* Check if we have stream smsgs in queue */ - if (ra_ctx->stream_q->len != 0) { - printf("there should be no stream smsgs in the queue (6): "); + /* we should now have a smsg as the http request is complete and triggered + * reassembly */ + if (ra_ctx->stream_q->len != 1) { + printf("there should one stream smsg in the queue (6): "); goto end; } diff --git a/src/stream-tcp-reassemble.h b/src/stream-tcp-reassemble.h index 708d0ff359..5afa4036b2 100644 --- a/src/stream-tcp-reassemble.h +++ b/src/stream-tcp-reassemble.h @@ -89,5 +89,7 @@ TcpSegment* StreamTcpGetSegment(ThreadVars *, TcpReassemblyThreadCtx *, uint16_t void StreamTcpReturnStreamSegments(TcpStream *); void StreamTcpSegmentReturntoPool(TcpSegment *); +void StreamTcpReassembleTriggerRawReassembly(TcpSession *); + #endif /* __STREAM_TCP_REASSEMBLE_H__ */