}
const DNSAnswerHeader *head = (DNSAnswerHeader *)data;
- switch (ntohs(head->type)) {
- case DNS_RECORD_TYPE_A:
- case DNS_RECORD_TYPE_AAAA:
- case DNS_RECORD_TYPE_CNAME:
- {
- data += sizeof(DNSAnswerHeader);
- SCLogDebug("head->len %u", ntohs(head->len));
+ data += sizeof(DNSAnswerHeader);
- if (input + input_len < data + ntohs(head->len)) {
- SCLogDebug("input buffer too small for data of len %u", ntohs(head->len));
- goto insufficient_data;
- }
- SCLogDebug("TTL %u", ntohl(head->ttl));
+ SCLogDebug("head->len %u", ntohs(head->len));
+
+ if (input + input_len < data + ntohs(head->len)) {
+ SCLogDebug("input buffer too small for data of len %u", ntohs(head->len));
+ goto insufficient_data;
+ }
- if (ntohs(head->type) == DNS_RECORD_TYPE_A && ntohs(head->len) == 4) {
+ SCLogDebug("TTL %u", ntohl(head->ttl));
+
+ switch (ntohs(head->type)) {
+ case DNS_RECORD_TYPE_A:
+ {
+ if (ntohs(head->len) == 4) {
//PrintRawDataFp(stdout, data, ntohs(head->len));
//char a[16];
//PrintInet(AF_INET, (const void *)data, a, sizeof(a));
DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
data, 4, ntohs(dns_header->tx_id));
- } else if (ntohs(head->type) == DNS_RECORD_TYPE_AAAA && ntohs(head->len) == 16) {
+ } else {
+ SCLogDebug("invalid length for A response data: %u", ntohs(head->len));
+ goto bad_data;
+ }
+
+ data += ntohs(head->len);
+ break;
+ }
+ case DNS_RECORD_TYPE_AAAA:
+ {
+ if (ntohs(head->len) == 16) {
//char a[46];
//PrintInet(AF_INET6, (const void *)data, a, sizeof(a));
//SCLogInfo("AAAA %s TTL %u", a, ntohl(head->ttl));
DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
data, 16, ntohs(dns_header->tx_id));
- } else if (ntohs(head->type) == DNS_RECORD_TYPE_CNAME) {
- uint8_t cname[DNS_MAX_SIZE];
- uint16_t cname_len = 0;
-
- if ((cname_len = DNSResponseGetNameByOffset(input, input_len,
- data - input, cname, sizeof(cname))) == 0)
- {
-#if DEBUG
- PrintRawDataFp(stdout, (uint8_t *)input, input_len);
- BUG_ON(1);
-#endif
- goto insufficient_data;
- }
-
- DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
- ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
- cname, cname_len, ntohs(dns_header->tx_id));
+ } else {
+ SCLogDebug("invalid length for AAAA response data: %u", ntohs(head->len));
+ goto bad_data;
}
data += ntohs(head->len);
break;
}
case DNS_RECORD_TYPE_MX:
+ case DNS_RECORD_TYPE_CNAME:
+ case DNS_RECORD_TYPE_PTR:
{
- data += sizeof(DNSAnswerHeader);
+ uint8_t name[DNS_MAX_SIZE];
+ uint16_t name_len = 0;
+ uint8_t skip = 0;
- SCLogDebug("head->len %u", ntohs(head->len));
-
- if (input + input_len < data + ntohs(head->len)) {
- SCLogDebug("input buffer too small for data of len %u", ntohs(head->len));
- goto insufficient_data;
+ if (ntohs(head->type) == DNS_RECORD_TYPE_MX) {
+ // Skip the preference header
+ skip = 2;
}
- SCLogDebug("TTL %u", ntohl(head->ttl));
-
- uint8_t mxname[DNS_MAX_SIZE];
- uint16_t mxname_len = 0;
-
- if ((mxname_len = DNSResponseGetNameByOffset(input, input_len,
- data - input + 2, mxname, sizeof(mxname))) == 0) {
+ if ((name_len = DNSResponseGetNameByOffset(input, input_len,
+ data - input + skip, name, sizeof(name))) == 0) {
#if DEBUG
PrintRawDataFp(stdout, (uint8_t *)input, input_len);
BUG_ON(1);
DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
- mxname, mxname_len, ntohs(dns_header->tx_id));
+ name, name_len, ntohs(dns_header->tx_id));
data += ntohs(head->len);
break;
case DNS_RECORD_TYPE_NS:
case DNS_RECORD_TYPE_SOA:
{
- data += sizeof(DNSAnswerHeader);
-
- if (input + input_len < data + ntohs(head->len)) {
- SCLogDebug("input buffer too small for data of len %u", ntohs(head->len));
- goto insufficient_data;
- }
-
- SCLogDebug("TTL %u", ntohl(head->ttl));
-
uint8_t pname[DNS_MAX_SIZE];
uint16_t pname_len = 0;
}
case DNS_RECORD_TYPE_TXT:
{
- data += sizeof(DNSAnswerHeader);
-
- if (input + input_len < data + ntohs(head->len)) {
- SCLogDebug("input buffer too small for data of len %u", ntohs(head->len));
- goto insufficient_data;
- }
-
uint16_t datalen = ntohs(head->len);
uint8_t txtlen = *data;
const uint8_t *tdata = data + 1;
}
default: /* unsupported record */
{
- data += sizeof(DNSAnswerHeader);
-
- if (input + input_len < data + ntohs(head->len)) {
- SCLogDebug("input buffer too small for data of len %u", ntohs(head->len));
- goto insufficient_data;
- }
-
DNSStoreAnswerInState(dns_state, list, NULL, 0,
ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
NULL, 0, ntohs(dns_header->tx_id));
snprintf(str, str_size, "%04x/%u", type, type);
}
}
+
+void DNSCreateRcodeString(uint8_t rcode, char *str, size_t str_size)
+{
+ switch (rcode) {
+ case DNS_RCODE_NOERROR:
+ snprintf(str, str_size, "NOERROR");
+ break;
+ case DNS_RCODE_FORMERR:
+ snprintf(str, str_size, "FORMERR");
+ break;
+ case DNS_RCODE_SERVFAIL:
+ snprintf(str, str_size, "SERVFAIL");
+ break;
+ case DNS_RCODE_NXDOMAIN:
+ snprintf(str, str_size, "NXDOMAIN");
+ break;
+ case DNS_RCODE_NOTIMP:
+ snprintf(str, str_size, "NOTIMP");
+ break;
+ case DNS_RCODE_REFUSED:
+ snprintf(str, str_size, "REFUSED");
+ break;
+ case DNS_RCODE_YXDOMAIN:
+ snprintf(str, str_size, "YXDOMAIN");
+ break;
+ case DNS_RCODE_YXRRSET:
+ snprintf(str, str_size, "YXRRSET");
+ break;
+ case DNS_RCODE_NXRRSET:
+ snprintf(str, str_size, "NXRRSET");
+ break;
+ case DNS_RCODE_NOTAUTH:
+ snprintf(str, str_size, "NOTAUTH");
+ break;
+ case DNS_RCODE_NOTZONE:
+ snprintf(str, str_size, "NOTZONE");
+ break;
+ /* these are the same, need more logic */
+ case DNS_RCODE_BADVERS:
+ //case DNS_RCODE_BADSIG:
+ snprintf(str, str_size, "BADVERS/BADSIG");
+ break;
+ case DNS_RCODE_BADKEY:
+ snprintf(str, str_size, "BADKEY");
+ break;
+ case DNS_RCODE_BADTIME:
+ snprintf(str, str_size, "BADTIME");
+ break;
+ case DNS_RCODE_BADMODE:
+ snprintf(str, str_size, "BADMODE");
+ break;
+ case DNS_RCODE_BADNAME:
+ snprintf(str, str_size, "BADNAME");
+ break;
+ case DNS_RCODE_BADALG:
+ snprintf(str, str_size, "BADALG");
+ break;
+ case DNS_RCODE_BADTRUNC:
+ snprintf(str, str_size, "BADTRUNC");
+ break;
+ default:
+ SCLogDebug("could not map DNS rcode to name, bug!");
+ snprintf(str, str_size, "%04x/%u", rcode, rcode);
+ }
+}
}
}
- SCReturnInt(1);
+ SCReturnInt(1);
bad_data:
insufficient_data:
SCReturnInt(-1);
uint8_t *input, uint32_t input_len,
void *local_data)
{
- DNSState *dns_state = (DNSState *)dstate;
+ DNSState *dns_state = (DNSState *)dstate;
SCLogDebug("starting %u", input_len);
}
}
- /* see if this is a "no such name" error */
- if (ntohs(dns_header->flags) & 0x0003) {
- SCLogDebug("no such name");
+ /* parse rcode, e.g. "noerror" or "nxdomain" */
+ uint8_t rcode = ntohs(dns_header->flags) & 0x0F;
+ if (rcode <= DNS_RCODE_NOTZONE || (rcode >= DNS_RCODE_BADSIG && rcode <= DNS_RCODE_BADTRUNC)) {
+ SCLogDebug("rcode %u", rcode);
if (tx != NULL)
- tx->no_such_name = 1;
+ tx->rcode = rcode;
+ } else {
+ /* this is not invalid, rcodes can be user defined */
+ SCLogDebug("unexpected DNS rcode %u", rcode);
}
if (ntohs(dns_header->flags) & 0x0080) {
void DNSUDPParserRegisterTests(void)
{
- UtRegisterTest("DNSUDPParserTest01", DNSUDPParserTest01, 1);
- UtRegisterTest("DNSUDPParserTest02", DNSUDPParserTest02, 1);
- UtRegisterTest("DNSUDPParserTest03", DNSUDPParserTest03, 1);
- UtRegisterTest("DNSUDPParserTest04", DNSUDPParserTest04, 1);
- UtRegisterTest("DNSUDPParserTest05", DNSUDPParserTest05, 1);
+ UtRegisterTest("DNSUDPParserTest01", DNSUDPParserTest01, 1);
+ UtRegisterTest("DNSUDPParserTest02", DNSUDPParserTest02, 1);
+ UtRegisterTest("DNSUDPParserTest03", DNSUDPParserTest03, 1);
+ UtRegisterTest("DNSUDPParserTest04", DNSUDPParserTest04, 1);
+ UtRegisterTest("DNSUDPParserTest05", DNSUDPParserTest05, 1);
}
#endif
/* id */
json_object_set_new(js, "id", json_integer(tx->tx_id));
+ /* rcode */
+ char rcode[16] = "";
+ DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
+ json_object_set_new(js, "rcode", json_string(rcode));
+
+ /* we are logging an answer RR */
if (entry != NULL) {
/* query */
if (entry->fqdn_len > 0) {
json_object_set_new(js, "rdata", json_string(a));
} else if (entry->data_len == 0) {
json_object_set_new(js, "rdata", json_string(""));
- } else if (entry->type == DNS_RECORD_TYPE_TXT) {
+ } else if (entry->type == DNS_RECORD_TYPE_TXT || entry->type == DNS_RECORD_TYPE_CNAME ||
+ entry->type == DNS_RECORD_TYPE_MX || entry->type == DNS_RECORD_TYPE_PTR) {
if (entry->data_len != 0) {
char buffer[256] = "";
uint16_t copy_len = entry->data_len < (sizeof(buffer) - 1) ?
return;
}
+static void OutputFailure(LogDnsLogThread *aft, json_t *djs, DNSTransaction *tx, DNSQueryEntry *entry)
+{
+ MemBuffer *buffer = (MemBuffer *)aft->buffer;
+ json_t *js = json_object();
+ if (js == NULL)
+ return;
+
+ /* type */
+ json_object_set_new(js, "type", json_string("answer"));
+
+ /* id */
+ json_object_set_new(js, "id", json_integer(tx->tx_id));
+
+ /* rcode */
+ char rcode[16] = "";
+ DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
+ json_object_set_new(js, "rcode", json_string(rcode));
+
+ /* no answer RRs, use query for rname */
+ char *c;
+ c = BytesToString((uint8_t *)((uint8_t *)entry + sizeof(DNSQueryEntry)), entry->len);
+ if (c != NULL) {
+ json_object_set_new(js, "rrname", json_string(c));
+ SCFree(c);
+ }
+
+ /* reset */
+ MemBufferReset(buffer);
+ json_object_set_new(djs, "dns", js);
+ OutputJSONBuffer(djs, aft->dnslog_ctx->file_ctx, buffer);
+ json_object_del(djs, "dns");
+
+ return;
+}
+
static void LogAnswers(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx, uint64_t tx_id)
{
SCLogDebug("got a DNS response and now logging !!");
- if (tx->no_such_name) {
- OutputAnswer(aft, js, tx, NULL);
+ /* rcode != noerror */
+ if (tx->rcode) {
+ /* Most DNS servers do not support multiple queries because
+ * the rcode in response is not per-query. Multiple queries
+ * are likely to lead to FORMERR, so log this. */
+ DNSQueryEntry *query = NULL;
+ TAILQ_FOREACH(query, &tx->query_list, next) {
+ OutputFailure(aft, js, tx, query);
+ }
}
DNSAnswerEntry *entry = NULL;