diff --git a/src/app-layer-dns-common.h b/src/app-layer-dns-common.h index bc20907f6f..2e069d0f4b 100644 --- a/src/app-layer-dns-common.h +++ b/src/app-layer-dns-common.h @@ -232,6 +232,8 @@ typedef struct DNSState_ { uint16_t offset; uint16_t record_len; uint8_t *buffer; + uint8_t gap_ts; /**< Flag set when a gap has occurred. */ + uint8_t gap_tc; /**< 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 21b8817640..cb88232e20 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_ts = 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_ts) { + if (DNSTcpProbingParser(input, input_len, NULL) == ALPROTO_DNS) { + SCLogDebug("New data probed as DNS, clearing gap state."); + BufferReset(dns_state); + dns_state->gap_ts = 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_tc = 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_tc) { + if (DNSTcpProbingParser(input, input_len, NULL) == ALPROTO_DNS) { + SCLogDebug("New data probed as DNS, clearing gap state."); + BufferReset(dns_state); + dns_state->gap_tc = 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);