dns: remove as much C DNS code as possible

As some of the C code is still used it can't all be removed.

Redmine issue:
https://redmine.openinfosecfoundation.org/issues/2850
pull/3801/head
Jason Ish 7 years ago committed by Victor Julien
parent 355d125c4f
commit 67b2692d34

@ -22,104 +22,7 @@
*/
#include "suricata-common.h"
#include "stream.h"
#include "app-layer-parser.h"
#include "app-layer-dns-common.h"
#ifdef DEBUG
#include "util-print.h"
#endif
#include "util-memcmp.h"
#include "util-atomic.h"
typedef struct DNSConfig_ {
uint32_t request_flood;
uint32_t state_memcap; /**< memcap in bytes per state */
uint64_t global_memcap; /**< memcap in bytes globally for parser */
} DNSConfig;
static DNSConfig dns_config;
void DNSConfigInit(void)
{
memset(&dns_config, 0x00, sizeof(dns_config));
}
void DNSConfigSetRequestFlood(uint32_t value)
{
dns_config.request_flood = value;
}
void DNSConfigSetStateMemcap(uint32_t value)
{
dns_config.state_memcap = value;
}
SC_ATOMIC_DECLARE(uint64_t, dns_memuse); /**< byte counter of current memuse */
SC_ATOMIC_DECLARE(uint64_t, dns_memcap_state); /**< counts number of 'rejects' */
SC_ATOMIC_DECLARE(uint64_t, dns_memcap_global); /**< counts number of 'rejects' */
void DNSConfigSetGlobalMemcap(uint64_t value)
{
dns_config.global_memcap = value;
SC_ATOMIC_INIT(dns_memuse);
SC_ATOMIC_INIT(dns_memcap_state);
SC_ATOMIC_INIT(dns_memcap_global);
}
void DNSIncrMemcap(uint32_t size, DNSState *state)
{
if (state != NULL) {
state->memuse += size;
}
SC_ATOMIC_ADD(dns_memuse, size);
}
void DNSDecrMemcap(uint32_t size, DNSState *state)
{
if (state != NULL) {
BUG_ON(size > state->memuse); /**< TODO remove later */
state->memuse -= size;
}
BUG_ON(size > SC_ATOMIC_GET(dns_memuse)); /**< TODO remove later */
(void)SC_ATOMIC_SUB(dns_memuse, size);
}
int DNSCheckMemcap(uint32_t want, DNSState *state)
{
if (state != NULL) {
if (state->memuse + want > dns_config.state_memcap) {
SC_ATOMIC_ADD(dns_memcap_state, 1);
DNSSetEvent(state, DNS_DECODER_EVENT_STATE_MEMCAP_REACHED);
return -1;
}
}
if (SC_ATOMIC_GET(dns_memuse) + (uint64_t)want > dns_config.global_memcap) {
SC_ATOMIC_ADD(dns_memcap_global, 1);
return -2;
}
return 0;
}
uint64_t DNSMemcapGetMemuseCounter(void)
{
uint64_t x = SC_ATOMIC_GET(dns_memuse);
return x;
}
uint64_t DNSMemcapGetMemcapStateCounter(void)
{
uint64_t x = SC_ATOMIC_GET(dns_memcap_state);
return x;
}
uint64_t DNSMemcapGetMemcapGlobalCounter(void)
{
uint64_t x = SC_ATOMIC_GET(dns_memcap_global);
return x;
}
SCEnumCharMap dns_decoder_event_table[ ] = {
{ "UNSOLLICITED_RESPONSE", DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE, },
@ -156,891 +59,6 @@ void DNSAppLayerRegisterGetEventInfo(uint8_t ipproto, AppProto alproto)
return;
}
AppLayerDecoderEvents *DNSGetEvents(void *state, uint64_t id)
{
DNSState *dns_state = (DNSState *)state;
DNSTransaction *tx;
if (dns_state->curr && dns_state->curr->tx_num == (id + 1)) {
return dns_state->curr->decoder_events;
}
TAILQ_FOREACH(tx, &dns_state->tx_list, next) {
if (tx->tx_num == (id+1))
return tx->decoder_events;
}
return NULL;
}
void *DNSGetTx(void *alstate, uint64_t tx_id)
{
DNSState *dns_state = (DNSState *)alstate;
DNSTransaction *tx = NULL;
/* fast track: try the current tx */
if (dns_state->curr && dns_state->curr->tx_num == tx_id + 1)
return dns_state->curr;
/* fast track:
* if the prev tx_id is equal to the stored tx ptr, we can
* use this shortcut to get to the next. */
if (dns_state->iter) {
if (tx_id == dns_state->iter->tx_num) {
tx = TAILQ_NEXT(dns_state->iter, next);
if (tx && tx->tx_num == tx_id + 1) {
dns_state->iter = tx;
return tx;
}
}
}
/* no luck with the fast tracks, do the full list walk */
TAILQ_FOREACH(tx, &dns_state->tx_list, next) {
SCLogDebug("tx->tx_num %u, tx_id %"PRIu64, tx->tx_num, (tx_id+1));
if ((tx_id+1) != tx->tx_num)
continue;
SCLogDebug("returning tx %p", tx);
dns_state->iter = tx;
return tx;
}
return NULL;
}
uint64_t DNSGetTxCnt(void *alstate)
{
DNSState *dns_state = (DNSState *)alstate;
return (uint64_t)dns_state->transaction_max;
}
int DNSGetAlstateProgress(void *tx, uint8_t direction)
{
DNSTransaction *dns_tx = (DNSTransaction *)tx;
if (direction & STREAM_TOCLIENT) {
/* response side of the tx is done if we parsed a reply
* or if we tagged this tx as 'reply lost'. */
return (dns_tx->replied|dns_tx->reply_lost) ? 1 : 0;
}
else {
/* tx is only created if we have a complete request,
* or if we lost the request. Either way, if we have
* a tx it we consider the request complete. */
return 1;
}
}
void DNSSetTxLogged(void *alstate, void *tx, LoggerId logged)
{
DNSTransaction *dns_tx = (DNSTransaction *)tx;
dns_tx->logged = logged;
}
LoggerId DNSGetTxLogged(void *alstate, void *tx)
{
DNSTransaction *dns_tx = (DNSTransaction *)tx;
return dns_tx->logged;
}
uint64_t DNSGetTxDetectFlags(void *vtx, uint8_t dir)
{
DNSTransaction *tx = (DNSTransaction *)vtx;
if (dir & STREAM_TOSERVER) {
return tx->detect_flags_ts;
} else {
return tx->detect_flags_tc;
}
}
void DNSSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t detect_flags)
{
DNSTransaction *tx = (DNSTransaction *)vtx;
if (dir & STREAM_TOSERVER) {
tx->detect_flags_ts = detect_flags;
} else {
tx->detect_flags_tc = detect_flags;
}
}
/** \brief get value for 'complete' status in DNS
*
* For DNS we use a simple bool. 1 means done.
*/
int DNSGetAlstateProgressCompletionStatus(uint8_t direction)
{
return 1;
}
void DNSSetEvent(DNSState *s, uint8_t e)
{
if (s && s->curr) {
SCLogDebug("s->curr->decoder_events %p", s->curr->decoder_events);
AppLayerDecoderEventsSetEventRaw(&s->curr->decoder_events, e);
SCLogDebug("s->curr->decoder_events %p", s->curr->decoder_events);
s->events++;
} else {
SCLogDebug("couldn't set event %u", e);
}
}
/** \internal
* \brief Allocate a DNS TX
* \retval tx or NULL */
static DNSTransaction *DNSTransactionAlloc(DNSState *state, const uint16_t tx_id)
{
if (DNSCheckMemcap(sizeof(DNSTransaction), state) < 0)
return NULL;
DNSTransaction *tx = SCMalloc(sizeof(DNSTransaction));
if (unlikely(tx == NULL))
return NULL;
DNSIncrMemcap(sizeof(DNSTransaction), state);
memset(tx, 0x00, sizeof(DNSTransaction));
TAILQ_INIT(&tx->query_list);
TAILQ_INIT(&tx->answer_list);
TAILQ_INIT(&tx->authority_list);
tx->tx_id = tx_id;
return tx;
}
/** \internal
* \brief Free a DNS TX
* \param tx DNS TX to free */
static void DNSTransactionFree(DNSTransaction *tx, DNSState *state)
{
SCEnter();
DNSQueryEntry *q = NULL;
while ((q = TAILQ_FIRST(&tx->query_list))) {
TAILQ_REMOVE(&tx->query_list, q, next);
DNSDecrMemcap((sizeof(DNSQueryEntry) + q->len), state);
SCFree(q);
}
DNSAnswerEntry *a = NULL;
while ((a = TAILQ_FIRST(&tx->answer_list))) {
TAILQ_REMOVE(&tx->answer_list, a, next);
DNSDecrMemcap((sizeof(DNSAnswerEntry) + a->fqdn_len + a->data_len), state);
SCFree(a);
}
while ((a = TAILQ_FIRST(&tx->authority_list))) {
TAILQ_REMOVE(&tx->authority_list, a, next);
DNSDecrMemcap((sizeof(DNSAnswerEntry) + a->fqdn_len + a->data_len), state);
SCFree(a);
}
AppLayerDecoderEventsFreeEvents(&tx->decoder_events);
if (tx->de_state != NULL) {
DetectEngineStateFree(tx->de_state);
}
if (state->iter == tx)
state->iter = NULL;
DNSDecrMemcap(sizeof(DNSTransaction), state);
SCFree(tx);
SCReturn;
}
/**
* \brief dns transaction cleanup callback
*/
void DNSStateTransactionFree(void *state, uint64_t tx_id)
{
SCEnter();
DNSState *dns_state = state;
DNSTransaction *tx = NULL;
SCLogDebug("state %p, id %"PRIu64, dns_state, tx_id);
TAILQ_FOREACH(tx, &dns_state->tx_list, next) {
SCLogDebug("tx %p tx->tx_num %u, tx_id %"PRIu64, tx, tx->tx_num, (tx_id+1));
if ((tx_id+1) < tx->tx_num)
break;
else if ((tx_id+1) > tx->tx_num)
continue;
if (tx == dns_state->curr)
dns_state->curr = NULL;
if (tx->decoder_events != NULL) {
if (tx->decoder_events->cnt <= dns_state->events)
dns_state->events -= tx->decoder_events->cnt;
else
dns_state->events = 0;
}
TAILQ_REMOVE(&dns_state->tx_list, tx, next);
DNSTransactionFree(tx, state);
break;
}
SCReturn;
}
/** \internal
* \brief Find the DNS Tx in the state
* \param tx_id id of the tx
* \retval tx or NULL if not found */
DNSTransaction *DNSTransactionFindByTxId(const DNSState *dns_state, const uint16_t tx_id)
{
if (dns_state->curr == NULL)
return NULL;
/* fast path */
if (dns_state->curr->tx_id == tx_id) {
return dns_state->curr;
/* slow path, iterate list */
} else {
DNSTransaction *tx = NULL;
TAILQ_FOREACH(tx, &dns_state->tx_list, next) {
if (tx->tx_id == tx_id) {
return tx;
} else if ((dns_state->transaction_max - tx->tx_num) >
(dns_state->window - 1U)) {
tx->reply_lost = 1;
}
}
}
/* not found */
return NULL;
}
DetectEngineState *DNSGetTxDetectState(void *vtx)
{
DNSTransaction *tx = (DNSTransaction *)vtx;
return tx->de_state;
}
int DNSSetTxDetectState(void *vtx, DetectEngineState *s)
{
DNSTransaction *tx = (DNSTransaction *)vtx;
tx->de_state = s;
return 0;
}
void *DNSStateAlloc(void)
{
void *s = SCMalloc(sizeof(DNSState));
if (unlikely(s == NULL))
return NULL;
memset(s, 0, sizeof(DNSState));
DNSState *dns_state = (DNSState *)s;
DNSIncrMemcap(sizeof(DNSState), dns_state);
TAILQ_INIT(&dns_state->tx_list);
return s;
}
void DNSStateFree(void *s)
{
SCEnter();
if (s) {
DNSState *dns_state = (DNSState *) s;
DNSTransaction *tx = NULL;
while ((tx = TAILQ_FIRST(&dns_state->tx_list))) {
TAILQ_REMOVE(&dns_state->tx_list, tx, next);
DNSTransactionFree(tx, dns_state);
}
if (dns_state->buffer != NULL) {
DNSDecrMemcap(0xffff, dns_state); /** TODO update if/once we alloc
* in a smarter way */
SCFree(dns_state->buffer);
}
DNSDecrMemcap(sizeof(DNSState), dns_state);
BUG_ON(dns_state->memuse > 0);
SCFree(s);
}
SCReturn;
}
/** \brief Validation checks for DNS request header
*
* Will set decoder events if anomalies are found.
*
* \retval 0 ok
* \retval -1 error
*/
int DNSValidateRequestHeader(DNSState *dns_state, const DNSHeader *dns_header)
{
uint16_t flags = SCNtohs(dns_header->flags);
if ((flags & 0x8000) != 0) {
SCLogDebug("not a request 0x%04x", flags);
DNSSetEvent(dns_state, DNS_DECODER_EVENT_NOT_A_REQUEST);
goto bad_data;
}
if ((flags & 0x0040) != 0) {
SCLogDebug("Z flag not 0, 0x%04x", flags);
DNSSetEvent(dns_state, DNS_DECODER_EVENT_Z_FLAG_SET);
goto bad_data;
}
return 0;
bad_data:
return -1;
}
/** \brief Validation checks for DNS response header
*
* Will set decoder events if anomalies are found.
*
* \retval 0 ok
* \retval -1 error
*/
int DNSValidateResponseHeader(DNSState *dns_state, const DNSHeader *dns_header)
{
uint16_t flags = SCNtohs(dns_header->flags);
if ((flags & 0x8000) == 0) {
SCLogDebug("not a response 0x%04x", flags);
DNSSetEvent(dns_state, DNS_DECODER_EVENT_NOT_A_RESPONSE);
goto bad_data;
}
if ((flags & 0x0040) != 0) {
SCLogDebug("Z flag not 0, 0x%04x", flags);
DNSSetEvent(dns_state, DNS_DECODER_EVENT_Z_FLAG_SET);
goto bad_data;
}
return 0;
bad_data:
return -1;
}
/** \internal
* \brief check the query list to see if we already have this exact query
* \retval bool true or false
*/
static int QueryIsDuplicate(DNSTransaction *tx, const uint8_t *fqdn, const uint16_t fqdn_len,
const uint16_t type, const uint16_t class)
{
DNSQueryEntry *q = NULL;
TAILQ_FOREACH(q, &tx->query_list, next) {
uint8_t *qfqdn = (uint8_t *)q + sizeof(DNSQueryEntry);
if (q->len == fqdn_len && q->type == type &&
q->class == class &&
SCMemcmp(qfqdn, fqdn, fqdn_len) == 0) {
return TRUE;
}
}
return FALSE;
}
void DNSStoreQueryInState(DNSState *dns_state, const uint8_t *fqdn, const uint16_t fqdn_len,
const uint16_t type, const uint16_t class, const uint16_t tx_id)
{
/* flood protection */
if (dns_state->givenup)
return;
/* find the tx and see if this is an exact duplicate */
DNSTransaction *tx = DNSTransactionFindByTxId(dns_state, tx_id);
if ((tx != NULL) && (QueryIsDuplicate(tx, fqdn, fqdn_len, type, class) == TRUE)) {
SCLogDebug("query is duplicate");
return;
}
/* check flood limit */
if (dns_config.request_flood != 0 &&
dns_state->unreplied_cnt > dns_config.request_flood) {
DNSSetEvent(dns_state, DNS_DECODER_EVENT_FLOODED);
dns_state->givenup = 1;
}
if (tx == NULL) {
tx = DNSTransactionAlloc(dns_state, tx_id);
if (tx == NULL)
return;
dns_state->transaction_max++;
SCLogDebug("dns_state->transaction_max updated to %"PRIu64, dns_state->transaction_max);
TAILQ_INSERT_TAIL(&dns_state->tx_list, tx, next);
dns_state->curr = tx;
tx->tx_num = dns_state->transaction_max;
SCLogDebug("new tx %u with internal id %u", tx->tx_id, tx->tx_num);
dns_state->unreplied_cnt++;
}
if (DNSCheckMemcap((sizeof(DNSQueryEntry) + fqdn_len), dns_state) < 0)
return;
DNSQueryEntry *q = SCMalloc(sizeof(DNSQueryEntry) + fqdn_len);
if (unlikely(q == NULL))
return;
DNSIncrMemcap((sizeof(DNSQueryEntry) + fqdn_len), dns_state);
q->type = type;
q->class = class;
q->len = fqdn_len;
memcpy((uint8_t *)q + sizeof(DNSQueryEntry), fqdn, fqdn_len);
TAILQ_INSERT_TAIL(&tx->query_list, q, next);
SCLogDebug("Query for TX %04x stored", tx_id);
}
void DNSStoreAnswerInState(DNSState *dns_state, const int rtype, const uint8_t *fqdn,
const uint16_t fqdn_len, const uint16_t type, const uint16_t class, const uint16_t ttl,
const uint8_t *data, const uint16_t data_len, const uint16_t tx_id)
{
DNSTransaction *tx = DNSTransactionFindByTxId(dns_state, tx_id);
if (tx == NULL) {
tx = DNSTransactionAlloc(dns_state, tx_id);
if (tx == NULL)
return;
TAILQ_INSERT_TAIL(&dns_state->tx_list, tx, next);
dns_state->curr = tx;
dns_state->transaction_max++;
tx->tx_num = dns_state->transaction_max;
}
if (DNSCheckMemcap((sizeof(DNSAnswerEntry) + fqdn_len + data_len), dns_state) < 0)
return;
DNSAnswerEntry *q = SCMalloc(sizeof(DNSAnswerEntry) + fqdn_len + data_len);
if (unlikely(q == NULL))
return;
DNSIncrMemcap((sizeof(DNSAnswerEntry) + fqdn_len + data_len), dns_state);
q->type = type;
q->class = class;
q->ttl = ttl;
q->fqdn_len = fqdn_len;
q->data_len = data_len;
uint8_t *ptr = (uint8_t *)q + sizeof(DNSAnswerEntry);
if (fqdn != NULL && fqdn_len > 0) {
memcpy(ptr, fqdn, fqdn_len);
ptr += fqdn_len;
}
if (data != NULL && data_len > 0) {
memcpy(ptr, data, data_len);
}
if (rtype == DNS_LIST_ANSWER)
TAILQ_INSERT_TAIL(&tx->answer_list, q, next);
else if (rtype == DNS_LIST_AUTHORITY)
TAILQ_INSERT_TAIL(&tx->authority_list, q, next);
else
BUG_ON(1);
SCLogDebug("Answer for TX %04x stored", tx_id);
/* mark tx is as replied so we can log it */
tx->replied = 1;
}
/** \internal
* \brief get domain name from dns packet
*
* In case of compressed name storage this function follows the ptrs to
* create the full domain name.
*
* The length bytes are converted into dots, e.g. |03|com|00| becomes
* .com
* The trailing . is not stored.
*
* \param input input buffer (complete dns record)
* \param input_len lenght of input buffer
* \param offset offset into @input where dns name starts
* \param fqdn buffer to store result
* \param fqdn_size size of @fqdn buffer
* \retval 0 on error/no buffer
* \retval size size of fqdn
*/
static uint16_t DNSResponseGetNameByOffset(const uint8_t * const input, const uint32_t input_len,
const uint16_t offset, uint8_t *fqdn, const size_t fqdn_size)
{
if (offset >= input_len) {
SCLogDebug("input buffer too small for domain of len %u", offset);
goto insufficient_data;
}
int steps = 0;
uint16_t fqdn_offset = 0;
uint8_t length = *(input + offset);
const uint8_t *qdata = input + offset;
SCLogDebug("qry length %u", length);
if (length == 0) {
memcpy(fqdn, "<root>", 6);
SCReturnUInt(6U);
}
if ((uint64_t)((qdata + 1) - input) >= (uint64_t)input_len) {
SCLogDebug("input buffer too small");
goto insufficient_data;
}
while (length != 0) {
int cnt = 0;
while (length & 0xc0) {
uint16_t off = ((length & 0x3f) << 8) + *(qdata+1);
qdata = (const uint8_t *)input + off;
if ((uint64_t)((qdata + 1) - input) >= (uint64_t)input_len) {
SCLogDebug("input buffer too small");
goto insufficient_data;
}
length = *qdata;
SCLogDebug("qry length %u", length);
if (cnt++ == 100) {
SCLogDebug("too many pointer iterations, loop?");
goto bad_data;
}
}
qdata++;
if (length == 0) {
break;
}
if (input + input_len < qdata + length) {
SCLogDebug("input buffer too small for domain of len %u", length);
goto insufficient_data;
}
//PrintRawDataFp(stdout, qdata, length);
if ((size_t)(fqdn_offset + length + 1) < fqdn_size) {
memcpy(fqdn + fqdn_offset, qdata, length);
fqdn_offset += length;
fqdn[fqdn_offset++] = '.';
}
qdata += length;
/* if we're at the end of the input data, we're done */
if ((uint64_t)((qdata + 1) - input) == (uint64_t)input_len) {
break;
}
else if ((uint64_t)((qdata + 1) - input) > (uint64_t)input_len) {
SCLogDebug("input buffer too small");
goto insufficient_data;
}
length = *qdata;
SCLogDebug("qry length %u", length);
steps++;
if (steps >= 255)
goto bad_data;
}
if (fqdn_offset) {
fqdn_offset--;
}
//PrintRawDataFp(stdout, fqdn, fqdn_offset);
SCReturnUInt(fqdn_offset);
bad_data:
insufficient_data:
SCReturnUInt(0U);
}
/** \internal
* \brief skip past domain name field
*
* Skip the domain at position data. We don't care about following compressed names
* as we only want to know when the next part of the buffer starts
*
* \param input input buffer (complete dns record)
* \param input_len lenght of input buffer
* \param data current position
*
* \retval NULL on out of bounds data
* \retval sdata ptr to position in buffer past the name
*/
static const uint8_t *SkipDomain(const uint8_t * const input,
const uint32_t input_len, const uint8_t *data)
{
const uint8_t *sdata = data;
while (*sdata != 0x00) {
if (*sdata & 0xc0) {
sdata++;
break;
} else {
sdata += ((*sdata) + 1);
}
if (input + input_len < sdata) {
SCLogDebug("input buffer too small for data of len");
goto insufficient_data;
}
}
sdata++;
if (input + input_len < sdata) {
SCLogDebug("input buffer too small for data of len");
goto insufficient_data;
}
return sdata;
insufficient_data:
return NULL;
}
const uint8_t *DNSReponseParse(DNSState *dns_state, const DNSHeader * const dns_header,
const uint16_t num, const DnsListEnum list, const uint8_t * const input,
const uint32_t input_len, const uint8_t *data)
{
if (input + input_len < data + 2) {
SCLogDebug("input buffer too small for record 'name' field, record %u, "
"total answer_rr %u", num, SCNtohs(dns_header->answer_rr));
goto insufficient_data;
}
uint8_t fqdn[DNS_MAX_SIZE];
uint16_t fqdn_len = 0;
/* see if name is compressed */
if (!(data[0] & 0xc0)) {
if ((fqdn_len = DNSResponseGetNameByOffset(input, input_len,
data - input, fqdn, sizeof(fqdn))) == 0)
{
DNSSetEvent(dns_state, DNS_DECODER_EVENT_MALFORMED_DATA);
goto insufficient_data;
}
//PrintRawDataFp(stdout, fqdn, fqdn_len);
const uint8_t *tdata = SkipDomain(input, input_len, data);
if (tdata == NULL) {
goto insufficient_data;
}
data = tdata;
} else {
uint16_t offset = (data[0] & 0x3f) << 8 | data[1];
if ((fqdn_len = DNSResponseGetNameByOffset(input, input_len,
offset, fqdn, sizeof(fqdn))) == 0)
{
DNSSetEvent(dns_state, DNS_DECODER_EVENT_MALFORMED_DATA);
goto insufficient_data;
}
//PrintRawDataFp(stdout, fqdn, fqdn_len);
data += 2;
}
if (input + input_len < data + sizeof(DNSAnswerHeader)) {
SCLogDebug("input buffer too small for DNSAnswerHeader");
goto insufficient_data;
}
const DNSAnswerHeader *head = (DNSAnswerHeader *)data;
const uint16_t datalen = SCNtohs(head->len);
data += sizeof(DNSAnswerHeader);
SCLogDebug("head->len %u", SCNtohs(head->len));
if (input + input_len < data + SCNtohs(head->len)) {
SCLogDebug("input buffer too small for data of len %u", SCNtohs(head->len));
goto insufficient_data;
}
SCLogDebug("TTL %u", SCNtohl(head->ttl));
switch (SCNtohs(head->type)) {
case DNS_RECORD_TYPE_A:
{
if (datalen == 0 || datalen == 4) {
//PrintRawDataFp(stdout, data, SCNtohs(head->len));
//char a[16];
//PrintInet(AF_INET, (const void *)data, a, sizeof(a));
//SCLogInfo("A %s TTL %u", a, SCNtohl(head->ttl));
DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
SCNtohs(head->type), SCNtohs(head->class), SCNtohl(head->ttl),
data, datalen, SCNtohs(dns_header->tx_id));
} else {
SCLogDebug("invalid length for A response data: %u", SCNtohs(head->len));
goto bad_data;
}
data += datalen;
break;
}
case DNS_RECORD_TYPE_AAAA:
{
if (datalen == 0 || datalen == 16) {
//char a[46];
//PrintInet(AF_INET6, (const void *)data, a, sizeof(a));
//SCLogInfo("AAAA %s TTL %u", a, SCNtohl(head->ttl));
DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
SCNtohs(head->type), SCNtohs(head->class), SCNtohl(head->ttl),
data, datalen, SCNtohs(dns_header->tx_id));
} else {
SCLogDebug("invalid length for AAAA response data: %u", SCNtohs(head->len));
goto bad_data;
}
data += datalen;
break;
}
case DNS_RECORD_TYPE_MX:
case DNS_RECORD_TYPE_CNAME:
case DNS_RECORD_TYPE_PTR:
{
uint8_t name[DNS_MAX_SIZE];
uint16_t name_len = 0;
uint8_t skip = 0;
if (SCNtohs(head->type) == DNS_RECORD_TYPE_MX) {
// Skip the preference header
skip = 2;
}
if ((name_len = DNSResponseGetNameByOffset(input, input_len,
data - input + skip, name, sizeof(name))) == 0)
{
DNSSetEvent(dns_state, DNS_DECODER_EVENT_MALFORMED_DATA);
goto insufficient_data;
}
DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
SCNtohs(head->type), SCNtohs(head->class), SCNtohl(head->ttl),
name, name_len, SCNtohs(dns_header->tx_id));
data += SCNtohs(head->len);
break;
}
case DNS_RECORD_TYPE_NS:
case DNS_RECORD_TYPE_SOA:
{
uint8_t pname[DNS_MAX_SIZE];
uint16_t pname_len = 0;
if ((pname_len = DNSResponseGetNameByOffset(input, input_len,
data - input, pname, sizeof(pname))) == 0)
{
DNSSetEvent(dns_state, DNS_DECODER_EVENT_MALFORMED_DATA);
goto insufficient_data;
}
if (SCNtohs(head->type) == DNS_RECORD_TYPE_SOA) {
const uint8_t *sdata = SkipDomain(input, input_len, data);
if (sdata == NULL) {
goto insufficient_data;
}
uint8_t pmail[DNS_MAX_SIZE];
uint16_t pmail_len = 0;
SCLogDebug("getting pmail");
if ((pmail_len = DNSResponseGetNameByOffset(input, input_len,
sdata - input, pmail, sizeof(pmail))) == 0)
{
DNSSetEvent(dns_state, DNS_DECODER_EVENT_MALFORMED_DATA);
goto insufficient_data;
}
SCLogDebug("pmail_len %u", pmail_len);
//PrintRawDataFp(stdout, (uint8_t *)pmail, pmail_len);
const uint8_t *tdata = SkipDomain(input, input_len, sdata);
if (tdata == NULL) {
goto insufficient_data;
}
#if DEBUG
struct Trailer {
uint32_t serial;
uint32_t refresh;
uint32_t retry;
uint32_t experiation;
uint32_t minttl;
} *tail = (struct Trailer *)tdata;
if (input + input_len < tdata + sizeof(struct Trailer)) {
SCLogDebug("input buffer too small for data of len");
goto insufficient_data;
}
SCLogDebug("serial %u refresh %u retry %u exp %u min ttl %u",
SCNtohl(tail->serial), SCNtohl(tail->refresh),
SCNtohl(tail->retry), SCNtohl(tail->experiation),
SCNtohl(tail->minttl));
#endif
}
DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
SCNtohs(head->type), SCNtohs(head->class), SCNtohl(head->ttl),
pname, pname_len, SCNtohs(dns_header->tx_id));
data += SCNtohs(head->len);
break;
}
case DNS_RECORD_TYPE_TXT:
{
uint16_t txtdatalen = datalen;
if (txtdatalen == 0) {
DNSSetEvent(dns_state, DNS_DECODER_EVENT_MALFORMED_DATA);
goto bad_data;
}
uint8_t txtlen = *data;
const uint8_t *tdata = data + 1;
do {
//PrintRawDataFp(stdout, (uint8_t*)tdata, txtlen);
if (txtlen >= txtdatalen)
goto bad_data;
DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
SCNtohs(head->type), SCNtohs(head->class), SCNtohl(head->ttl),
(uint8_t*)tdata, (uint16_t)txtlen, SCNtohs(dns_header->tx_id));
txtdatalen -= txtlen;
tdata += txtlen;
txtlen = *tdata;
tdata++;
txtdatalen--;
SCLogDebug("datalen %u, txtlen %u", txtdatalen, txtlen);
} while (txtdatalen > 1);
data += datalen;
break;
}
case DNS_RECORD_TYPE_SSHFP:
{
/* data here should be:
* [1 byte algo][1 byte type][var bytes fingerprint]
* As we currently can't store each of those in the state,
* we just store the raw data an let the output/detect
* code figure out what to do with it. */
DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
SCNtohs(head->type), SCNtohs(head->class), SCNtohl(head->ttl),
data, SCNtohs(head->len), SCNtohs(dns_header->tx_id));
data += datalen;
break;
}
default: /* unsupported record */
{
DNSStoreAnswerInState(dns_state, list, NULL, 0,
SCNtohs(head->type), SCNtohs(head->class), SCNtohl(head->ttl),
NULL, 0, SCNtohs(dns_header->tx_id));
//PrintRawDataFp(stdout, data, SCNtohs(head->len));
data += datalen;
break;
}
}
return data;
bad_data:
insufficient_data:
return NULL;
}
void DNSCreateTypeString(uint16_t type, char *str, size_t str_size)
{
switch (type) {

@ -26,13 +26,9 @@
#include "app-layer-protos.h"
#include "app-layer-parser.h"
#include "flow.h"
#include "queue.h"
#include "util-byte.h"
#define DNS_MAX_SIZE 256
#define DNS_RECORD_TYPE_A 1
#define DNS_RECORD_TYPE_NS 2
#define DNS_RECORD_TYPE_MD 3 // Obsolete
@ -138,172 +134,10 @@ typedef struct DNSHeader_ {
uint16_t additional_rr;
} __attribute__((__packed__)) DNSHeader;
typedef struct DNSQueryTrailer_ {
uint16_t type;
uint16_t class;
} __attribute__((__packed__)) DNSQueryTrailer;
/** \brief DNS answer header
* packed as we don't want alignment to mess up sizeof() */
struct DNSAnswerHeader_ {
uint16_t type;
uint16_t class;
uint32_t ttl;
uint16_t len;
} __attribute__((__packed__));
typedef struct DNSAnswerHeader_ DNSAnswerHeader;
/** \brief List types in the TX.
* Used when storing answers from "Answer" or "Authority" */
typedef enum {
DNS_LIST_ANSWER = 0,
DNS_LIST_AUTHORITY,
} DnsListEnum;
/** \brief DNS Query storage. Stored in TX list.
*
* Layout is:
* [list ptr][2 byte type][2 byte class][2 byte len][...data...]
*/
typedef struct DNSQueryEntry_ {
TAILQ_ENTRY(DNSQueryEntry_) next;
uint16_t type;
uint16_t class;
uint16_t len;
} DNSQueryEntry;
/** \brief DNS Answer storage. Stored in TX list.
*
* Layout is:
* [list ptr][2 byte type][2 byte class][2 byte ttl] \
* [2 byte fqdn len][2 byte data len][...fqdn...][...data...]
*/
typedef struct DNSAnswerEntry_ {
TAILQ_ENTRY(DNSAnswerEntry_) next;
uint16_t type;
uint16_t class;
uint32_t ttl;
uint16_t fqdn_len;
uint16_t data_len;
} DNSAnswerEntry;
/** \brief DNS Transaction, request/reply with same TX id. */
typedef struct DNSTransaction_ {
uint16_t tx_num; /**< internal: id */
uint16_t tx_id; /**< transaction id */
uint16_t flags; /**< dns flags */
uint32_t logged; /**< flags for loggers done logging */
uint8_t replied; /**< bool indicating request is
replied to. */
uint8_t reply_lost;
uint8_t rcode; /**< response code (e.g. "no error" / "no such name") */
uint8_t recursion_desired; /**< server said "recursion desired" */
/** detection engine flags */
uint64_t detect_flags_ts;
uint64_t detect_flags_tc;
TAILQ_HEAD(, DNSQueryEntry_) query_list; /**< list for query/queries */
TAILQ_HEAD(, DNSAnswerEntry_) answer_list; /**< list for answers */
TAILQ_HEAD(, DNSAnswerEntry_) authority_list; /**< list for authority records */
AppLayerDecoderEvents *decoder_events; /**< per tx events */
TAILQ_ENTRY(DNSTransaction_) next;
DetectEngineState *de_state;
} DNSTransaction;
/** \brief Per flow DNS state container */
typedef struct DNSState_ {
TAILQ_HEAD(, DNSTransaction_) tx_list; /**< transaction list */
DNSTransaction *curr; /**< ptr to current tx */
DNSTransaction *iter;
uint64_t transaction_max;
uint32_t unreplied_cnt; /**< number of unreplied requests in a row */
uint32_t memuse; /**< state memuse, for comparing with
state-memcap settings */
struct timeval last_req; /**< Timestamp of last request. */
struct timeval last_resp; /**< Timestamp of last response. */
uint16_t window; /**< Window of allowed unreplied
* requests. Set by the maximum
* number of subsequent requests
* without a response. */
uint16_t events;
uint16_t givenup;
/* used by TCP only */
uint16_t offset;
uint16_t record_len;
uint8_t gap_ts; /**< Flag set when a gap has occurred. */
uint8_t gap_tc; /**< Flag set when a gap has occurred. */
uint8_t *buffer;
} DNSState;
#define DNS_CONFIG_DEFAULT_REQUEST_FLOOD 500
#define DNS_CONFIG_DEFAULT_STATE_MEMCAP 512*1024
#define DNS_CONFIG_DEFAULT_GLOBAL_MEMCAP 16*1024*1024
void DNSConfigInit(void);
void DNSConfigSetRequestFlood(uint32_t value);
void DNSConfigSetStateMemcap(uint32_t value);
void DNSConfigSetGlobalMemcap(uint64_t value);
void DNSIncrMemcap(uint32_t size, DNSState *state);
void DNSDecrMemcap(uint32_t size, DNSState *state);
int DNSCheckMemcap(uint32_t want, DNSState *state);
uint64_t DNSMemcapGetMemuseCounter(void);
uint64_t DNSMemcapGetMemcapStateCounter(void);
uint64_t DNSMemcapGetMemcapGlobalCounter(void);
void RegisterDNSParsers(void);
void DNSParserTests(void);
void DNSParserRegisterTests(void);
void DNSAppLayerDecoderEventsRegister(int alproto);
int DNSStateGetEventInfo(const char *event_name,
int *event_id, AppLayerEventType *event_type);
void DNSAppLayerRegisterGetEventInfo(uint8_t ipproto, AppProto alproto);
void *DNSGetTx(void *alstate, uint64_t tx_id);
uint64_t DNSGetTxCnt(void *alstate);
void DNSSetTxLogged(void *alstate, void *tx, LoggerId logged);
LoggerId DNSGetTxLogged(void *alstate, void *tx);
int DNSGetAlstateProgress(void *tx, uint8_t direction);
int DNSGetAlstateProgressCompletionStatus(uint8_t direction);
void DNSStateTransactionFree(void *state, uint64_t tx_id);
DNSTransaction *DNSTransactionFindByTxId(const DNSState *dns_state, const uint16_t tx_id);
DetectEngineState *DNSGetTxDetectState(void *vtx);
int DNSSetTxDetectState(void *vtx, DetectEngineState *s);
uint64_t DNSGetTxDetectFlags(void *vtx, uint8_t dir);
void DNSSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t detect_flags);
void DNSSetEvent(DNSState *s, uint8_t e);
void *DNSStateAlloc(void);
void DNSStateFree(void *s);
AppLayerDecoderEvents *DNSGetEvents(void *state, uint64_t id);
int DNSValidateRequestHeader(DNSState *, const DNSHeader *dns_header);
int DNSValidateResponseHeader(DNSState *, const DNSHeader *dns_header);
void DNSStoreQueryInState(DNSState *dns_state, const uint8_t *fqdn, const uint16_t fqdn_len,
const uint16_t type, const uint16_t class, const uint16_t tx_id);
void DNSStoreAnswerInState(DNSState *dns_state, const int rtype, const uint8_t *fqdn,
const uint16_t fqdn_len, const uint16_t type, const uint16_t class, const uint16_t ttl,
const uint8_t *data, const uint16_t data_len, const uint16_t tx_id);
const uint8_t *DNSReponseParse(DNSState *dns_state, const DNSHeader * const dns_header,
const uint16_t num, const DnsListEnum list, const uint8_t * const input,
const uint32_t input_len, const uint8_t *data);
uint16_t DNSUdpResponseGetNameByOffset(const uint8_t * const input, const uint32_t input_len,
const uint16_t offset, uint8_t *fqdn, const size_t fqdn_size);
void DNSCreateTypeString(uint16_t type, char *str, size_t str_size);
void DNSCreateRcodeString(uint8_t rcode, char *str, size_t str_size);

@ -23,888 +23,10 @@
#include "suricata-common.h"
#include "suricata.h"
#include "debug.h"
#include "decode.h"
#include "flow-util.h"
#include "threads.h"
#include "util-print.h"
#include "util-pool.h"
#include "util-debug.h"
#include "stream-tcp-private.h"
#include "stream-tcp-reassemble.h"
#include "stream-tcp.h"
#include "stream.h"
#include "app-layer-protos.h"
#include "app-layer-parser.h"
#include "util-spm.h"
#include "util-unittest.h"
#include "app-layer-dns-tcp.h"
#ifdef HAVE_RUST
#include "app-layer-dns-tcp-rust.h"
#endif
#ifndef HAVE_RUST
struct DNSTcpHeader_ {
uint16_t len;
uint16_t tx_id;
uint16_t flags;
uint16_t questions;
uint16_t answer_rr;
uint16_t authority_rr;
uint16_t additional_rr;
} __attribute__((__packed__));
typedef struct DNSTcpHeader_ DNSTcpHeader;
static uint16_t DNSTcpProbingParser(Flow *f, uint8_t direction,
uint8_t *input, uint32_t ilen,
uint8_t *rdir);
/** \internal
* \param input_len at least enough for the DNSTcpHeader
*/
static int DNSTCPRequestParseProbe(uint8_t *input, uint32_t input_len)
{
#ifdef DEBUG
BUG_ON(input_len < sizeof(DNSTcpHeader));
#endif
SCLogDebug("starting %u", input_len);
DNSTcpHeader *dns_tcp_header = (DNSTcpHeader *)input;
if (SCNtohs(dns_tcp_header->len) < sizeof(DNSHeader)) {
goto bad_data;
}
if (SCNtohs(dns_tcp_header->len) >= input_len) {
goto insufficient_data;
}
input += 2;
input_len -= 2;
DNSHeader *dns_header = (DNSHeader *)input;
uint16_t q;
const uint8_t *data = input + sizeof(DNSHeader);
for (q = 0; q < SCNtohs(dns_header->questions); q++) {
uint16_t fqdn_offset = 0;
if (input + input_len < data + 1) {
SCLogDebug("input buffer too small for len field");
goto insufficient_data;
}
SCLogDebug("query length %u", *data);
while (*data != 0) {
if (*data > 63) {
/** \todo set event?*/
goto bad_data;
}
uint8_t length = *data;
data++;
if (length > 0) {
if (input + input_len < data + length) {
SCLogDebug("input buffer too small for domain of len %u", length);
goto insufficient_data;
}
//PrintRawDataFp(stdout, data, qry->length);
if ((fqdn_offset + length + 1) < DNS_MAX_SIZE) {
fqdn_offset += length;
} else {
/** \todo set event? */
goto bad_data;
}
}
data += length;
if (input + input_len < data + 1) {
SCLogDebug("input buffer too small for new len");
goto insufficient_data;
}
SCLogDebug("qry length %u", *data);
}
if (fqdn_offset) {
fqdn_offset--;
}
data++;
if (input + input_len < data + sizeof(DNSQueryTrailer)) {
SCLogDebug("input buffer too small for DNSQueryTrailer");
goto insufficient_data;
}
#ifdef DEBUG
DNSQueryTrailer *trailer = (DNSQueryTrailer *)data;
SCLogDebug("trailer type %04x class %04x", SCNtohs(trailer->type), SCNtohs(trailer->class));
#endif
data += sizeof(DNSQueryTrailer);
}
SCReturnInt(1);
insufficient_data:
SCReturnInt(0);
bad_data:
SCReturnInt(-1);
}
static int BufferData(DNSState *dns_state, uint8_t *data, uint16_t len)
{
if (dns_state->buffer == NULL) {
if (DNSCheckMemcap(0xffff, dns_state) < 0)
return -1;
/** \todo be smarter about this, like use a pool or several pools for
* chunks of various sizes */
dns_state->buffer = SCMalloc(0xffff);
if (dns_state->buffer == NULL) {
return -1;
}
DNSIncrMemcap(0xffff, dns_state);
}
if ((uint32_t)len + (uint32_t)dns_state->offset > (uint32_t)dns_state->record_len) {
SCLogDebug("oh my, we have more data than the max record size. What do we do. WHAT DO WE DOOOOO!");
#ifdef DEBUG
BUG_ON(1);
#endif
len = dns_state->record_len - dns_state->offset;
}
memcpy(dns_state->buffer + dns_state->offset, data, len);
dns_state->offset += len;
return 0;
}
static void BufferReset(DNSState *dns_state)
{
dns_state->record_len = 0;
dns_state->offset = 0;
}
static int DNSRequestParseData(Flow *f, DNSState *dns_state, const uint8_t *input, const uint32_t input_len)
{
DNSHeader *dns_header = (DNSHeader *)input;
if (DNSValidateRequestHeader(dns_state, dns_header) < 0)
goto bad_data;
//SCLogInfo("ID %04x", SCNtohs(dns_header->tx_id));
uint16_t q;
const uint8_t *data = input + sizeof(DNSHeader);
//PrintRawDataFp(stdout, (uint8_t*)data, input_len - (data - input));
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++;
}
}
}
for (q = 0; q < SCNtohs(dns_header->questions); q++) {
uint8_t fqdn[DNS_MAX_SIZE];
uint16_t fqdn_offset = 0;
if (input + input_len < data + 1) {
SCLogDebug("input buffer too small for DNSTcpQuery");
goto insufficient_data;
}
SCLogDebug("query length %u", *data);
while (*data != 0) {
if (*data > 63) {
/** \todo set event?*/
goto insufficient_data;
}
uint8_t length = *data;
data++;
if (length > 0) {
if (input + input_len < data + length) {
SCLogDebug("input buffer too small for domain of len %u", length);
goto insufficient_data;
}
//PrintRawDataFp(stdout, data, qry->length);
if ((size_t)(fqdn_offset + length + 1) < sizeof(fqdn)) {
memcpy(fqdn + fqdn_offset, data, length);
fqdn_offset += length;
fqdn[fqdn_offset++] = '.';
} else {
/** \todo set event? */
goto insufficient_data;
}
}
data += length;
if (input + input_len < data + 1) {
SCLogDebug("input buffer too small for DNSTcpQuery(2)");
goto insufficient_data;
}
SCLogDebug("qry length %u", *data);
}
if (fqdn_offset) {
fqdn_offset--;
}
data++;
if (input + input_len < data + sizeof(DNSQueryTrailer)) {
SCLogDebug("input buffer too small for DNSQueryTrailer");
goto insufficient_data;
}
DNSQueryTrailer *trailer = (DNSQueryTrailer *)data;
SCLogDebug("trailer type %04x class %04x", SCNtohs(trailer->type), SCNtohs(trailer->class));
data += sizeof(DNSQueryTrailer);
/* store our data */
if (dns_state != NULL) {
DNSStoreQueryInState(dns_state, fqdn, fqdn_offset,
SCNtohs(trailer->type), SCNtohs(trailer->class),
SCNtohs(dns_header->tx_id));
}
}
SCReturnInt(1);
bad_data:
insufficient_data:
SCReturnInt(-1);
}
/** \internal
* \brief Parse DNS request packet
*/
static int DNSTCPRequestParse(Flow *f, void *dstate,
AppLayerParserState *pstate,
uint8_t *input, uint32_t input_len,
void *local_data, const uint8_t flags)
{
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);
}
/** \todo remove this when PP is fixed to enforce ipproto */
if (f != NULL && f->proto != IPPROTO_TCP)
SCReturnInt(-1);
/* probably a rst/fin sending an eof */
if (input == NULL || input_len == 0) {
goto insufficient_data;
}
/* Clear gap state. */
if (dns_state->gap_ts) {
if (DNSTcpProbingParser(f, STREAM_TOSERVER,
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)) {
SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSTcpHeader));
goto insufficient_data;
}
SCLogDebug("input_len %u offset %u record %u",
input_len, dns_state->offset, dns_state->record_len);
/* this is the first data of this record */
if (dns_state->offset == 0) {
DNSTcpHeader *dns_tcp_header = (DNSTcpHeader *)input;
SCLogDebug("DNS %p", dns_tcp_header);
if (SCNtohs(dns_tcp_header->len) < sizeof(DNSHeader)) {
/* bogus len, doesn't fit even basic dns header */
goto bad_data;
} else if (SCNtohs(dns_tcp_header->len) == (input_len-2)) {
/* we have all data, so process w/o buffering */
if (DNSRequestParseData(f, dns_state, input+2, input_len-2) < 0)
goto bad_data;
} else if ((input_len-2) > SCNtohs(dns_tcp_header->len)) {
/* we have all data, so process w/o buffering */
if (DNSRequestParseData(f, dns_state, input+2, SCNtohs(dns_tcp_header->len)) < 0)
goto bad_data;
/* treat the rest of the data as a (potential) new record */
input += (2 + SCNtohs(dns_tcp_header->len));
input_len -= (2 + SCNtohs(dns_tcp_header->len));
goto next_record;
} else {
/* not enough data, store record length and buffer */
dns_state->record_len = SCNtohs(dns_tcp_header->len);
BufferData(dns_state, input+2, input_len-2);
}
} else if (input_len + dns_state->offset < dns_state->record_len) {
/* we don't have the full record yet, buffer */
BufferData(dns_state, input, input_len);
} else if (input_len > (uint32_t)(dns_state->record_len - dns_state->offset)) {
/* more data than expected, we may have another record coming up */
uint16_t need = (dns_state->record_len - dns_state->offset);
BufferData(dns_state, input, need);
int r = DNSRequestParseData(f, dns_state, dns_state->buffer, dns_state->record_len);
BufferReset(dns_state);
if (r < 0)
goto bad_data;
/* treat the rest of the data as a (potential) new record */
input += need;
input_len -= need;
goto next_record;
} else {
/* implied exactly the amount of data we want
* add current to buffer, then inspect buffer */
BufferData(dns_state, input, input_len);
int r = DNSRequestParseData(f, dns_state, dns_state->buffer, dns_state->record_len);
BufferReset(dns_state);
if (r < 0)
goto bad_data;
}
if (f != NULL) {
dns_state->last_req = f->lastts;
}
SCReturnInt(1);
insufficient_data:
SCReturnInt(-1);
bad_data:
SCReturnInt(-1);
}
static int DNSReponseParseData(Flow *f, DNSState *dns_state, const uint8_t *input, const uint32_t input_len)
{
DNSHeader *dns_header = (DNSHeader *)input;
if (DNSValidateResponseHeader(dns_state, dns_header) < 0)
goto bad_data;
DNSTransaction *tx = NULL;
int found = 0;
if ((tx = DNSTransactionFindByTxId(dns_state, SCNtohs(dns_header->tx_id))) != NULL)
found = 1;
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--;
}
uint16_t q;
const uint8_t *data = input + sizeof(DNSHeader);
for (q = 0; q < SCNtohs(dns_header->questions); q++) {
uint8_t fqdn[DNS_MAX_SIZE];
uint16_t fqdn_offset = 0;
if (input + input_len < data + 1) {
SCLogDebug("input buffer too small for len field");
goto insufficient_data;
}
SCLogDebug("qry length %u", *data);
while (*data != 0) {
uint8_t length = *data;
data++;
if (length > 0) {
if (input + input_len < data + length) {
SCLogDebug("input buffer too small for domain of len %u", length);
goto insufficient_data;
}
//PrintRawDataFp(stdout, data, length);
if ((size_t)(fqdn_offset + length + 1) < sizeof(fqdn)) {
memcpy(fqdn + fqdn_offset, data, length);
fqdn_offset += length;
fqdn[fqdn_offset++] = '.';
}
}
data += length;
if (input + input_len < data + 1) {
SCLogDebug("input buffer too small for len field");
goto insufficient_data;
}
SCLogDebug("length %u", *data);
}
if (fqdn_offset) {
fqdn_offset--;
}
data++;
if (input + input_len < data + sizeof(DNSQueryTrailer)) {
SCLogDebug("input buffer too small for DNSQueryTrailer");
goto insufficient_data;
}
#if DEBUG
DNSQueryTrailer *trailer = (DNSQueryTrailer *)data;
SCLogDebug("trailer type %04x class %04x", SCNtohs(trailer->type), SCNtohs(trailer->class));
#endif
data += sizeof(DNSQueryTrailer);
}
for (q = 0; q < SCNtohs(dns_header->answer_rr); q++) {
data = DNSReponseParse(dns_state, dns_header, q, DNS_LIST_ANSWER,
input, input_len, data);
if (data == NULL) {
goto insufficient_data;
}
}
//PrintRawDataFp(stdout, (uint8_t *)data, input_len - (data - input));
for (q = 0; q < SCNtohs(dns_header->authority_rr); q++) {
data = DNSReponseParse(dns_state, dns_header, q, DNS_LIST_AUTHORITY,
input, input_len, data);
if (data == NULL) {
goto insufficient_data;
}
}
/* parse rcode, e.g. "noerror" or "nxdomain" */
uint8_t rcode = SCNtohs(dns_header->flags) & 0x0F;
if (rcode <= DNS_RCODE_NOTZONE) {
SCLogDebug("rcode %u", rcode);
if (tx != NULL)
tx->rcode = rcode;
} else {
/* this is not invalid, rcodes can be user defined */
SCLogDebug("unexpected DNS rcode %u", rcode);
}
if (SCNtohs(dns_header->flags) & 0x0080) {
SCLogDebug("recursion desired");
if (tx != NULL)
tx->recursion_desired = 1;
}
if (tx != NULL) {
tx->flags = ntohs(dns_header->flags);
tx->replied = 1;
}
SCReturnInt(1);
bad_data:
insufficient_data:
SCReturnInt(-1);
}
/** \internal
* \brief DNS TCP record parser, entry function
*
* Parses a DNS TCP record and fills the DNS state
*
* As TCP records can be 64k we'll have to buffer the data. Streaming parsing
* would have been _very_ tricky due to the way names are compressed in DNS
*
*/
static int DNSTCPResponseParse(Flow *f, void *dstate,
AppLayerParserState *pstate,
uint8_t *input, uint32_t input_len,
void *local_data, const uint8_t flags)
{
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);
}
/** \todo remove this when PP is fixed to enforce ipproto */
if (f != NULL && f->proto != IPPROTO_TCP)
SCReturnInt(-1);
/* probably a rst/fin sending an eof */
if (input == NULL || input_len == 0) {
goto insufficient_data;
}
/* Clear gap state. */
if (dns_state->gap_tc) {
if (DNSTcpProbingParser(f, STREAM_TOCLIENT,
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)) {
SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSTcpHeader));
goto insufficient_data;
}
SCLogDebug("input_len %u offset %u record %u",
input_len, dns_state->offset, dns_state->record_len);
/* this is the first data of this record */
if (dns_state->offset == 0) {
DNSTcpHeader *dns_tcp_header = (DNSTcpHeader *)input;
SCLogDebug("DNS %p", dns_tcp_header);
if (SCNtohs(dns_tcp_header->len) == 0) {
goto bad_data;
} else if (SCNtohs(dns_tcp_header->len) == (input_len-2)) {
/* we have all data, so process w/o buffering */
if (DNSReponseParseData(f, dns_state, input+2, input_len-2) < 0)
goto bad_data;
} else if ((input_len-2) > SCNtohs(dns_tcp_header->len)) {
/* we have all data, so process w/o buffering */
if (DNSReponseParseData(f, dns_state, input+2, SCNtohs(dns_tcp_header->len)) < 0)
goto bad_data;
/* treat the rest of the data as a (potential) new record */
input += (2 + SCNtohs(dns_tcp_header->len));
input_len -= (2 + SCNtohs(dns_tcp_header->len));
goto next_record;
} else {
/* not enough data, store record length and buffer */
dns_state->record_len = SCNtohs(dns_tcp_header->len);
BufferData(dns_state, input+2, input_len-2);
}
} else if (input_len + dns_state->offset < dns_state->record_len) {
/* we don't have the full record yet, buffer */
BufferData(dns_state, input, input_len);
} else if (input_len > (uint32_t)(dns_state->record_len - dns_state->offset)) {
/* more data than expected, we may have another record coming up */
uint16_t need = (dns_state->record_len - dns_state->offset);
BufferData(dns_state, input, need);
int r = DNSReponseParseData(f, dns_state, dns_state->buffer, dns_state->record_len);
BufferReset(dns_state);
if (r < 0)
goto bad_data;
/* treat the rest of the data as a (potential) new record */
input += need;
input_len -= need;
goto next_record;
} else {
/* implied exactly the amount of data we want
* add current to buffer, then inspect buffer */
BufferData(dns_state, input, input_len);
int r = DNSReponseParseData(f, dns_state, dns_state->buffer, dns_state->record_len);
BufferReset(dns_state);
if (r < 0)
goto bad_data;
}
if (f != NULL) {
dns_state->last_resp = f->lastts;
}
SCReturnInt(1);
insufficient_data:
SCReturnInt(-1);
bad_data:
SCReturnInt(-1);
}
static uint16_t DNSTcpProbingParser(Flow *f, uint8_t direction,
uint8_t *input, uint32_t ilen,
uint8_t *rdir)
{
if (ilen == 0 || ilen < sizeof(DNSTcpHeader)) {
SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSTcpHeader));
return ALPROTO_UNKNOWN;
}
DNSTcpHeader *dns_header = (DNSTcpHeader *)input;
if (SCNtohs(dns_header->len) < sizeof(DNSHeader)) {
/* length field bogus, won't even fit a minimal DNS header. */
return ALPROTO_FAILED;
} else if (SCNtohs(dns_header->len) > ilen) {
int r = DNSTCPRequestParseProbe(input, ilen);
if (r == -1) {
/* probing parser told us "bad data", so it's not
* DNS */
return ALPROTO_FAILED;
} else if (ilen > 512) {
SCLogDebug("all the parser told us was not enough data, which is expected. Lets assume it's DNS");
return ALPROTO_DNS;
}
SCLogDebug("not yet enough info %u > %u", SCNtohs(dns_header->len), ilen);
return ALPROTO_UNKNOWN;
}
int r = DNSTCPRequestParseProbe(input, ilen);
if (r != 1)
return ALPROTO_FAILED;
SCLogDebug("ALPROTO_DNS");
return ALPROTO_DNS;
}
/**
* \brief Probing parser for TCP DNS responses.
*
* This is a minimal parser that just checks that the input contains enough
* data for a TCP DNS response.
*/
static uint16_t DNSTcpProbeResponse(Flow *f, uint8_t direction,
uint8_t *input, uint32_t len,
uint8_t *rdir)
{
if (len == 0 || len < sizeof(DNSTcpHeader)) {
return ALPROTO_UNKNOWN;
}
DNSTcpHeader *dns_header = (DNSTcpHeader *)input;
if (SCNtohs(dns_header->len) < sizeof(DNSHeader)) {
return ALPROTO_FAILED;
}
return ALPROTO_DNS;
}
#endif /* HAVE_RUST */
void RegisterDNSTCPParsers(void)
{
#ifdef HAVE_RUST
RegisterRustDNSTCPParsers();
return;
#else
const char *proto_name = "dns";
/** DNS */
if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
AppLayerProtoDetectRegisterProtocol(ALPROTO_DNS, proto_name);
if (RunmodeIsUnittests()) {
AppLayerProtoDetectPPRegister(IPPROTO_TCP,
"53",
ALPROTO_DNS,
0, sizeof(DNSTcpHeader),
STREAM_TOSERVER,
DNSTcpProbingParser, NULL);
} else {
int have_cfg = AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
proto_name, ALPROTO_DNS,
0, sizeof(DNSTcpHeader),
DNSTcpProbingParser,
DNSTcpProbeResponse);
/* if we have no config, we enable the default port 53 */
if (!have_cfg) {
SCLogWarning(SC_ERR_DNS_CONFIG, "no DNS TCP config found, "
"enabling DNS detection on "
"port 53.");
AppLayerProtoDetectPPRegister(IPPROTO_TCP, "53",
ALPROTO_DNS, 0, sizeof(DNSTcpHeader),
STREAM_TOSERVER, DNSTcpProbingParser,
DNSTcpProbeResponse);
}
}
} else {
SCLogInfo("Protocol detection and parser disabled for %s protocol.",
proto_name);
return;
}
if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_DNS, STREAM_TOSERVER,
DNSTCPRequestParse);
AppLayerParserRegisterParser(IPPROTO_TCP , ALPROTO_DNS, STREAM_TOCLIENT,
DNSTCPResponseParse);
AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_DNS, DNSStateAlloc,
DNSStateFree);
AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_DNS,
DNSStateTransactionFree);
AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_DNS, DNSGetEvents);
AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_DNS,
DNSGetTxDetectState, DNSSetTxDetectState);
AppLayerParserRegisterDetectFlagsFuncs(IPPROTO_TCP, ALPROTO_DNS,
DNSGetTxDetectFlags, DNSSetTxDetectFlags);
AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_DNS, DNSGetTx);
AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_DNS, DNSGetTxCnt);
AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_DNS, DNSGetTxLogged,
DNSSetTxLogged);
AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_DNS,
DNSGetAlstateProgress);
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);
}
#ifdef UNITTESTS
AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_DNS,
DNSTCPParserRegisterTests);
#endif
return;
#endif /* HAVE_RUST */
}
/* UNITTESTS */
#ifndef HAVE_RUST
#ifdef UNITTESTS
#include "util-unittest-helper.h"
static int DNSTCPParserTestMultiRecord(void)
{
/* This is a buffer containing 20 DNS requests each prefixed by
* the request length for transport over TCP. It was generated with Scapy,
* where each request is:
* DNS(id=i, rd=1, qd=DNSQR(qname="%d.google.com" % i, qtype="A"))
* where i is 0 to 19.
*/
uint8_t req[] = {
0x00, 0x1e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x30,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1e, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x31,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1e, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x32,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1e, 0x00, 0x03, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x33,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1e, 0x00, 0x04, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x34,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1e, 0x00, 0x05, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x35,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1e, 0x00, 0x06, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x36,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1e, 0x00, 0x07, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x37,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1e, 0x00, 0x08, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1e, 0x00, 0x09, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x39,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1f, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31,
0x30, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00,
0x01, 0x00, 0x1f, 0x00, 0x0b, 0x01, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x31, 0x31, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01,
0x00, 0x01, 0x00, 0x1f, 0x00, 0x0c, 0x01, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x31, 0x32, 0x06, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00,
0x01, 0x00, 0x01, 0x00, 0x1f, 0x00, 0x0d, 0x01,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x31, 0x33, 0x06, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
0x00, 0x01, 0x00, 0x01, 0x00, 0x1f, 0x00, 0x0e,
0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x31, 0x34, 0x06, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d,
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x1f, 0x00,
0x0f, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x31, 0x35, 0x06, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f,
0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x1f,
0x00, 0x10, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x31, 0x36, 0x06,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
0x1f, 0x00, 0x11, 0x01, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31, 0x37,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x1f, 0x00, 0x12, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31,
0x38, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00,
0x01, 0x00, 0x1f, 0x00, 0x13, 0x01, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x31, 0x39, 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.2.3.4", "1.2.3.5", 1024, 53);
FAIL_IF_NULL(f);
f->proto = IPPROTO_TCP;
f->alproto = ALPROTO_DNS;
f->alstate = state;
FAIL_IF_NOT(DNSTCPRequestParse(f, f->alstate, NULL, req, reqlen, NULL, STREAM_START));
FAIL_IF(state->transaction_max != 20);
UTHFreeFlow(f);
PASS;
}
void DNSTCPParserRegisterTests(void)
{
UtRegisterTest("DNSTCPParserTestMultiRecord", DNSTCPParserTestMultiRecord);
}
#endif /* UNITTESTS */
#endif /* HAVE_RUST */

@ -24,15 +24,6 @@
#ifndef __APP_LAYER_DNS_TCP_H__
#define __APP_LAYER_DNS_TCP_H__
#include "app-layer-protos.h"
#include "app-layer-parser.h"
#include "app-layer-dns-common.h"
#include "flow.h"
#include "queue.h"
#include "util-byte.h"
void RegisterDNSTCPParsers(void);
void DNSTCPParserTests(void);
void DNSTCPParserRegisterTests(void);
#endif /* __APP_LAYER_DNS_TCP_H__ */

@ -21,966 +21,11 @@
*/
#include "suricata-common.h"
#include "suricata.h"
#include "conf.h"
#include "util-misc.h"
#include "debug.h"
#include "decode.h"
#include "flow-util.h"
#include "threads.h"
#include "util-print.h"
#include "util-pool.h"
#include "util-debug.h"
#include "stream-tcp-private.h"
#include "stream-tcp-reassemble.h"
#include "stream-tcp.h"
#include "stream.h"
#include "app-layer-protos.h"
#include "app-layer-parser.h"
#include "util-spm.h"
#include "util-unittest.h"
#include "app-layer-dns-udp.h"
#ifdef HAVE_RUST
#include "app-layer-dns-udp-rust.h"
#endif
#ifndef HAVE_RUST
/** \internal
* \brief Parse DNS request packet
*/
static int DNSUDPRequestParse(Flow *f, void *dstate,
AppLayerParserState *pstate,
uint8_t *input, uint32_t input_len,
void *local_data, const uint8_t flags)
{
DNSState *dns_state = (DNSState *)dstate;
SCLogDebug("starting %u", input_len);
if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
SCReturnInt(1);
}
/** \todo remove this when PP is fixed to enforce ipproto */
if (f != NULL && f->proto != IPPROTO_UDP)
SCReturnInt(-1);
if (input == NULL || input_len == 0 || input_len < sizeof(DNSHeader)) {
SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSHeader));
goto insufficient_data;
}
DNSHeader *dns_header = (DNSHeader *)input;
SCLogDebug("DNS %p", dns_header);
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 < SCNtohs(dns_header->questions); q++) {
uint8_t fqdn[DNS_MAX_SIZE];
uint16_t fqdn_offset = 0;
if (input + input_len < data + 1) {
SCLogDebug("input buffer too small for len");
goto insufficient_data;
}
SCLogDebug("query length %u", *data);
while (*data != 0) {
if (*data > 63) {
/** \todo set event?*/
goto insufficient_data;
}
uint8_t length = *data;
data++;
if (length == 0) {
break;
}
if (input + input_len < data + length) {
SCLogDebug("input buffer too small for domain of len %u", length);
goto insufficient_data;
}
//PrintRawDataFp(stdout, data, qry->length);
if ((size_t)(fqdn_offset + length + 1) < sizeof(fqdn)) {
memcpy(fqdn + fqdn_offset, data, length);
fqdn_offset += length;
fqdn[fqdn_offset++] = '.';
} else {
/** \todo set event? */
goto insufficient_data;
}
data += length;
if (input + input_len < data + 1) {
SCLogDebug("input buffer too small for len(2)");
goto insufficient_data;
}
SCLogDebug("qry length %u", *data);
}
if (fqdn_offset) {
fqdn_offset--;
}
data++;
if (input + input_len < data + sizeof(DNSQueryTrailer)) {
SCLogDebug("input buffer too small for DNSQueryTrailer");
goto insufficient_data;
}
DNSQueryTrailer *trailer = (DNSQueryTrailer *)data;
SCLogDebug("trailer type %04x class %04x", SCNtohs(trailer->type), SCNtohs(trailer->class));
data += sizeof(DNSQueryTrailer);
/* store our data */
if (dns_state != NULL) {
DNSStoreQueryInState(dns_state, fqdn, fqdn_offset,
SCNtohs(trailer->type), SCNtohs(trailer->class),
SCNtohs(dns_header->tx_id));
}
}
if (dns_state != NULL && f != NULL) {
dns_state->last_req = f->lastts;
}
SCReturnInt(1);
bad_data:
insufficient_data:
SCReturnInt(-1);
}
/** \internal
* \brief DNS UDP record parser, entry function
*
* Parses a DNS UDP record and fills the DNS state
*
*/
static int DNSUDPResponseParse(Flow *f, void *dstate,
AppLayerParserState *pstate,
uint8_t *input, uint32_t input_len,
void *local_data, const uint8_t flags)
{
DNSState *dns_state = (DNSState *)dstate;
SCLogDebug("starting %u", input_len);
if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
SCReturnInt(1);
}
/** \todo remove this when PP is fixed to enforce ipproto */
if (f != NULL && f->proto != IPPROTO_UDP)
SCReturnInt(-1);
if (input == NULL || input_len == 0 || input_len < sizeof(DNSHeader)) {
SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSHeader));
goto insufficient_data;
}
DNSHeader *dns_header = (DNSHeader *)input;
SCLogDebug("DNS %p %04x %04x", dns_header, SCNtohs(dns_header->tx_id), dns_header->flags);
DNSTransaction *tx = NULL;
int found = 0;
if ((tx = DNSTransactionFindByTxId(dns_state, SCNtohs(dns_header->tx_id))) != NULL)
found = 1;
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)
goto bad_data;
SCLogDebug("queries %04x", SCNtohs(dns_header->questions));
uint16_t q;
const uint8_t *data = input + sizeof(DNSHeader);
for (q = 0; q < SCNtohs(dns_header->questions); q++) {
uint8_t fqdn[DNS_MAX_SIZE];
uint16_t fqdn_offset = 0;
if (input + input_len < data + 1) {
SCLogDebug("input buffer too small for len");
goto insufficient_data;
}
SCLogDebug("qry length %u", *data);
while (*data != 0) {
uint8_t length = *data;
data++;
if (length == 0)
break;
if (input + input_len < data + length) {
SCLogDebug("input buffer too small for domain of len %u", length);
goto insufficient_data;
}
//PrintRawDataFp(stdout, data, length);
if ((size_t)(fqdn_offset + length + 1) < sizeof(fqdn)) {
memcpy(fqdn + fqdn_offset, data, length);
fqdn_offset += length;
fqdn[fqdn_offset++] = '.';
}
data += length;
if (input + input_len < data + 1) {
SCLogDebug("input buffer too small for len");
goto insufficient_data;
}
SCLogDebug("length %u", *data);
}
if (fqdn_offset) {
fqdn_offset--;
}
data++;
if (input + input_len < data + sizeof(DNSQueryTrailer)) {
SCLogDebug("input buffer too small for DNSQueryTrailer");
goto insufficient_data;
}
#if DEBUG
DNSQueryTrailer *trailer = (DNSQueryTrailer *)data;
SCLogDebug("trailer type %04x class %04x", SCNtohs(trailer->type), SCNtohs(trailer->class));
#endif
data += sizeof(DNSQueryTrailer);
}
SCLogDebug("answer_rr %04x", SCNtohs(dns_header->answer_rr));
for (q = 0; q < SCNtohs(dns_header->answer_rr); q++) {
data = DNSReponseParse(dns_state, dns_header, q, DNS_LIST_ANSWER,
input, input_len, data);
if (data == NULL) {
goto insufficient_data;
}
}
SCLogDebug("authority_rr %04x", SCNtohs(dns_header->authority_rr));
for (q = 0; q < SCNtohs(dns_header->authority_rr); q++) {
data = DNSReponseParse(dns_state, dns_header, q, DNS_LIST_AUTHORITY,
input, input_len, data);
if (data == NULL) {
goto insufficient_data;
}
}
/* if we previously didn't have a tx, it could have been created by the
* above code, so lets check again */
if (tx == NULL) {
tx = DNSTransactionFindByTxId(dns_state, SCNtohs(dns_header->tx_id));
}
if (tx != NULL) {
/* parse rcode, e.g. "noerror" or "nxdomain" */
uint8_t rcode = SCNtohs(dns_header->flags) & 0x0F;
if (rcode <= DNS_RCODE_NOTZONE) {
SCLogDebug("rcode %u", rcode);
tx->rcode = rcode;
} else {
/* this is not invalid, rcodes can be user defined */
SCLogDebug("unexpected DNS rcode %u", rcode);
}
if (SCNtohs(dns_header->flags) & 0x0080) {
SCLogDebug("recursion desired");
tx->recursion_desired = 1;
}
tx->flags = ntohs(dns_header->flags);
tx->replied = 1;
}
if (f != NULL) {
dns_state->last_resp = f->lastts;
}
SCReturnInt(1);
bad_data:
insufficient_data:
DNSSetEvent(dns_state, DNS_DECODER_EVENT_MALFORMED_DATA);
SCReturnInt(-1);
}
static uint16_t DNSUdpProbingParser(Flow *f, uint8_t direction,
uint8_t *input, uint32_t ilen, uint8_t *rdir)
{
if (ilen == 0 || ilen < sizeof(DNSHeader)) {
SCLogDebug("ilen too small, hoped for at least %"PRIuMAX, (uintmax_t)sizeof(DNSHeader));
return ALPROTO_UNKNOWN;
}
if (DNSUDPRequestParse(NULL, NULL, NULL, input, ilen, NULL, 0) == -1)
return ALPROTO_FAILED;
return ALPROTO_DNS;
}
static void DNSUDPConfigure(void)
{
uint32_t request_flood = DNS_CONFIG_DEFAULT_REQUEST_FLOOD;
uint32_t state_memcap = DNS_CONFIG_DEFAULT_STATE_MEMCAP;
uint64_t global_memcap = DNS_CONFIG_DEFAULT_GLOBAL_MEMCAP;
ConfNode *p = ConfGetNode("app-layer.protocols.dns.request-flood");
if (p != NULL) {
uint32_t value;
if (ParseSizeStringU32(p->val, &value) < 0) {
SCLogError(SC_ERR_DNS_CONFIG, "invalid value for request-flood %s", p->val);
} else {
request_flood = value;
}
}
SCLogConfig("DNS request flood protection level: %u", request_flood);
DNSConfigSetRequestFlood(request_flood);
p = ConfGetNode("app-layer.protocols.dns.state-memcap");
if (p != NULL) {
uint32_t value;
if (ParseSizeStringU32(p->val, &value) < 0) {
SCLogError(SC_ERR_DNS_CONFIG, "invalid value for state-memcap %s", p->val);
} else {
state_memcap = value;
}
}
SCLogConfig("DNS per flow memcap (state-memcap): %u", state_memcap);
DNSConfigSetStateMemcap(state_memcap);
p = ConfGetNode("app-layer.protocols.dns.global-memcap");
if (p != NULL) {
uint64_t value;
if (ParseSizeStringU64(p->val, &value) < 0) {
SCLogError(SC_ERR_DNS_CONFIG, "invalid value for global-memcap %s", p->val);
} else {
global_memcap = value;
}
}
SCLogConfig("DNS global memcap: %"PRIu64, global_memcap);
DNSConfigSetGlobalMemcap(global_memcap);
}
#endif /* HAVE_RUST */
void RegisterDNSUDPParsers(void)
{
#ifdef HAVE_RUST
return RegisterRustDNSUDPParsers();
#else
const char *proto_name = "dns";
/** DNS */
if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name)) {
AppLayerProtoDetectRegisterProtocol(ALPROTO_DNS, proto_name);
if (RunmodeIsUnittests()) {
AppLayerProtoDetectPPRegister(IPPROTO_UDP,
"53",
ALPROTO_DNS,
0, sizeof(DNSHeader),
STREAM_TOSERVER,
DNSUdpProbingParser,
NULL);
} else {
int have_cfg = AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP,
proto_name, ALPROTO_DNS,
0, sizeof(DNSHeader),
DNSUdpProbingParser, NULL);
/* if we have no config, we enable the default port 53 */
if (!have_cfg) {
#ifndef AFLFUZZ_APPLAYER
SCLogWarning(SC_ERR_DNS_CONFIG, "no DNS UDP config found, "
"enabling DNS detection on "
"port 53.");
#endif
AppLayerProtoDetectPPRegister(IPPROTO_UDP, "53",
ALPROTO_DNS, 0, sizeof(DNSHeader),
STREAM_TOSERVER, DNSUdpProbingParser, NULL);
}
}
} else {
SCLogInfo("Protocol detection and parser disabled for %s protocol.",
proto_name);
return;
}
if (AppLayerParserConfParserEnabled("udp", proto_name)) {
AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_DNS, STREAM_TOSERVER,
DNSUDPRequestParse);
AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_DNS, STREAM_TOCLIENT,
DNSUDPResponseParse);
AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_DNS, DNSStateAlloc,
DNSStateFree);
AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_DNS,
DNSStateTransactionFree);
AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_DNS, DNSGetEvents);
AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_DNS,
DNSGetTxDetectState, DNSSetTxDetectState);
AppLayerParserRegisterDetectFlagsFuncs(IPPROTO_UDP, ALPROTO_DNS,
DNSGetTxDetectFlags, DNSSetTxDetectFlags);
AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_DNS,
DNSGetTx);
AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_DNS,
DNSGetTxCnt);
AppLayerParserRegisterLoggerFuncs(IPPROTO_UDP, ALPROTO_DNS, DNSGetTxLogged,
DNSSetTxLogged);
AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP, ALPROTO_DNS,
DNSGetAlstateProgress);
AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_DNS,
DNSGetAlstateProgressCompletionStatus);
DNSAppLayerRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_DNS);
DNSUDPConfigure();
} else {
SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
"still on.", proto_name);
}
#ifdef UNITTESTS
AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_DNS, DNSUDPParserRegisterTests);
#endif
#endif /* HAVE_RUST */
}
/* UNITTESTS */
#ifndef HAVE_RUST
#ifdef UNITTESTS
#include "util-unittest-helper.h"
static int DNSUDPParserTest01 (void)
{
/* query: abcdefghijk.com
* TTL: 86400
* serial 20130422 refresh 28800 retry 7200 exp 604800 min ttl 86400
* ns, hostmaster */
uint8_t buf[] = { 0x00, 0x3c, 0x85, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x0b, 0x61, 0x62, 0x63,
0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x0f, 0x00,
0x01, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x01,
0x51, 0x80, 0x00, 0x25, 0x02, 0x6e, 0x73, 0x00,
0x0a, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61, 0x73,
0x74, 0x65, 0x72, 0xc0, 0x2f, 0x01, 0x33, 0x2a,
0x76, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x1c,
0x20, 0x00, 0x09, 0x3a, 0x80, 0x00, 0x01, 0x51,
0x80};
size_t buflen = sizeof(buf);
Flow *f = NULL;
f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 53);
FAIL_IF_NULL(f);
f->proto = IPPROTO_UDP;
f->alproto = ALPROTO_DNS;
f->alstate = DNSStateAlloc();
FAIL_IF_NOT(DNSUDPResponseParse(f, f->alstate, NULL, buf, buflen, NULL, STREAM_START));
UTHFreeFlow(f);
PASS;
}
static int DNSUDPParserTest02 (void)
{
uint8_t buf[] = {
0x6D,0x08,0x84,0x80,0x00,0x01,0x00,0x08,0x00,0x00,0x00,0x01,0x03,0x57,0x57,0x57,
0x04,0x54,0x54,0x54,0x54,0x03,0x56,0x56,0x56,0x03,0x63,0x6F,0x6D,0x02,0x79,0x79,
0x00,0x00,0x01,0x00,0x01,0xC0,0x0C,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,
0x02,0xC0,0x0C,0xC0,0x31,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,
0x31,0xC0,0x3F,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x3F,0xC0,
0x4D,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x4D,0xC0,0x5B,0x00,
0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x5B,0xC0,0x69,0x00,0x05,0x00,
0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x69,0xC0,0x77,0x00,0x05,0x00,0x01,0x00,
0x00,0x0E,0x10,0x00,0x02,0xC0,0x77,0xC0,0x85,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,
0x10,0x00,0x02,0xC0,0x85,0x00,0x00,0x29,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
size_t buflen = sizeof(buf);
Flow *f = NULL;
f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 53);
FAIL_IF_NULL(f);
f->proto = IPPROTO_UDP;
f->alproto = ALPROTO_DNS;
f->alstate = DNSStateAlloc();
FAIL_IF_NOT(DNSUDPResponseParse(f, f->alstate, NULL, buf, buflen, NULL, STREAM_START));
UTHFreeFlow(f);
PASS;
}
static int DNSUDPParserTest03 (void)
{
uint8_t buf[] = {
0x6F,0xB4,0x84,0x80,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x03,0x03,0x57,0x57,0x77,
0x0B,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x03,0x55,0x55,0x55,
0x02,0x79,0x79,0x00,0x00,0x01,0x00,0x01,0xC0,0x0C,0x00,0x05,0x00,0x01,0x00,0x00,
0x0E,0x10,0x00,0x02,0xC0,0x10,0xC0,0x34,0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,
0x00,0x04,0xC3,0xEA,0x04,0x19,0xC0,0x34,0x00,0x02,0x00,0x01,0x00,0x00,0x0E,0x10,
0x00,0x0A,0x03,0x6E,0x73,0x31,0x03,0x61,0x67,0x62,0xC0,0x20,0xC0,0x46,0x00,0x02,
0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x06,0x03,0x6E,0x73,0x32,0xC0,0x56,0xC0,0x52,
0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x04,0xC3,0xEA,0x04,0x0A,0xC0,0x68,
0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x04,0xC3,0xEA,0x05,0x14,0x00,0x00,
0x29,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
size_t buflen = sizeof(buf);
Flow *f = NULL;
f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 53);
FAIL_IF_NULL(f);
f->proto = IPPROTO_UDP;
f->alproto = ALPROTO_DNS;
f->alstate = DNSStateAlloc();
FAIL_IF_NOT(DNSUDPResponseParse(f, f->alstate, NULL, buf, buflen, NULL, STREAM_START));
UTHFreeFlow(f);
PASS;
}
/** \test TXT records in answer */
static int DNSUDPParserTest04 (void)
{
uint8_t buf[] = {
0xc2,0x2f,0x81,0x80,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x0a,0x41,0x41,0x41,
0x41,0x41,0x4f,0x31,0x6b,0x51,0x41,0x05,0x3d,0x61,0x75,0x74,0x68,0x03,0x73,0x72,
0x76,0x06,0x74,0x75,0x6e,0x6e,0x65,0x6c,0x03,0x63,0x6f,0x6d,0x00,0x00,0x10,0x00,
0x01,
/* answer record start */
0xc0,0x0c,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x22,
/* txt record starts: */
0x20, /* <txt len 32 */ 0x41,0x68,0x76,0x4d,0x41,0x41,0x4f,0x31,0x6b,0x41,0x46,
0x45,0x35,0x54,0x45,0x39,0x51,0x54,0x6a,0x46,0x46,0x4e,0x30,0x39,0x52,0x4e,0x31,
0x6c,0x59,0x53,0x44,0x6b,0x00, /* <txt len 0 */ 0xc0,0x1d,0x00,0x02,0x00,0x01,
0x00,0x09,0x3a,0x80,0x00,0x09,0x06,0x69,0x6f,0x64,0x69,0x6e,0x65,0xc0,0x21,0xc0,
0x6b,0x00,0x01,0x00,0x01,0x00,0x09,0x3a,0x80,0x00,0x04,0x0a,0x1e,0x1c,0x5f
};
size_t buflen = sizeof(buf);
Flow *f = NULL;
f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 53);
FAIL_IF_NULL(f);
f->proto = IPPROTO_UDP;
f->alproto = ALPROTO_DNS;
f->alstate = DNSStateAlloc();
FAIL_IF_NOT(DNSUDPResponseParse(f, f->alstate, NULL, buf, buflen, NULL, STREAM_START));
UTHFreeFlow(f);
PASS;
}
/** \test TXT records in answer, bad txtlen */
static int DNSUDPParserTest05 (void)
{
uint8_t buf[] = {
0xc2,0x2f,0x81,0x80,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x0a,0x41,0x41,0x41,
0x41,0x41,0x4f,0x31,0x6b,0x51,0x41,0x05,0x3d,0x61,0x75,0x74,0x68,0x03,0x73,0x72,
0x76,0x06,0x74,0x75,0x6e,0x6e,0x65,0x6c,0x03,0x63,0x6f,0x6d,0x00,0x00,0x10,0x00,
0x01,
/* answer record start */
0xc0,0x0c,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x22,
/* txt record starts: */
0x40, /* <txt len 64 */ 0x41,0x68,0x76,0x4d,0x41,0x41,0x4f,0x31,0x6b,0x41,0x46,
0x45,0x35,0x54,0x45,0x39,0x51,0x54,0x6a,0x46,0x46,0x4e,0x30,0x39,0x52,0x4e,0x31,
0x6c,0x59,0x53,0x44,0x6b,0x00, /* <txt len 0 */ 0xc0,0x1d,0x00,0x02,0x00,0x01,
0x00,0x09,0x3a,0x80,0x00,0x09,0x06,0x69,0x6f,0x64,0x69,0x6e,0x65,0xc0,0x21,0xc0,
0x6b,0x00,0x01,0x00,0x01,0x00,0x09,0x3a,0x80,0x00,0x04,0x0a,0x1e,0x1c,0x5f
};
size_t buflen = sizeof(buf);
Flow *f = NULL;
f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 53);
FAIL_IF_NULL(f);
f->proto = IPPROTO_UDP;
f->alproto = ALPROTO_DNS;
f->alstate = DNSStateAlloc();
FAIL_IF(DNSUDPResponseParse(f, f->alstate, NULL, buf, buflen, NULL, STREAM_START) != -1);
UTHFreeFlow(f);
PASS;
}
/**
* \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 two requests with an incrementing tx id. */
FAIL_IF_NOT(DNSUDPRequestParse(f, f->alstate, NULL, req, reqlen, NULL, STREAM_START));
req[1] = 0x02;
FAIL_IF_NOT(DNSUDPRequestParse(f, f->alstate, NULL, req, reqlen, NULL, 0));
/* Send response to the first request. */
FAIL_IF_NOT(DNSUDPResponseParse(f, f->alstate, NULL, res, reslen, NULL, STREAM_START));
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;
uint8_t flags = STREAM_START;
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, flags));
FAIL_IF(state->givenup);
flags = 0;
}
/* 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, 0));
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, STREAM_START));
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, 0));
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, 0));
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, 0));
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, 0));
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, 0));
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;
}
static int DNSUDPParserTxCleanup(void)
{
uint64_t ret[4];
/* 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);
Flow *f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 53);
FAIL_IF_NULL(f);
f->proto = IPPROTO_UDP;
f->protomap = FlowGetProtoMapping(IPPROTO_UDP);
f->alproto = ALPROTO_DNS;
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
FAIL_IF_NULL(alp_tctx);
/* First request. */
req[1] = 0x01;
int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_DNS,
STREAM_TOSERVER | STREAM_START, req, reqlen);
FAIL_IF_NOT(r == 0);
/* Second request. */
req[1] = 0x02;
r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_DNS,
STREAM_TOSERVER, req, reqlen);
FAIL_IF_NOT(r == 0);
/* Third request. */
req[1] = 0x03;
r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_DNS,
STREAM_TOSERVER, req, reqlen);
FAIL_IF_NOT(r == 0);
/* Now respond to the second. */
res[1] = 0x02;
r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_DNS,
STREAM_TOCLIENT, res, reslen);
FAIL_IF_NOT(r == 0);
/* Send a 4th request. */
req[1] = 0x04;
r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_DNS,
STREAM_TOSERVER, req, reqlen);
FAIL_IF_NOT(r == 0);
AppLayerParserTransactionsCleanup(f);
UTHAppLayerParserStateGetIds(f->alparser, &ret[0], &ret[1], &ret[2], &ret[3]);
FAIL_IF_NOT(ret[0] == 0); // inspect_id[0]
FAIL_IF_NOT(ret[1] == 0); // inspect_id[1]
FAIL_IF_NOT(ret[2] == 0); // log_id
FAIL_IF_NOT(ret[3] == 0); // min_id
/* Response to the third request. */
res[1] = 0x03;
r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_DNS,
STREAM_TOCLIENT, res, reslen);
FAIL_IF_NOT(r == 0);
DNSState *state = f->alstate;
DNSTransaction *tx = TAILQ_FIRST(&state->tx_list);
FAIL_IF_NULL(tx);
FAIL_IF(tx->replied);
FAIL_IF(!tx->reply_lost);
AppLayerParserTransactionsCleanup(f);
UTHAppLayerParserStateGetIds(f->alparser, &ret[0], &ret[1], &ret[2], &ret[3]);
FAIL_IF_NOT(ret[0] == 3); // inspect_id[0]
FAIL_IF_NOT(ret[1] == 3); // inspect_id[1]
FAIL_IF_NOT(ret[2] == 3); // log_id
FAIL_IF_NOT(ret[3] == 3); // min_id
/* Also free's state. */
UTHFreeFlow(f);
PASS;
}
void DNSUDPParserRegisterTests(void)
{
UtRegisterTest("DNSUDPParserTest01", DNSUDPParserTest01);
UtRegisterTest("DNSUDPParserTest02", DNSUDPParserTest02);
UtRegisterTest("DNSUDPParserTest03", DNSUDPParserTest03);
UtRegisterTest("DNSUDPParserTest04", DNSUDPParserTest04);
UtRegisterTest("DNSUDPParserTest05", DNSUDPParserTest05);
UtRegisterTest("DNSUDPParserTestFlood", DNSUDPParserTestFlood);
UtRegisterTest("DNSUDPParserTestDelayedResponse",
DNSUDPParserTestDelayedResponse);
UtRegisterTest("DNSUDPParserTestLostResponse",
DNSUDPParserTestLostResponse);
UtRegisterTest("DNSUDPParserTxCleanup",
DNSUDPParserTxCleanup);
}
#endif
#endif /* HAVE_RUST */

