diff --git a/src/app-layer-dns-common.h b/src/app-layer-dns-common.h index bc20907f6f48..6a9b7c7db6a8 100644 --- a/src/app-layer-dns-common.h +++ b/src/app-layer-dns-common.h @@ -232,6 +232,7 @@ typedef struct DNSState_ { uint16_t offset; uint16_t record_len; uint8_t *buffer; + uint8_t gap; /**< Flag set when a gap has occurred. */ } DNSState; #define DNS_CONFIG_DEFAULT_REQUEST_FLOOD 500 diff --git a/src/app-layer-dns-tcp.c b/src/app-layer-dns-tcp.c index 21b881764036..14991181f914 100644 --- a/src/app-layer-dns-tcp.c +++ b/src/app-layer-dns-tcp.c @@ -58,6 +58,9 @@ struct DNSTcpHeader_ { } __attribute__((__packed__)); typedef struct DNSTcpHeader_ DNSTcpHeader; +static uint16_t DNSTcpProbingParser(uint8_t *input, uint32_t ilen, + uint32_t *offset); + /** \internal * \param input_len at least enough for the DNSTcpHeader */ @@ -288,6 +291,13 @@ static int DNSTCPRequestParse(Flow *f, void *dstate, DNSState *dns_state = (DNSState *)dstate; SCLogDebug("starting %u", input_len); + if (input == NULL && input_len > 0) { + SCLogDebug("Input is NULL, but len is %"PRIu32": must be a gap.", + input_len); + dns_state->gap = 1; + SCReturnInt(1); + } + if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) { SCReturnInt(1); } @@ -301,6 +311,18 @@ static int DNSTCPRequestParse(Flow *f, void *dstate, goto insufficient_data; } + /* Clear gap state. */ + if (dns_state->gap) { + if (DNSTcpProbingParser(input, input_len, NULL) == ALPROTO_DNS) { + SCLogDebug("New data probed as DNS, clearing gap state."); + BufferReset(dns_state); + dns_state->gap = 0; + } else { + SCLogDebug("Unable to sync DNS parser, leaving gap state."); + SCReturnInt(1); + } + } + next_record: /* if this is the beginning of a record, we need at least the header */ if (dns_state->offset == 0 && input_len < sizeof(DNSTcpHeader)) { @@ -508,6 +530,13 @@ static int DNSTCPResponseParse(Flow *f, void *dstate, { DNSState *dns_state = (DNSState *)dstate; + if (input == NULL && input_len > 0) { + SCLogDebug("Input is NULL, but len is %"PRIu32": must be a gap.", + input_len); + dns_state->gap = 1; + SCReturnInt(1); + } + if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) { SCReturnInt(1); } @@ -521,6 +550,18 @@ static int DNSTCPResponseParse(Flow *f, void *dstate, goto insufficient_data; } + /* Clear gap state. */ + if (dns_state->gap) { + if (DNSTcpProbingParser(input, input_len, NULL) == ALPROTO_DNS) { + SCLogDebug("New data probed as DNS, clearing gap state."); + BufferReset(dns_state); + dns_state->gap = 0; + } else { + SCLogDebug("Unable to sync DNS parser, leaving gap state."); + SCReturnInt(1); + } + } + next_record: /* if this is the beginning of a record, we need at least the header */ if (dns_state->offset == 0 && input_len < sizeof(DNSTcpHeader)) { @@ -712,6 +753,11 @@ void RegisterDNSTCPParsers(void) AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_DNS, DNSGetAlstateProgressCompletionStatus); DNSAppLayerRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_DNS); + + /* This parser accepts gaps. */ + AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_DNS, + APP_LAYER_PARSER_OPT_ACCEPT_GAPS); + } else { SCLogInfo("Parsed disabled for %s protocol. Protocol detection" "still on.", proto_name); diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 44a03368cb36..99ce1a3d32e7 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -127,6 +127,9 @@ typedef struct AppLayerParserProtoCtx_ * STREAM_TOSERVER, STREAM_TOCLIENT */ uint8_t first_data_dir; + /* Option flags such as supporting gaps or not. */ + uint64_t flags; + #ifdef UNITTESTS void (*RegisterUnittests)(void); #endif @@ -364,6 +367,16 @@ void AppLayerParserRegisterParserAcceptableDataDirection(uint8_t ipproto, AppPro SCReturn; } +void AppLayerParserRegisterOptionFlags(uint8_t ipproto, AppProto alproto, + uint64_t flags) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].flags |= flags; + + SCReturn; +} + void AppLayerParserRegisterStateFuncs(uint8_t ipproto, AppProto alproto, void *(*StateAlloc)(void), void (*StateFree)(void *)) @@ -980,14 +993,15 @@ int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow if (p->StateAlloc == NULL) goto end; - /* Do this check before calling AppLayerParse */ if (flags & STREAM_GAP) { - SCLogDebug("stream gap detected (missing packets), " - "this is not yet supported."); - - if (f->alstate != NULL) - AppLayerParserStreamTruncated(f->proto, alproto, f->alstate, flags); - goto error; + if (!(p->flags & APP_LAYER_PARSER_OPT_ACCEPT_GAPS)) { + SCLogDebug("app-layer parser does not accept gaps"); + if (f->alstate != NULL) { + AppLayerParserStreamTruncated(f->proto, alproto, f->alstate, + flags); + } + goto error; + } } /* Get the parser state (if any) */ diff --git a/src/app-layer-parser.h b/src/app-layer-parser.h index e887e0008b0c..6ee70b83e268 100644 --- a/src/app-layer-parser.h +++ b/src/app-layer-parser.h @@ -30,11 +30,15 @@ #include "util-file.h" #include "stream-tcp-private.h" +/* Flags for AppLayerParserState. */ #define APP_LAYER_PARSER_EOF 0x01 #define APP_LAYER_PARSER_NO_INSPECTION 0x02 #define APP_LAYER_PARSER_NO_REASSEMBLY 0x04 #define APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD 0x08 +/* Flags for AppLayerParserProtoCtx. */ +#define APP_LAYER_PARSER_OPT_ACCEPT_GAPS BIT_U64(0) + int AppLayerParserProtoIsRegistered(uint8_t ipproto, AppProto alproto); /***** transaction handling *****/ @@ -115,6 +119,8 @@ int AppLayerParserRegisterParser(uint8_t ipproto, AppProto alproto, void AppLayerParserRegisterParserAcceptableDataDirection(uint8_t ipproto, AppProto alproto, uint8_t direction); +void AppLayerParserRegisterOptionFlags(uint8_t ipproto, AppProto alproto, + uint64_t flags); void AppLayerParserRegisterStateFuncs(uint8_t ipproto, AppProto alproto, void *(*StateAlloc)(void), void (*StateFree)(void *)); diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index 6072383132f3..3a6dada5cba9 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -893,7 +893,12 @@ static void GetAppBuffer(TcpStream *stream, const uint8_t **data, uint32_t *data } } -static inline bool CheckGap(TcpStream *stream, Packet *p) +/** + * \brief Check if there is a gap in the application data. + * + * \retval the size of the gap in bytes, 0 if no gap. + */ +static inline uint32_t CheckGap(TcpStream *stream, Packet *p) { const uint64_t app_progress = STREAM_APP_PROGRESS(stream); uint64_t last_ack_abs = STREAM_BASE_OFFSET(stream); @@ -915,16 +920,16 @@ static inline bool CheckGap(TcpStream *stream, Packet *p) if (next_seq_abs > app_progress+1) { /* fall through */ } else { - return false; + return 0; } } } SCLogDebug("packet %u GAP! last_ack_abs %u > app_progress %u, at no data.", (uint)p->pcap_cnt, (uint)last_ack_abs, (uint)app_progress); - return true; + return delta; } } - return false; + return 0; } /** \internal @@ -934,9 +939,9 @@ static inline bool CheckGap(TcpStream *stream, Packet *p) static int ReassembleUpdateAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, TcpSession *ssn, TcpStream *stream, - Packet *p, enum StreamUpdateDir dir) + Packet *p, enum StreamUpdateDir dir, uint32_t gap) { - const uint64_t app_progress = STREAM_APP_PROGRESS(stream); + const uint64_t app_progress = STREAM_APP_PROGRESS(stream) + gap; SCLogDebug("app progress %"PRIu64, app_progress); SCLogDebug("last_ack %u, base_seq %u", stream->last_ack, stream->base_seq); @@ -945,11 +950,12 @@ static int ReassembleUpdateAppLayer (ThreadVars *tv, uint32_t mydata_len; GetAppBuffer(stream, &mydata, &mydata_len, app_progress); if (mydata == NULL || mydata_len == 0) { - if (CheckGap(stream, p)) { + /* Only check for gap and signal if not already done. */ + if (gap == 0 && ((gap = CheckGap(stream, p) > 0))) { /* send gap signal */ SCLogDebug("sending GAP to app-layer"); AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream, - NULL, 0, + NULL, gap, StreamGetAppLayerFlags(ssn, stream, p, dir)|STREAM_GAP); AppLayerProfilingStore(ra_ctx->app_tctx, p); @@ -1005,7 +1011,7 @@ static int ReassembleUpdateAppLayer (ThreadVars *tv, SCLogDebug("app progress %"PRIu64" increasing with data len %u to %"PRIu64, app_progress, mydata_len, app_progress + mydata_len); - stream->app_progress_rel += mydata_len; + stream->app_progress_rel += mydata_len + gap; SCLogDebug("app progress now %"PRIu64, STREAM_APP_PROGRESS(stream)); } else { SCLogDebug("NOT UPDATED app progress still %"PRIu64, app_progress); @@ -1032,6 +1038,8 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, { SCEnter(); + uint32_t gap = 0; + /* this function can be directly called by app layer protocol * detection. */ if ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) || @@ -1043,7 +1051,6 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, SCReturnInt(0); } - SCLogDebug("stream->seg_list %p", stream->seg_list); #ifdef DEBUG PrintList(stream->seg_list); @@ -1081,12 +1088,15 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, "(base %u, isn %u, last_ack %u => diff %u) p %"PRIu64, stream->base_seq, stream->isn, stream->last_ack, stream->last_ack - (stream->isn + ackadd), p->pcap_cnt); + gap = stream->last_ack - (stream->isn + ackadd); + } else { + gap = stream->seg_list->seq - stream->base_seq; } /* send gap signal */ SCLogDebug("sending GAP to app-layer"); AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream, - NULL, 0, + NULL, gap, StreamGetAppLayerFlags(ssn, stream, p, dir)|STREAM_GAP); AppLayerProfilingStore(ra_ctx->app_tctx, p); @@ -1096,8 +1106,6 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, StreamTcpSetEvent(p, STREAM_REASSEMBLY_SEQ_GAP); StatsIncr(tv, ra_ctx->counter_tcp_reass_gap); - - SCReturnInt(0); } } @@ -1128,7 +1136,7 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, } /* with all that out of the way, lets update the app-layer */ - return ReassembleUpdateAppLayer(tv, ra_ctx, ssn, stream, p, dir); + return ReassembleUpdateAppLayer(tv, ra_ctx, ssn, stream, p, dir, gap); } /** \internal