aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/suricata/src/app-layer-dns-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/suricata/src/app-layer-dns-common.c')
-rw-r--r--framework/src/suricata/src/app-layer-dns-common.c1141
1 files changed, 0 insertions, 1141 deletions
diff --git a/framework/src/suricata/src/app-layer-dns-common.c b/framework/src/suricata/src/app-layer-dns-common.c
deleted file mode 100644
index 3c67fe44..00000000
--- a/framework/src/suricata/src/app-layer-dns-common.c
+++ /dev/null
@@ -1,1141 +0,0 @@
-/* Copyright (C) 2013-2014 Open Information Security Foundation
- *
- * You can copy, redistribute or modify this Program under the terms of
- * the GNU General Public License version 2 as published by the Free
- * Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-/**
- * \file
- *
- * \author Victor Julien <victor@inliniac.net>
- */
-
-#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, },
- { "MALFORMED_DATA", DNS_DECODER_EVENT_MALFORMED_DATA, },
- { "NOT_A_REQUEST", DNS_DECODER_EVENT_NOT_A_REQUEST, },
- { "NOT_A_RESPONSE", DNS_DECODER_EVENT_NOT_A_RESPONSE, },
- { "Z_FLAG_SET", DNS_DECODER_EVENT_Z_FLAG_SET, },
- { "FLOODED", DNS_DECODER_EVENT_FLOODED, },
- { "STATE_MEMCAP_REACHED", DNS_DECODER_EVENT_STATE_MEMCAP_REACHED, },
-
- { NULL, -1 },
-};
-
-int DNSStateGetEventInfo(const char *event_name,
- int *event_id, AppLayerEventType *event_type)
-{
- *event_id = SCMapEnumNameToValue(event_name, dns_decoder_event_table);
- if (*event_id == -1) {
- SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
- "dns's enum map table.", event_name);
- /* this should be treated as fatal */
- return -1;
- }
-
- *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
-
- return 0;
-}
-
-void DNSAppLayerRegisterGetEventInfo(uint8_t ipproto, AppProto alproto)
-{
- AppLayerParserRegisterGetEventInfo(ipproto, alproto, DNSStateGetEventInfo);
-
- 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;
-}
-
-int DNSHasEvents(void *state)
-{
- DNSState *dns_state = (DNSState *)state;
- return (dns_state->events > 0);
-}
-
-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;
- }
-}
-
-/** \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);
- BUG_ON(state->tx_with_detect_state_cnt == 0);
- state->tx_with_detect_state_cnt--;
- }
-
- 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;
- }
- }
- }
- /* not found */
- return NULL;
-}
-
-int DNSStateHasTxDetectState(void *alstate)
-{
- DNSState *state = (DNSState *)alstate;
- return (state->tx_with_detect_state_cnt > 0);
-}
-
-DetectEngineState *DNSGetTxDetectState(void *vtx)
-{
- DNSTransaction *tx = (DNSTransaction *)vtx;
- return tx->de_state;
-}
-
-int DNSSetTxDetectState(void *alstate, void *vtx, DetectEngineState *s)
-{
- DNSState *state = (DNSState *)alstate;
- DNSTransaction *tx = (DNSTransaction *)vtx;
- state->tx_with_detect_state_cnt++;
- 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);
- }
-
- BUG_ON(dns_state->tx_with_detect_state_cnt > 0);
-
- 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 = ntohs(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 = ntohs(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;
- }
-
- /* see if the last tx is unreplied */
- if (dns_state->curr != tx && dns_state->curr != NULL &&
- dns_state->curr->replied == 0)
- {
- dns_state->curr->reply_lost = 1;
- dns_state->unreplied_cnt++;
-
- /* 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);
- }
-
- 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;
- 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;
-
- /* reset unreplied counter */
- dns_state->unreplied_cnt = 0;
-}
-
-/** \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 (input + input_len < input + offset + 1) {
- 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);
- }
-
- while (length != 0) {
- int cnt = 0;
- while (length & 0xc0) {
- uint16_t offset = ((length & 0x3f) << 8) + *(qdata+1);
- qdata = (const uint8_t *)input + offset;
-
- if (input + input_len < qdata + 1) {
- 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 (input + input_len < qdata + 1) {
- SCLogDebug("input buffer too small for len field");
- 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, ntohs(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)
- {
-#if DEBUG
- PrintRawDataFp(stdout, (uint8_t *)input, input_len);
- BUG_ON(1);
-#endif
- 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)
- {
-#if DEBUG
- PrintRawDataFp(stdout, (uint8_t *)input, input_len);
- BUG_ON(1);
-#endif
- 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;
-
- data += sizeof(DNSAnswerHeader);
-
- SCLogDebug("head->len %u", ntohs(head->len));
-
- if (input + input_len < data + ntohs(head->len)) {
- SCLogDebug("input buffer too small for data of len %u", ntohs(head->len));
- goto insufficient_data;
- }
-
- SCLogDebug("TTL %u", ntohl(head->ttl));
-
- switch (ntohs(head->type)) {
- case DNS_RECORD_TYPE_A:
- {
- if (ntohs(head->len) == 4) {
- //PrintRawDataFp(stdout, data, ntohs(head->len));
- //char a[16];
- //PrintInet(AF_INET, (const void *)data, a, sizeof(a));
- //SCLogInfo("A %s TTL %u", a, ntohl(head->ttl));
-
- DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
- ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
- data, 4, ntohs(dns_header->tx_id));
- } else {
- SCLogDebug("invalid length for A response data: %u", ntohs(head->len));
- goto bad_data;
- }
-
- data += ntohs(head->len);
- break;
- }
- case DNS_RECORD_TYPE_AAAA:
- {
- if (ntohs(head->len) == 16) {
- //char a[46];
- //PrintInet(AF_INET6, (const void *)data, a, sizeof(a));
- //SCLogInfo("AAAA %s TTL %u", a, ntohl(head->ttl));
-
- DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
- ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
- data, 16, ntohs(dns_header->tx_id));
- } else {
- SCLogDebug("invalid length for AAAA response data: %u", ntohs(head->len));
- goto bad_data;
- }
-
- data += ntohs(head->len);
- break;
- }
- case DNS_RECORD_TYPE_MX:
- case DNS_RECORD_TYPE_CNAME:
- case DNS_RECORD_TYPE_PTR:
- {
- uint8_t name[DNS_MAX_SIZE];
- uint16_t name_len = 0;
- uint8_t skip = 0;
-
- if (ntohs(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) {
-#if DEBUG
- PrintRawDataFp(stdout, (uint8_t *)input, input_len);
- BUG_ON(1);
-#endif
- goto insufficient_data;
- }
-
- DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
- ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
- name, name_len, ntohs(dns_header->tx_id));
-
- data += ntohs(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)
- {
-#if DEBUG
- PrintRawDataFp(stdout, (uint8_t *)input, input_len);
- BUG_ON(1);
-#endif
- goto insufficient_data;
- }
-
- if (ntohs(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)
- {
-#if DEBUG
- PrintRawDataFp(stdout, (uint8_t *)input, input_len);
- BUG_ON(1);
-#endif
- 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",
- ntohl(tail->serial), ntohl(tail->refresh),
- ntohl(tail->retry), ntohl(tail->experiation),
- ntohl(tail->minttl));
-#endif
- }
-
- DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
- ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
- pname, pname_len, ntohs(dns_header->tx_id));
-
- data += ntohs(head->len);
- break;
- }
- case DNS_RECORD_TYPE_TXT:
- {
- uint16_t datalen = ntohs(head->len);
- uint8_t txtlen = *data;
- const uint8_t *tdata = data + 1;
-
- do {
- //PrintRawDataFp(stdout, (uint8_t*)tdata, txtlen);
-
- if (txtlen >= datalen)
- goto bad_data;
-
- DNSStoreAnswerInState(dns_state, list, fqdn, fqdn_len,
- ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
- (uint8_t*)tdata, (uint16_t)txtlen, ntohs(dns_header->tx_id));
-
- datalen -= txtlen;
- tdata += txtlen;
- txtlen = *tdata;
-
- tdata++;
- datalen--;
-
- SCLogDebug("datalen %u, txtlen %u", datalen, txtlen);
- } while (datalen > 1);
-
- data += ntohs(head->len);
- break;
- }
- default: /* unsupported record */
- {
- DNSStoreAnswerInState(dns_state, list, NULL, 0,
- ntohs(head->type), ntohs(head->class), ntohl(head->ttl),
- NULL, 0, ntohs(dns_header->tx_id));
-
- //PrintRawDataFp(stdout, data, ntohs(head->len));
- data += ntohs(head->len);
- break;
- }
- }
- return data;
-bad_data:
-insufficient_data:
- return NULL;
-}
-
-void DNSCreateTypeString(uint16_t type, char *str, size_t str_size)
-{
- switch (type) {
- case DNS_RECORD_TYPE_A:
- snprintf(str, str_size, "A");
- break;
- case DNS_RECORD_TYPE_NS:
- snprintf(str, str_size, "NS");
- break;
- case DNS_RECORD_TYPE_AAAA:
- snprintf(str, str_size, "AAAA");
- break;
- case DNS_RECORD_TYPE_TXT:
- snprintf(str, str_size, "TXT");
- break;
- case DNS_RECORD_TYPE_CNAME:
- snprintf(str, str_size, "CNAME");
- break;
- case DNS_RECORD_TYPE_SOA:
- snprintf(str, str_size, "SOA");
- break;
- case DNS_RECORD_TYPE_MX:
- snprintf(str, str_size, "MX");
- break;
- case DNS_RECORD_TYPE_PTR:
- snprintf(str, str_size, "PTR");
- break;
- case DNS_RECORD_TYPE_ANY:
- snprintf(str, str_size, "ANY");
- break;
- case DNS_RECORD_TYPE_TKEY:
- snprintf(str, str_size, "TKEY");
- break;
- case DNS_RECORD_TYPE_TSIG:
- snprintf(str, str_size, "TSIG");
- break;
- case DNS_RECORD_TYPE_SRV:
- snprintf(str, str_size, "SRV");
- break;
- case DNS_RECORD_TYPE_NAPTR:
- snprintf(str, str_size, "NAPTR");
- break;
- case DNS_RECORD_TYPE_DS:
- snprintf(str, str_size, "DS");
- break;
- case DNS_RECORD_TYPE_RRSIG:
- snprintf(str, str_size, "RRSIG");
- break;
- case DNS_RECORD_TYPE_NSEC:
- snprintf(str, str_size, "NSEC");
- break;
- case DNS_RECORD_TYPE_NSEC3:
- snprintf(str, str_size, "NSEC3");
- break;
- default:
- snprintf(str, str_size, "%04x/%u", type, type);
- }
-}
-
-void DNSCreateRcodeString(uint8_t rcode, char *str, size_t str_size)
-{
- switch (rcode) {
- case DNS_RCODE_NOERROR:
- snprintf(str, str_size, "NOERROR");
- break;
- case DNS_RCODE_FORMERR:
- snprintf(str, str_size, "FORMERR");
- break;
- case DNS_RCODE_SERVFAIL:
- snprintf(str, str_size, "SERVFAIL");
- break;
- case DNS_RCODE_NXDOMAIN:
- snprintf(str, str_size, "NXDOMAIN");
- break;
- case DNS_RCODE_NOTIMP:
- snprintf(str, str_size, "NOTIMP");
- break;
- case DNS_RCODE_REFUSED:
- snprintf(str, str_size, "REFUSED");
- break;
- case DNS_RCODE_YXDOMAIN:
- snprintf(str, str_size, "YXDOMAIN");
- break;
- case DNS_RCODE_YXRRSET:
- snprintf(str, str_size, "YXRRSET");
- break;
- case DNS_RCODE_NXRRSET:
- snprintf(str, str_size, "NXRRSET");
- break;
- case DNS_RCODE_NOTAUTH:
- snprintf(str, str_size, "NOTAUTH");
- break;
- case DNS_RCODE_NOTZONE:
- snprintf(str, str_size, "NOTZONE");
- break;
- /* these are the same, need more logic */
- case DNS_RCODE_BADVERS:
- //case DNS_RCODE_BADSIG:
- snprintf(str, str_size, "BADVERS/BADSIG");
- break;
- case DNS_RCODE_BADKEY:
- snprintf(str, str_size, "BADKEY");
- break;
- case DNS_RCODE_BADTIME:
- snprintf(str, str_size, "BADTIME");
- break;
- case DNS_RCODE_BADMODE:
- snprintf(str, str_size, "BADMODE");
- break;
- case DNS_RCODE_BADNAME:
- snprintf(str, str_size, "BADNAME");
- break;
- case DNS_RCODE_BADALG:
- snprintf(str, str_size, "BADALG");
- break;
- case DNS_RCODE_BADTRUNC:
- snprintf(str, str_size, "BADTRUNC");
- break;
- default:
- SCLogDebug("could not map DNS rcode to name, bug!");
- snprintf(str, str_size, "%04x/%u", rcode, rcode);
- }
-}