@ -23,15 +23,6 @@
#ifndef __APP_LAYER_DNS_UDP_H__
#define __APP_LAYER_DNS_UDP_H__
#include "app-layer-protos.h"
#include "app-layer-parser.h"
#include "app-layer-dns-common.h"
#include "flow.h"
#include "queue.h"
#include "util-byte.h"
void RegisterDNSUDPParsers(void);
void DNSUDPParserTests(void);
void DNSUDPParserRegisterTests(void);
#endif /* __APP_LAYER_DNS_UDP_H__ */

@ -852,9 +852,6 @@ void AppLayerProfilingStoreInternal(AppLayerThreadCtx *app_tctx, Packet *p)
*/
void AppLayerRegisterGlobalCounters(void)
{
StatsRegisterGlobalCounter("dns.memuse", DNSMemcapGetMemuseCounter);
StatsRegisterGlobalCounter("dns.memcap_state", DNSMemcapGetMemcapStateCounter);
StatsRegisterGlobalCounter("dns.memcap_global", DNSMemcapGetMemcapGlobalCounter);
StatsRegisterGlobalCounter("http.memuse", HTPMemuseGlobalCounter);
StatsRegisterGlobalCounter("http.memcap", HTPMemcapGlobalCounter);
StatsRegisterGlobalCounter("ftp.memuse", FTPMemuseGlobalCounter);

@ -60,9 +60,7 @@
#include "util-unittest-helper.h"
#ifdef HAVE_RUST
#include "rust-dns-dns-gen.h"
#endif
static int DetectDnsQuerySetup (DetectEngineCtx *, Signature *, const char *);
static void DetectDnsQueryRegisterTests(void);
@ -70,11 +68,7 @@ static int g_dns_query_buffer_id = 0;
struct DnsQueryGetDataArgs {
int local_id; /**< used as index into thread inspect array */
#ifdef HAVE_RUST
void *txv;
#else
const DNSQueryEntry *query;
#endif
};
static InspectionBuffer *DnsQueryGetData(DetectEngineThreadCtx *det_ctx,
@ -92,16 +86,10 @@ static InspectionBuffer *DnsQueryGetData(DetectEngineThreadCtx *det_ctx,
const uint8_t *data;
uint32_t data_len;
#ifdef HAVE_RUST
if (rs_dns_tx_get_query_name(cbdata->txv, (uint16_t)cbdata->local_id,
(uint8_t **)&data, &data_len) == 0) {
return NULL;
}
#else
const DNSQueryEntry *query = cbdata->query;
data = (const uint8_t *)((uint8_t *)query + sizeof(DNSQueryEntry));
data_len = query->len;
#endif
InspectionBufferSetup(buffer, data, data_len);
InspectionBufferApplyTransforms(buffer, transforms);
@ -121,7 +109,6 @@ static int DetectEngineInspectDnsQuery(
transforms = engine->v2.transforms;
}
#ifdef HAVE_RUST
while(1) {
struct DnsQueryGetDataArgs cbdata = { local_id, txv, };
InspectionBuffer *buffer = DnsQueryGetData(det_ctx,
@ -144,33 +131,6 @@ static int DetectEngineInspectDnsQuery(
}
local_id++;
}
#else
DNSTransaction *tx = (DNSTransaction *)txv;
DNSQueryEntry *query = NULL;
TAILQ_FOREACH(query, &tx->query_list, next)
{
struct DnsQueryGetDataArgs cbdata = { local_id, query };
InspectionBuffer *buffer = DnsQueryGetData(det_ctx,
transforms, f, &cbdata, engine->sm_list, false);
if (buffer == NULL || buffer->inspect == NULL)
break;
det_ctx->buffer_offset = 0;
det_ctx->discontinue_matching = 0;
det_ctx->inspection_recursion_counter = 0;
const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd,
f,
(uint8_t *)buffer->inspect,
buffer->inspect_len,
buffer->inspect_offset, DETECT_CI_FLAGS_SINGLE,
DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE, NULL);
if (match == 1) {
return DETECT_ENGINE_INSPECT_SIG_MATCH;
}
local_id++;
}
#endif
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
}
@ -200,7 +160,6 @@ static void PrefilterTxDnsQuery(DetectEngineThreadCtx *det_ctx,
const int list_id = ctx->list_id;
int local_id = 0;
#ifdef HAVE_RUST
while(1) {
// loop until we get a NULL
@ -218,23 +177,6 @@ static void PrefilterTxDnsQuery(DetectEngineThreadCtx *det_ctx,
local_id++;
}
#else
const DNSTransaction *tx = (DNSTransaction *)txv;
const DNSQueryEntry *query = NULL;
TAILQ_FOREACH(query, &tx->query_list, next)
{
struct DnsQueryGetDataArgs cbdata = { local_id, query };
InspectionBuffer *buffer = DnsQueryGetData(det_ctx, ctx->transforms,
f, &cbdata, list_id, true);
if (buffer != NULL && buffer->inspect_len >= mpm_ctx->minlen) {
(void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx,
&det_ctx->mtcu, &det_ctx->pmq,
buffer->inspect, buffer->inspect_len);
}
local_id++;
}
#endif
}
static void PrefilterMpmDnsQueryFree(void *ptr)
@ -333,7 +275,7 @@ static int DetectDnsQueryTest01(void)
0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
0x00, 0x10, 0x00, 0x01, };
Flow f;
DNSState *dns_state = NULL;
RSDNSState *dns_state = NULL;
Packet *p = NULL;
Signature *s = NULL;
ThreadVars tv;
@ -438,7 +380,7 @@ static int DetectDnsQueryTest02(void)
0x65, 0x03, 0x6E, 0x65, 0x74, 0x00,
0x00, 0x10, 0x00, 0x01, };
Flow f;
DNSState *dns_state = NULL;
RSDNSState *dns_state = NULL;
Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
Signature *s = NULL;
ThreadVars tv;
@ -592,7 +534,7 @@ static int DetectDnsQueryTest03(void)
0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
0x00, 0x10, 0x00, 0x01, };
Flow f;
DNSState *dns_state = NULL;
RSDNSState *dns_state = NULL;
Packet *p = NULL;
Signature *s = NULL;
ThreadVars tv;
@ -681,7 +623,7 @@ static int DetectDnsQueryTest04(void)
0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
0x00, 0x10, 0x00, 0x01, };
Flow f;
DNSState *dns_state = NULL;
RSDNSState *dns_state = NULL;
Packet *p1 = NULL, *p2 = NULL;
Signature *s = NULL;
ThreadVars tv;
@ -822,7 +764,7 @@ static int DetectDnsQueryTest05(void)
0x65, 0x03, 0x6E, 0x65, 0x74, 0x00,
0x00, 0x10, 0x00, 0x01, };
Flow f;
DNSState *dns_state = NULL;
RSDNSState *dns_state = NULL;
Packet *p1 = NULL, *p2 = NULL, *p3 = NULL, *p4 = NULL;
Signature *s = NULL;
ThreadVars tv;
@ -1008,7 +950,7 @@ static int DetectDnsQueryTest06(void)
0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
0x00, 0x10, 0x00, 0x01, };
Flow f;
DNSState *dns_state = NULL;
RSDNSState *dns_state = NULL;
Packet *p = NULL;
Signature *s = NULL;
ThreadVars tv;
@ -1124,7 +1066,7 @@ static int DetectDnsQueryTest07(void)
0x65, 0x03, 0x6E, 0x65, 0x74, 0x00,
0x00, 0x10, 0x00, 0x01, };
Flow f;
DNSState *dns_state = NULL;
RSDNSState *dns_state = NULL;
Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
Signature *s = NULL;
ThreadVars tv;

