diff options
Diffstat (limited to 'framework/src/suricata/src/app-layer-dns-common.c')
-rw-r--r-- | framework/src/suricata/src/app-layer-dns-common.c | 1141 |
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); - } -} |