if (DNSValidateRequestHeader(dns_state, dns_header) < 0)
goto bad_data;
+ if (dns_state != NULL) {
+ if (timercmp(&dns_state->last_req, &dns_state->last_resp, >=)) {
+ if (dns_state->window <= dns_state->unreplied_cnt) {
+ dns_state->window++;
+ }
+ }
+ }
+
uint16_t q;
const uint8_t *data = input + sizeof(DNSHeader);
for (q = 0; q < ntohs(dns_header->questions); q++) {
}
}
+ if (dns_state != NULL && f != NULL) {
+ dns_state->last_req = f->lastts;
+ }
+
SCReturnInt(1);
bad_data:
insufficient_data:
if (!found) {
SCLogDebug("DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE");
DNSSetEvent(dns_state, DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE);
+ } else if (dns_state->unreplied_cnt > 0) {
+ dns_state->unreplied_cnt--;
}
if (DNSValidateResponseHeader(dns_state, dns_header) < 0)
tx->replied = 1;
}
+ if (dns_state != NULL && f != NULL) {
+ dns_state->last_resp = f->lastts;
+ }
SCReturnInt(1);
bad_data:
return (result);
}
+/**
+ * \test Test subsequent requests before response.
+ *
+ * This test sends 2 DNS requests on the same state then sends the response
+ * to the first request checking that it is seen and associated with the
+ * transaction.
+ */
+static int DNSUDPParserTestDelayedResponse(void)
+{
+ /* DNS request:
+ * - Flags: 0x0100 Standard query
+ * - A www.google.com
+ */
+ uint8_t req[] = {
+ 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77,
+ 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+ 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+ };
+ size_t reqlen = sizeof(req);
+
+ /* DNS response:
+ * - Flags: 0x8180 Standard query response, no error
+ * - www.google.com A 24.244.4.56
+ * - www.google.com A 24.244.4.54
+ * - www.google.com A 24.244.4.57
+ * - www.google.com A 24.244.4.55
+ * - www.google.com A 24.244.4.52
+ * - www.google.com A 24.244.4.53
+ * - www.google.com A 24.244.4.58
+ * - www.google.com A 24.244.4.59
+ */
+ uint8_t res[] = {
+ 0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77,
+ 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+ 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x38,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x39,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x34,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x35,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x36,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x3b,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x37,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x3a
+ };
+ size_t reslen = sizeof(res);
+
+ DNSState *state = DNSStateAlloc();
+ FAIL_IF_NULL(state);
+ Flow *f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 53);
+ FAIL_IF_NULL(f);
+ f->proto = IPPROTO_UDP;
+ f->alproto = ALPROTO_DNS;
+ f->alstate = state;
+
+ /* Send to requests with an incrementing tx id. */
+ FAIL_IF_NOT(DNSUDPRequestParse(f, f->alstate, NULL, req, reqlen, NULL));
+ req[1] = 0x02;
+ FAIL_IF_NOT(DNSUDPRequestParse(f, f->alstate, NULL, req, reqlen, NULL));
+
+ /* Send response to the first request. */
+ FAIL_IF_NOT(DNSUDPResponseParse(f, f->alstate, NULL, res, reslen, NULL));
+ DNSTransaction *tx = TAILQ_FIRST(&state->tx_list);
+ FAIL_IF_NULL(tx);
+ FAIL_IF_NOT(tx->replied);
+
+ /* Also free's state. */
+ UTHFreeFlow(f);
+
+ PASS;
+}
+
+/**
+ * \test Test entering the flood/givenup state.
+ */
+static int DNSUDPParserTestFlood(void)
+{
+ /* DNS request:
+ * - Flags: 0x0100 Standard query
+ * - A www.google.com
+ */
+ uint8_t req[] = {
+ 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77,
+ 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+ 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+ };
+ size_t reqlen = sizeof(req);
+
+ DNSState *state = DNSStateAlloc();
+ FAIL_IF_NULL(state);
+ Flow *f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 53);
+ FAIL_IF_NULL(f);
+ f->proto = IPPROTO_UDP;
+ f->alproto = ALPROTO_DNS;
+ f->alstate = state;
+
+ uint16_t txid;
+ for (txid = 1; txid <= DNS_CONFIG_DEFAULT_REQUEST_FLOOD + 1; txid++) {
+ req[0] = (txid >> 8) & 0xff;
+ req[1] = txid & 0xff;
+ FAIL_IF_NOT(DNSUDPRequestParse(f, f->alstate, NULL, req, reqlen, NULL));
+ FAIL_IF(state->givenup);
+ }
+
+ /* With one more request we should enter a flooded state. */
+ txid++;
+ req[0] = (txid >> 8) & 0xff;
+ req[1] = txid & 0xff;
+ FAIL_IF_NOT(DNSUDPRequestParse(f, f->alstate, NULL, req, reqlen, NULL));
+ FAIL_IF(!state->givenup);
+
+ /* Also free's state. */
+ UTHFreeFlow(f);
+
+ PASS;
+}
+
+static int DNSUDPParserTestLostResponse(void)
+{
+ /* DNS request:
+ * - Flags: 0x0100 Standard query
+ * - A www.google.com
+ */
+ uint8_t req[] = {
+ 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77,
+ 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+ 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+ };
+ size_t reqlen = sizeof(req);
+
+ uint8_t res[] = {
+ 0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77,
+ 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+ 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x38,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x39,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x34,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x35,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x36,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x3b,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x37,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x04, 0x18, 0xf4, 0x04, 0x3a
+ };
+ size_t reslen = sizeof(res);
+
+ DNSTransaction *tx;
+ DNSState *state = DNSStateAlloc();
+ FAIL_IF_NULL(state);
+ Flow *f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 53);
+ FAIL_IF_NULL(f);
+ f->proto = IPPROTO_UDP;
+ f->alproto = ALPROTO_DNS;
+ f->alstate = state;
+
+ /* First request. */
+ req[1] = 0x01;
+ FAIL_IF_NOT(DNSUDPRequestParse(f, f->alstate, NULL, req, reqlen, NULL));
+ FAIL_IF_NOT(state->transaction_max == 1);
+ FAIL_IF_NOT(state->unreplied_cnt == 1);
+ FAIL_IF_NOT(state->window == 1);
+
+ /* Second request. */
+ req[1] = 0x02;
+ FAIL_IF_NOT(DNSUDPRequestParse(f, f->alstate, NULL, req, reqlen, NULL));
+ FAIL_IF_NOT(state->transaction_max == 2);
+ FAIL_IF_NOT(state->unreplied_cnt == 2);
+ FAIL_IF_NOT(state->window == 2);
+
+ /* Third request. */
+ req[1] = 0x03;
+ FAIL_IF_NOT(DNSUDPRequestParse(f, f->alstate, NULL, req, reqlen, NULL));
+ FAIL_IF_NOT(state->transaction_max == 3);
+ FAIL_IF_NOT(state->unreplied_cnt == 3);
+ FAIL_IF_NOT(state->window == 3);
+
+ /* Now respond to the second. */
+ res[1] = 0x02;
+ FAIL_IF_NOT(DNSUDPResponseParse(f, f->alstate, NULL, res, reslen, NULL));
+ FAIL_IF_NOT(state->unreplied_cnt == 2);
+ FAIL_IF_NOT(state->window == 3);
+ tx = TAILQ_FIRST(&state->tx_list);
+ FAIL_IF_NULL(tx);
+ FAIL_IF(tx->replied);
+ FAIL_IF(tx->reply_lost);
+
+ /* Send a 4th request. */
+ req[1] = 0x04;
+ FAIL_IF_NOT(DNSUDPRequestParse(f, f->alstate, NULL, req, reqlen, NULL));
+ FAIL_IF_NOT(state->unreplied_cnt == 3);
+ FAIL_IF(state->window != 3);
+ FAIL_IF_NOT(state->transaction_max == 4);
+
+ /* Response to the third request. */
+ res[1] = 0x03;
+ FAIL_IF_NOT(DNSUDPResponseParse(f, f->alstate, NULL, res, reslen, NULL));
+ FAIL_IF_NOT(state->unreplied_cnt == 2);
+ FAIL_IF_NOT(state->window == 3);
+ tx = TAILQ_FIRST(&state->tx_list);
+ FAIL_IF_NULL(tx);
+ FAIL_IF(tx->replied);
+ FAIL_IF(!tx->reply_lost);
+
+ /* Also free's state. */
+ UTHFreeFlow(f);
+
+ PASS;
+}
void DNSUDPParserRegisterTests(void)
{
UtRegisterTest("DNSUDPParserTest03", DNSUDPParserTest03);
UtRegisterTest("DNSUDPParserTest04", DNSUDPParserTest04);
UtRegisterTest("DNSUDPParserTest05", DNSUDPParserTest05);
+ UtRegisterTest("DNSUDPParserTestFlood", DNSUDPParserTestFlood);
+ UtRegisterTest("DNSUDPParserTestDelayedResponse",
+ DNSUDPParserTestDelayedResponse);
+ UtRegisterTest("DNSUDPParserTestLostResponse",
+ DNSUDPParserTestLostResponse);
}
#endif