@ -189,7 +189,7 @@ static void AlertJsonDnp3(const Flow *f, const uint64_t tx_id, json_t *js)
static void AlertJsonDns(const Flow *f, const uint64_t tx_id, json_t *js)
{
DNSState *dns_state = (DNSState *)FlowGetAppState(f);
RSDNSState *dns_state = (RSDNSState *)FlowGetAppState(f);
if (dns_state) {
void *txptr = AppLayerParserGetTx(f->proto, ALPROTO_DNS,
dns_state, tx_id);

@ -51,11 +51,7 @@
#include "output-json.h"
#include "output-json-dns.h"
#ifdef HAVE_LIBJANSSON
#ifdef HAVE_RUST
#include "rust-dns-log-gen.h"
#endif
/* we can do query logging as well, but it's disabled for now as the
* TX id handling doesn't expect it */
@ -196,11 +192,7 @@ typedef enum {
DNS_VERSION_2
} DnsVersion;
#ifdef HAVE_RUST
#define DNS_VERSION_DEFAULT DNS_VERSION_2
#else
#define DNS_VERSION_DEFAULT DNS_VERSION_1
#endif
static struct {
const char *config_rrtype;
@ -281,200 +273,12 @@ typedef struct LogDnsLogThread_ {
MemBuffer *buffer;
} LogDnsLogThread;
#ifndef HAVE_RUST
static int DNSRRTypeEnabled(uint16_t type, uint64_t flags)
{
if (likely(flags == ~0UL)) {
return 1;
}
switch (type) {
case DNS_RECORD_TYPE_A:
return ((flags & LOG_A) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_NS:
return ((flags & LOG_NS) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_MD:
return ((flags & LOG_MD) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_MF:
return ((flags & LOG_MF) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_CNAME:
return ((flags & LOG_CNAME) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_SOA:
return ((flags & LOG_SOA) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_MB:
return ((flags & LOG_MB) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_MG:
return ((flags & LOG_MG) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_MR:
return ((flags & LOG_MR) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_NULL:
return ((flags & LOG_NULL) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_WKS:
return ((flags & LOG_WKS) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_PTR:
return ((flags & LOG_PTR) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_HINFO:
return ((flags & LOG_HINFO) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_MINFO:
return ((flags & LOG_MINFO) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_MX:
return ((flags & LOG_MX) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_TXT:
return ((flags & LOG_TXT) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_RP:
return ((flags & LOG_RP) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_AFSDB:
return ((flags & LOG_AFSDB) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_X25:
return ((flags & LOG_X25) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_ISDN:
return ((flags & LOG_ISDN) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_RT:
return ((flags & LOG_RT) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_NSAP:
return ((flags & LOG_NSAP) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_NSAPPTR:
return ((flags & LOG_NSAPPTR) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_SIG:
return ((flags & LOG_SIG) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_KEY:
return ((flags & LOG_KEY) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_PX:
return ((flags & LOG_PX) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_GPOS:
return ((flags & LOG_GPOS) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_AAAA:
return ((flags & LOG_AAAA) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_LOC:
return ((flags & LOG_LOC) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_NXT:
return ((flags & LOG_NXT) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_SRV:
return ((flags & LOG_SRV) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_ATMA:
return ((flags & LOG_ATMA) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_NAPTR:
return ((flags & LOG_NAPTR) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_KX:
return ((flags & LOG_KX) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_CERT:
return ((flags & LOG_CERT) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_A6:
return ((flags & LOG_A6) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_DNAME:
return ((flags & LOG_DNAME) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_OPT:
return ((flags & LOG_OPT) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_APL:
return ((flags & LOG_APL) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_DS:
return ((flags & LOG_DS) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_SSHFP:
return ((flags & LOG_SSHFP) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_IPSECKEY:
return ((flags & LOG_IPSECKEY) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_RRSIG:
return ((flags & LOG_RRSIG) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_NSEC:
return ((flags & LOG_NSEC) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_DNSKEY:
return ((flags & LOG_DNSKEY) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_DHCID:
return ((flags & LOG_DHCID) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_NSEC3:
return ((flags & LOG_NSEC3) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_NSEC3PARAM:
return ((flags & LOG_NSEC3PARAM) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_TLSA:
return ((flags & LOG_TLSA) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_HIP:
return ((flags & LOG_HIP) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_CDS:
return ((flags & LOG_CDS) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_CDNSKEY:
return ((flags & LOG_CDNSKEY) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_SPF:
return ((flags & LOG_SPF) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_TKEY:
return ((flags & LOG_TKEY) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_TSIG:
return ((flags & LOG_TSIG) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_MAILA:
return ((flags & LOG_MAILA) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_ANY:
return ((flags & LOG_ANY) != 0) ? 1 : 0;
case DNS_RECORD_TYPE_URI:
return ((flags & LOG_URI) != 0) ? 1 : 0;
default:
return 0;
}
}
#endif
#ifndef HAVE_RUST
static json_t *OutputQuery(DNSTransaction *tx, uint64_t tx_id, DNSQueryEntry *entry)
{
json_t *djs = json_object();
if (djs == NULL) {
return NULL;
}
/* type */
json_object_set_new(djs, "type", json_string("query"));
/* id */
json_object_set_new(djs, "id", json_integer(tx->tx_id));
/* query */
char *c;
c = BytesToString((uint8_t *)((uint8_t *)entry + sizeof(DNSQueryEntry)), entry->len);
if (c != NULL) {
json_object_set_new(djs, "rrname", SCJsonString(c));
SCFree(c);
}
/* name */
char record[16] = "";
DNSCreateTypeString(entry->type, record, sizeof(record));
json_object_set_new(djs, "rrtype", json_string(record));
/* tx id (tx counter) */
json_object_set_new(djs, "tx_id", json_integer(tx_id));
return djs;
}
static void LogQuery(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx,
uint64_t tx_id, DNSQueryEntry *entry)
{
SCLogDebug("got a DNS request and now logging !!");
if (!DNSRRTypeEnabled(entry->type, aft->dnslog_ctx->flags)) {
return;
}
json_t *djs = OutputQuery(tx, tx_id, entry);
if (djs == NULL) {
return;
}
/* reset */
MemBufferReset(aft->buffer);
/* dns */
json_object_set_new(js, "dns", djs);
OutputJSONBuffer(js, aft->dnslog_ctx->file_ctx, &aft->buffer);
json_object_del(js, "dns");
}
#endif
json_t *JsonDNSLogQuery(void *txptr, uint64_t tx_id)
{
json_t *queryjs = json_array();
if (queryjs == NULL)
return NULL;
#ifdef HAVE_RUST
for (uint16_t i = 0; i < UINT16_MAX; i++) {
json_t *dns = rs_dns_log_json_query((void *)txptr, i, LOG_ALL_RRTYPES);
if (unlikely(dns == NULL)) {
@ -482,548 +286,14 @@ json_t *JsonDNSLogQuery(void *txptr, uint64_t tx_id)
}
json_array_append_new(queryjs, dns);
}
#else
DNSTransaction *tx = txptr;
DNSQueryEntry *entry = NULL;
TAILQ_FOREACH(entry, &tx->query_list, next) {
json_t *qjs = OutputQuery(tx, tx_id, entry);
if (qjs != NULL) {
json_array_append_new(queryjs, qjs);
}
}
#endif
return queryjs;
}
#ifndef HAVE_RUST
static json_t *DnsParseSshFpType(DNSAnswerEntry *entry, uint8_t *ptr)
{
/* get algo and type */
uint8_t algo = *ptr;
uint8_t fptype = *(ptr+1);
/* turn fp raw buffer into a nice :-separate hex string */
uint16_t fp_len = (entry->data_len - 2);
uint8_t *dptr = ptr+2;
/* c-string for ':' separated hex and trailing \0. */
uint32_t output_len = fp_len * 3 + 1;
char hexstring[output_len];
memset(hexstring, 0x00, output_len);
uint16_t x;
for (x = 0; x < fp_len; x++) {
char one[4];
snprintf(one, sizeof(one), x == fp_len - 1 ? "%02x" : "%02x:", dptr[x]);
strlcat(hexstring, one, output_len);
}
/* wrap the whole thing in it's own structure */
json_t *hjs = json_object();
if (hjs == NULL) {
return NULL;
}
json_object_set_new(hjs, "fingerprint", json_string(hexstring));
json_object_set_new(hjs, "algo", json_integer(algo));
json_object_set_new(hjs, "type", json_integer(fptype));
return hjs;
}
static void OutputAnswerDetailed(DNSAnswerEntry *entry, json_t *js,
uint64_t flags)
{
do {
json_t *jdata = json_object();
if (jdata == NULL) {
return;
}
/* query */
if (entry->fqdn_len > 0) {
char *c;
c = BytesToString((uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry)),
entry->fqdn_len);
if (c != NULL) {
json_object_set_new(jdata, "rrname", json_string(c));
SCFree(c);
}
}
/* name */
char record[16] = "";
DNSCreateTypeString(entry->type, record, sizeof(record));
json_object_set_new(jdata, "rrtype", json_string(record));
/* ttl */
json_object_set_new(jdata, "ttl", json_integer(entry->ttl));
uint8_t *ptr = (uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry)+ entry->fqdn_len);
if (entry->type == DNS_RECORD_TYPE_A && entry->data_len == 4) {
char a[16] = "";
PrintInet(AF_INET, (const void *)ptr, a, sizeof(a));
json_object_set_new(jdata, "rdata", json_string(a));
} else if (entry->type == DNS_RECORD_TYPE_AAAA && entry->data_len == 16) {
char a[46] = "";
PrintInet(AF_INET6, (const void *)ptr, a, sizeof(a));
json_object_set_new(jdata, "rdata", json_string(a));
} else if (entry->data_len == 0) {
json_object_set_new(jdata, "rdata", json_string(""));
} 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 ||
entry->type == DNS_RECORD_TYPE_NS) {
if (entry->data_len != 0) {
char buffer[256] = "";
uint16_t copy_len = entry->data_len < (sizeof(buffer) - 1) ?
entry->data_len : sizeof(buffer) - 1;
memcpy(buffer, ptr, copy_len);
buffer[copy_len] = '\0';
json_object_set_new(jdata, "rdata", json_string(buffer));
} else {
json_object_set_new(jdata, "rdata", json_string(""));
}
} else if (entry->type == DNS_RECORD_TYPE_SSHFP) {
if (entry->data_len > 2) {
json_t *hjs = DnsParseSshFpType(entry, ptr);
if (hjs != NULL) {
json_object_set_new(jdata, "sshfp", hjs);
}
}
}
json_array_append_new(js, jdata);
} while ((entry = TAILQ_NEXT(entry, next)));
}
static void OutputAnswerGrouped(DNSAnswerEntry *entry, json_t *js)
{
struct {
#define ENTRY_TYPE_A 0
#define ENTRY_TYPE_AAAA 1
#define ENTRY_TYPE_TXT 2
#define ENTRY_TYPE_CNAME 3
#define ENTRY_TYPE_MX 4
#define ENTRY_TYPE_PTR 5
#define ENTRY_TYPE_NS 6
#define ENTRY_TYPE_SSHFP 7
#define ENTRY_TYPE_MAX 8
const char *name;
json_t *value;
} dns_rtypes[] = {
{ "A", NULL },
{ "AAAA", NULL },
{ "TXT", NULL },
{ "CNAME", NULL },
{ "MX", NULL },
{ "PTR", NULL },
{ "NS", NULL },
{ "SSHFP", NULL }
};
int i;
json_t *jrdata = json_object();
if (jrdata == NULL) {
return;
}
do {
uint8_t *ptr = (uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry)+ entry->fqdn_len);
if (entry->type == DNS_RECORD_TYPE_A && entry->data_len == 4) {
char a[16] = "";
if (dns_rtypes[ENTRY_TYPE_A].value == NULL) {
dns_rtypes[ENTRY_TYPE_A].value = json_array();
if (dns_rtypes[ENTRY_TYPE_A].value == NULL) {
goto out;
}
}
PrintInet(AF_INET, (const void *)ptr, a, sizeof(a));
json_array_append_new(dns_rtypes[ENTRY_TYPE_A].value, json_string(a));
} else if (entry->type == DNS_RECORD_TYPE_AAAA && entry->data_len == 16) {
char a[46] = "";
if (dns_rtypes[ENTRY_TYPE_AAAA].value == NULL) {
dns_rtypes[ENTRY_TYPE_AAAA].value = json_array();
if (dns_rtypes[ENTRY_TYPE_AAAA].value == NULL) {
goto out;
}
}
PrintInet(AF_INET6, (const void *)ptr, a, sizeof(a));
json_array_append_new(dns_rtypes[ENTRY_TYPE_AAAA].value, 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 || entry->type == DNS_RECORD_TYPE_CNAME ||
entry->type == DNS_RECORD_TYPE_MX || entry->type == DNS_RECORD_TYPE_PTR ||
entry->type == DNS_RECORD_TYPE_NS) {
if (entry->data_len != 0) {
char buffer[256] = "";
uint16_t copy_len = entry->data_len < (sizeof(buffer) - 1) ?
entry->data_len : sizeof(buffer) - 1;
memcpy(buffer, ptr, copy_len);
buffer[copy_len] = '\0';
if (entry->type == DNS_RECORD_TYPE_TXT) {
if (dns_rtypes[ENTRY_TYPE_TXT].value == NULL) {
dns_rtypes[ENTRY_TYPE_TXT].value = json_array();
if (dns_rtypes[ENTRY_TYPE_TXT].value == NULL) {
goto out;
}
}
json_array_append_new(dns_rtypes[ENTRY_TYPE_TXT].value, json_string(buffer));
} else if (entry->type == DNS_RECORD_TYPE_CNAME) {
if (dns_rtypes[ENTRY_TYPE_CNAME].value == NULL) {
dns_rtypes[ENTRY_TYPE_CNAME].value = json_array();
if (dns_rtypes[ENTRY_TYPE_CNAME].value == NULL) {
goto out;
}
}
json_array_append_new(dns_rtypes[ENTRY_TYPE_CNAME].value, json_string(buffer));
} else if (entry->type == DNS_RECORD_TYPE_MX) {
if (dns_rtypes[ENTRY_TYPE_MX].value == NULL) {
dns_rtypes[ENTRY_TYPE_MX].value = json_array();
if (dns_rtypes[ENTRY_TYPE_MX].value == NULL) {
goto out;
}
}
json_array_append_new(dns_rtypes[ENTRY_TYPE_MX].value, json_string(buffer));
} else if (entry->type == DNS_RECORD_TYPE_PTR) {
if (dns_rtypes[ENTRY_TYPE_PTR].value == NULL) {
dns_rtypes[ENTRY_TYPE_PTR].value = json_array();
if (dns_rtypes[ENTRY_TYPE_PTR].value == NULL) {
goto out;
}
}
json_array_append_new(dns_rtypes[ENTRY_TYPE_PTR].value, json_string(buffer));
} else if (entry->type == DNS_RECORD_TYPE_NS) {
if (dns_rtypes[ENTRY_TYPE_NS].value == NULL) {
dns_rtypes[ENTRY_TYPE_NS].value = json_array();
if (dns_rtypes[ENTRY_TYPE_NS].value == NULL) {
goto out;
}
}
json_array_append_new(dns_rtypes[ENTRY_TYPE_NS].value, json_string(buffer));
}
} else {
json_object_set_new(js, "rdata", json_string(""));
}
} else if (entry->type == DNS_RECORD_TYPE_SSHFP) {
if (entry->data_len > 2) {
json_t *hjs = DnsParseSshFpType(entry, ptr);
if (hjs != NULL) {
if (dns_rtypes[ENTRY_TYPE_SSHFP].value == NULL) {
dns_rtypes[ENTRY_TYPE_SSHFP].value = json_array();
if (dns_rtypes[ENTRY_TYPE_SSHFP].value == NULL) {
goto out;
}
}
json_array_append_new(dns_rtypes[ENTRY_TYPE_SSHFP].value, hjs);
}
}
}
} while ((entry = TAILQ_NEXT(entry, next)));
out:
for (i = 0; i < ENTRY_TYPE_MAX; i++) {
if (dns_rtypes[i].value != NULL) {
json_object_set_new(jrdata, dns_rtypes[i].name, dns_rtypes[i].value);
dns_rtypes[i].value = NULL;
}
}
json_object_set_new(js, "grouped", jrdata);
}
static void OutputAnswerV1(LogDnsLogThread *aft, json_t *djs,
DNSTransaction *tx, DNSAnswerEntry *entry)
{
if (!DNSRRTypeEnabled(entry->type, aft->dnslog_ctx->flags)) {
return;
}
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));
/* dns */
char flags[7] = "";
snprintf(flags, sizeof(flags), "%4x", tx->flags);
json_object_set_new(js, "flags", json_string(flags));
if (tx->flags & 0x8000)
json_object_set_new(js, "qr", json_true());
if (tx->flags & 0x0400)
json_object_set_new(js, "aa", json_true());
if (tx->flags & 0x0200)
json_object_set_new(js, "tc", json_true());
if (tx->flags & 0x0100)
json_object_set_new(js, "rd", json_true());
if (tx->flags & 0x0080)
json_object_set_new(js, "ra", json_true());
/* rcode */
char rcode[16] = "";
DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
json_object_set_new(js, "rcode", json_string(rcode));
/* query */
if (entry->fqdn_len > 0) {
char *c;
c = BytesToString((uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry)),
entry->fqdn_len);
if (c != NULL) {
json_object_set_new(js, "rrname", SCJsonString(c));
SCFree(c);
}
}
/* name */
char record[16] = "";
DNSCreateTypeString(entry->type, record, sizeof(record));
json_object_set_new(js, "rrtype", json_string(record));
/* ttl */
json_object_set_new(js, "ttl", json_integer(entry->ttl));
uint8_t *ptr = (uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry)+ entry->fqdn_len);
if (entry->type == DNS_RECORD_TYPE_A && entry->data_len == 4) {
char a[16] = "";
PrintInet(AF_INET, (const void *)ptr, a, sizeof(a));
json_object_set_new(js, "rdata", json_string(a));
} else if (entry->type == DNS_RECORD_TYPE_AAAA && entry->data_len == 16) {
char a[46] = "";
PrintInet(AF_INET6, (const void *)ptr, a, sizeof(a));
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 || entry->type == DNS_RECORD_TYPE_CNAME ||
entry->type == DNS_RECORD_TYPE_MX || entry->type == DNS_RECORD_TYPE_PTR ||
entry->type == DNS_RECORD_TYPE_NS) {
if (entry->data_len != 0) {
char buffer[256] = "";
uint16_t copy_len = entry->data_len < (sizeof(buffer) - 1) ?
entry->data_len : sizeof(buffer) - 1;
memcpy(buffer, ptr, copy_len);
buffer[copy_len] = '\0';
json_object_set_new(js, "rdata", SCJsonString(buffer));
} else {
json_object_set_new(js, "rdata", json_string(""));
}
} else if (entry->type == DNS_RECORD_TYPE_SSHFP) {
if (entry->data_len > 2) {
json_t *hjs = DnsParseSshFpType(entry, ptr);
if (hjs != NULL) {
json_object_set_new(js, "sshfp", hjs);
}
}
}
/* reset */
MemBufferReset(aft->buffer);
json_object_set_new(djs, "dns", js);
OutputJSONBuffer(djs, aft->dnslog_ctx->file_ctx, &aft->buffer);
json_object_del(djs, "dns");
return;
}
static json_t *BuildAnswer(DNSTransaction *tx, uint64_t tx_id, uint64_t flags,
DnsVersion version)
{
json_t *js = json_object();
if (js == NULL)
return NULL;
/* version */
if (version == DNS_VERSION_2) {
json_object_set_new(js, "version", json_integer(DNS_VERSION_2));
} else {
json_object_set_new(js, "version", json_integer(DNS_VERSION_1));
}
/* type */
json_object_set_new(js, "type", json_string("answer"));
/* id */
json_object_set_new(js, "id", json_integer(tx->tx_id));
/* flags */
char dns_flags[7] = "";
snprintf(dns_flags, sizeof(dns_flags), "%4x", tx->flags);
json_object_set_new(js, "flags", json_string(dns_flags));
if (tx->flags & 0x8000)
json_object_set_new(js, "qr", json_true());
if (tx->flags & 0x0400)
json_object_set_new(js, "aa", json_true());
if (tx->flags & 0x0200)
json_object_set_new(js, "tc", json_true());
if (tx->flags & 0x0100)
json_object_set_new(js, "rd", json_true());
if (tx->flags & 0x0080)
json_object_set_new(js, "ra", json_true());
/* rcode */
char rcode[16] = "";
DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
json_object_set_new(js, "rcode", json_string(rcode));
/* Log the query rrname and rrtype. Mostly useful on error, but
* still useful. */
DNSQueryEntry *query = TAILQ_FIRST(&tx->query_list);
if (query != NULL) {
char *c;
c = BytesToString((uint8_t *)((uint8_t *)query + sizeof(DNSQueryEntry)),
query->len);
if (c != NULL) {
json_object_set_new(js, "rrname", json_string(c));
SCFree(c);
}
char rrtype[16] = "";
DNSCreateTypeString(query->type, rrtype, sizeof(rrtype));
json_object_set_new(js, "rrtype", json_string(rrtype));
}
if (flags & LOG_FORMAT_DETAILED) {
if (!TAILQ_EMPTY(&tx->answer_list)) {
json_t *jarray = json_array();
if (jarray == NULL) {
json_decref(js);
return NULL;
}
OutputAnswerDetailed(TAILQ_FIRST(&tx->answer_list), jarray, flags);
json_object_set_new(js, "answers", jarray);
}
if (!TAILQ_EMPTY(&tx->authority_list)) {
json_t *js_authorities = json_array();
if (likely(js_authorities != NULL)) {
OutputAnswerDetailed(TAILQ_FIRST(&tx->authority_list),
js_authorities, flags);
json_object_set_new(js, "authorities", js_authorities);
}
}
}
if (!TAILQ_EMPTY(&tx->answer_list) && (flags & LOG_FORMAT_GROUPED)) {
OutputAnswerGrouped(TAILQ_FIRST(&tx->answer_list), js);
}
return js;
}
static void OutputAnswerV2(LogDnsLogThread *aft, json_t *djs,
DNSTransaction *tx)
{
json_t *dnsjs = BuildAnswer(tx, tx->tx_id, aft->dnslog_ctx->flags,
aft->dnslog_ctx->version);
if (dnsjs != NULL) {
/* reset */
MemBufferReset(aft->buffer);
json_object_set_new(djs, "dns", dnsjs);
OutputJSONBuffer(djs, aft->dnslog_ctx->file_ctx, &aft->buffer);
}
}
#endif
json_t *JsonDNSLogAnswer(void *txptr, uint64_t tx_id)
{
#ifdef HAVE_RUST
return rs_dns_log_json_answer(txptr, LOG_ALL_RRTYPES);
#else
DNSTransaction *tx = txptr;
DNSAnswerEntry *entry = TAILQ_FIRST(&tx->answer_list);
if (entry) {
return BuildAnswer(tx, tx_id, LOG_FORMAT_DETAILED, DNS_VERSION_2);
}
return NULL;
#endif
}
#ifndef HAVE_RUST
static void OutputFailure(LogDnsLogThread *aft, json_t *djs,
DNSTransaction *tx, DNSQueryEntry *entry) __attribute__((nonnull));
static void OutputFailure(LogDnsLogThread *aft, json_t *djs,
DNSTransaction *tx, DNSQueryEntry *entry)
{
if (!DNSRRTypeEnabled(entry->type, aft->dnslog_ctx->flags)) {
return;
}
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", SCJsonString(c));
SCFree(c);
}
/* reset */
MemBufferReset(aft->buffer);
json_object_set_new(djs, "dns", js);
OutputJSONBuffer(djs, aft->dnslog_ctx->file_ctx, &aft->buffer);
json_object_del(djs, "dns");
return;
}
#endif
#ifndef HAVE_RUST
static void LogAnswers(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx, uint64_t tx_id)
{
SCLogDebug("got a DNS response and now logging !!");
if (aft->dnslog_ctx->version == DNS_VERSION_2) {
DNSQueryEntry *query = TAILQ_FIRST(&tx->query_list);
if (query && !DNSRRTypeEnabled(query->type, aft->dnslog_ctx->flags)) {
return;
}
OutputAnswerV2(aft, js, tx);
} else {
DNSAnswerEntry *entry = 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);
}
}
TAILQ_FOREACH(entry, &tx->answer_list, next) {
OutputAnswerV1(aft, js, tx, entry);
}
TAILQ_FOREACH(entry, &tx->authority_list, next) {
OutputAnswerV1(aft, js, tx, entry);
}
}
}
#endif
static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data,
const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id)
@ -1038,7 +308,6 @@ static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data,
return TM_ECODE_OK;
}
#ifdef HAVE_RUST
for (uint16_t i = 0; i < 0xffff; i++) {
js = CreateJSONHeader(p, LOG_DIR_PACKET, "dns");
if (unlikely(js == NULL)) {
@ -1056,21 +325,6 @@ static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data,
OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer);
json_decref(js);
}
#else
DNSTransaction *tx = txptr;
DNSQueryEntry *query = NULL;
TAILQ_FOREACH(query, &tx->query_list, next) {
js = CreateJSONHeader(p, LOG_DIR_PACKET, "dns");
if (unlikely(js == NULL))
return TM_ECODE_OK;
JsonAddCommonOptions(&dnslog_ctx->cfg, p, f, js);
LogQuery(td, js, tx, tx_id, query);
json_decref(js);
}
#endif
SCReturnInt(TM_ECODE_OK);
}
@ -1093,7 +347,6 @@ static int JsonDnsLoggerToClient(ThreadVars *tv, void *thread_data,
JsonAddCommonOptions(&dnslog_ctx->cfg, p, f, js);
#if HAVE_RUST
if (td->dnslog_ctx->version == DNS_VERSION_2) {
json_t *answer = rs_dns_log_json_answer(txptr,
td->dnslog_ctx->flags);
@ -1128,11 +381,6 @@ static int JsonDnsLoggerToClient(ThreadVars *tv, void *thread_data,
json_object_del(js, "dns");
}
}
#else
DNSTransaction *tx = txptr;
LogAnswers(td, js, tx, tx_id);
#endif
json_decref(js);
@ -1447,11 +695,3 @@ void JsonDnsLogRegister (void)
JsonDnsLoggerToClient, 1, 1, LogDnsLogThreadInit, LogDnsLogThreadDeinit,
NULL);
}
#else
void JsonDnsLogRegister (void)
{
}
#endif

@ -26,11 +26,9 @@
void JsonDnsLogRegister(void);
#ifdef HAVE_LIBJANSSON
#include "app-layer-dns-common.h"
json_t *JsonDNSLogQuery(void *txptr, uint64_t tx_id) __attribute__((nonnull));
json_t *JsonDNSLogAnswer(void *txptr, uint64_t tx_id) __attribute__((nonnull));
#endif
#endif /* __OUTPUT_JSON_DNS_H__ */

@ -58,65 +58,29 @@
#include "util-lua-common.h"
#include "util-lua-dns.h"
#ifdef HAVE_RUST
#include "rust-dns-dns-gen.h"
#include "rust-dns-lua-gen.h"
#endif
static int DnsGetDnsRrname(lua_State *luastate)
{
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL) {
return LuaCallbackError(luastate, "internal error: no tx");
}
return rs_dns_lua_get_rrname(luastate, tx);
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
DNSQueryEntry *query = NULL;
TAILQ_FOREACH(query, &tx->query_list, next) {
char *c;
size_t input_len;
c = BytesToString((uint8_t *)((uint8_t *)query + sizeof(DNSQueryEntry)), query->len);
if (c != NULL) {
int ret;
input_len = strlen(c);
/* sanity check */
if (input_len > (size_t)(2 * query->len)) {
SCFree(c);
return LuaCallbackError(luastate, "invalid length");
}
ret = LuaPushStringBuffer(luastate, (uint8_t *)c, input_len);
SCFree(c);
return ret;
}
}
return LuaCallbackError(luastate, "no query");
#endif
}
static int DnsGetTxid(lua_State *luastate)
{
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL) {
return LuaCallbackError(luastate, "internal error: no tx");
}
rs_dns_lua_get_tx_id(luastate, tx);
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
lua_pushinteger(luastate, tx->tx_id);
#endif
return 1;
}
@ -125,19 +89,12 @@ static int DnsGetRcode(lua_State *luastate)
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
uint16_t rcode = 0;
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL) {
return LuaCallbackError(luastate, "internal error: no tx");
}
uint16_t flags = rs_dns_tx_get_response_flags(tx);
rcode = flags & 0x000f;
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
rcode = tx->rcode;
#endif
if (rcode) {
char rcode_str[16] = "";
DNSCreateRcodeString(rcode, rcode_str, sizeof(rcode_str));
@ -151,7 +108,6 @@ static int DnsGetRecursionDesired(lua_State *luastate)
{
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL) {
return LuaCallbackError(luastate, "internal error: no tx");
@ -159,13 +115,6 @@ static int DnsGetRecursionDesired(lua_State *luastate)
uint16_t flags = rs_dns_tx_get_response_flags(tx);
int recursion_desired = flags & 0x0080 ? 1 : 0;
lua_pushboolean(luastate, recursion_desired);
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
lua_pushboolean(luastate, tx->recursion_desired);
#endif
return 1;
}
@ -173,180 +122,29 @@ static int DnsGetQueryTable(lua_State *luastate)
{
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL) {
return LuaCallbackError(luastate, "internal error: no tx");
}
return rs_dns_lua_get_query_table(luastate, tx);
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
uint32_t u = 0;
lua_newtable(luastate);
DNSQueryEntry *query = NULL;
TAILQ_FOREACH(query, &tx->query_list, next) {
lua_pushinteger(luastate, u++);
lua_newtable(luastate);
char record[16] = "";
DNSCreateTypeString(query->type, record, sizeof(record));
lua_pushstring(luastate, "type");
lua_pushstring(luastate, record);
lua_settable(luastate, -3);
{
char *c;
size_t input_len;
c = BytesToString((uint8_t *)((uint8_t *)query + sizeof(DNSQueryEntry)), query->len);
if (c != NULL) {
input_len = strlen(c);
/* sanity check */
if (input_len > (size_t)(2 * query->len)) {
SCFree(c);
return LuaCallbackError(luastate, "invalid length");
}
lua_pushstring(luastate, "rrname");
LuaPushStringBuffer(luastate, (uint8_t *)c, input_len);
lua_settable(luastate, -3);
SCFree(c);
}
}
lua_settable(luastate, -3);
}
return 1;
#endif
}
static int DnsGetAnswerTable(lua_State *luastate)
{
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
return rs_dns_lua_get_answer_table(luastate, tx);
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
uint32_t u = 0;
lua_newtable(luastate);
DNSAnswerEntry *answer = NULL;
TAILQ_FOREACH(answer, &tx->answer_list, next) {
lua_pushinteger(luastate, u++);
lua_newtable(luastate);
char record[16] = "";
DNSCreateTypeString(answer->type, record, sizeof(record));
lua_pushstring(luastate, "type");
lua_pushstring(luastate, record);
lua_settable(luastate, -3);
lua_pushstring(luastate, "ttl");
lua_pushinteger(luastate, answer->ttl);
lua_settable(luastate, -3);
{
uint8_t *ptr = (uint8_t *)((uint8_t *)answer + sizeof(DNSAnswerEntry));
lua_pushstring(luastate, "rrname");
LuaPushStringBuffer(luastate, ptr, answer->fqdn_len);
lua_settable(luastate, -3);
ptr = (uint8_t *)((uint8_t *)answer + sizeof(DNSAnswerEntry) + answer->fqdn_len);
if (answer->type == DNS_RECORD_TYPE_A) {
char a[16] = "";
if (answer->data_len == 4) {
PrintInet(AF_INET, (const void *)ptr, a, sizeof(a));
}
lua_pushstring(luastate, "addr");
LuaPushStringBuffer(luastate, (uint8_t *)a, strlen(a));
lua_settable(luastate, -3);
} else if (answer->type == DNS_RECORD_TYPE_AAAA) {
char a[46] = "";
if (answer->data_len == 16) {
PrintInet(AF_INET6, (const void *)ptr, a, sizeof(a));
}
lua_pushstring(luastate, "addr");
LuaPushStringBuffer(luastate, (uint8_t *)a, strlen(a));
lua_settable(luastate, -3);
} else if (answer->data_len == 0) {
/* not setting 'addr' */
} else {
lua_pushstring(luastate, "addr");
LuaPushStringBuffer(luastate, (uint8_t *)ptr, answer->data_len);
lua_settable(luastate, -3);
}
}
lua_settable(luastate, -3);
}
return 1;
#endif
}
static int DnsGetAuthorityTable(lua_State *luastate)
{
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
return rs_dns_lua_get_authority_table(luastate, tx);
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
uint32_t u = 0;
lua_newtable(luastate);
DNSAnswerEntry *answer = NULL;
TAILQ_FOREACH(answer, &tx->authority_list, next) {
lua_pushinteger(luastate, u++);
lua_newtable(luastate);
char record[16] = "";
DNSCreateTypeString(answer->type, record, sizeof(record));
lua_pushstring(luastate, "type");
lua_pushstring(luastate, record);
lua_settable(luastate, -3);
lua_pushstring(luastate, "ttl");
lua_pushinteger(luastate, answer->ttl);
lua_settable(luastate, -3);
{
char *c;
size_t input_len;
c = BytesToString((uint8_t *)((uint8_t *)answer + sizeof(DNSAnswerEntry)), answer->fqdn_len);
if (c != NULL) {
input_len = strlen(c);
/* sanity check */
if (input_len > (size_t)(2 * answer->fqdn_len)) {
SCFree(c);
return LuaCallbackError(luastate, "invalid length");
}
lua_pushstring(luastate, "rrname");
LuaPushStringBuffer(luastate, (uint8_t *)c, input_len);
lua_settable(luastate, -3);
SCFree(c);
}
}
lua_settable(luastate, -3);
}
return 1;
#endif
}
/** \brief register http lua extensions in a luastate */
int LuaRegisterDnsFunctions(lua_State *luastate)
{

Loading…
Cancel
Save