diff options
Diffstat (limited to 'framework/src/suricata/src/app-layer-dns-tcp.c')
-rw-r--r-- | framework/src/suricata/src/app-layer-dns-tcp.c | 682 |
1 files changed, 0 insertions, 682 deletions
diff --git a/framework/src/suricata/src/app-layer-dns-tcp.c b/framework/src/suricata/src/app-layer-dns-tcp.c deleted file mode 100644 index f1cb597d..00000000 --- a/framework/src/suricata/src/app-layer-dns-tcp.c +++ /dev/null @@ -1,682 +0,0 @@ -/* Copyright (C) 2013 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 "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" - -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; - -/** \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 (ntohs(dns_tcp_header->len) < sizeof(DNSHeader)) { - goto bad_data; - } - if (ntohs(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 < ntohs(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", ntohs(trailer->type), ntohs(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", ntohs(dns_header->tx_id)); - - uint16_t q; - const uint8_t *data = input + sizeof(DNSHeader); - - //PrintRawDataFp(stdout, (uint8_t*)data, input_len - (data - input)); - - for (q = 0; q < ntohs(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", ntohs(trailer->type), ntohs(trailer->class)); - data += sizeof(DNSQueryTrailer); - - /* store our data */ - if (dns_state != NULL) { - DNSStoreQueryInState(dns_state, fqdn, fqdn_offset, - ntohs(trailer->type), ntohs(trailer->class), - ntohs(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) -{ - 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_TCP) - SCReturnInt(-1); - - /* probably a rst/fin sending an eof */ - if (input == NULL || input_len == 0) { - goto insufficient_data; - } - -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 (ntohs(dns_tcp_header->len) < sizeof(DNSHeader)) { - /* bogus len, doesn't fit even basic dns header */ - goto bad_data; - } else if (ntohs(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) > ntohs(dns_tcp_header->len)) { - /* we have all data, so process w/o buffering */ - if (DNSRequestParseData(f, dns_state, input+2, ntohs(dns_tcp_header->len)) < 0) - goto bad_data; - - /* treat the rest of the data as a (potential) new record */ - input += ntohs(dns_tcp_header->len); - input_len -= ntohs(dns_tcp_header->len); - goto next_record; - } else { - /* not enough data, store record length and buffer */ - dns_state->record_len = ntohs(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; - } - - 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, ntohs(dns_header->tx_id))) != NULL) - found = 1; - - if (!found) { - SCLogDebug("DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE"); - DNSSetEvent(dns_state, DNS_DECODER_EVENT_UNSOLLICITED_RESPONSE); - } - - uint16_t q; - const uint8_t *data = input + sizeof(DNSHeader); - for (q = 0; q < ntohs(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", ntohs(trailer->type), ntohs(trailer->class)); -#endif - data += sizeof(DNSQueryTrailer); - } - - for (q = 0; q < ntohs(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 < ntohs(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 = ntohs(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 (ntohs(dns_header->flags) & 0x0080) { - SCLogDebug("recursion desired"); - if (tx != NULL) - tx->recursion_desired = 1; - } - - if (tx != NULL) { - 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) -{ - DNSState *dns_state = (DNSState *)dstate; - - 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; - } - -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 (ntohs(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) > ntohs(dns_tcp_header->len)) { - /* we have all data, so process w/o buffering */ - if (DNSReponseParseData(f, dns_state, input+2, ntohs(dns_tcp_header->len)) < 0) - goto bad_data; - - /* treat the rest of the data as a (potential) new record */ - input += ntohs(dns_tcp_header->len); - input_len -= ntohs(dns_tcp_header->len); - goto next_record; - } else { - /* not enough data, store record length and buffer */ - dns_state->record_len = ntohs(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; - } - SCReturnInt(1); -insufficient_data: - SCReturnInt(-1); -bad_data: - SCReturnInt(-1); -} - -static uint16_t DNSTcpProbingParser(uint8_t *input, uint32_t ilen, uint32_t *offset) -{ - 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 (ntohs(dns_header->len) < sizeof(DNSHeader)) { - /* length field bogus, won't even fit a minimal DNS header. */ - return ALPROTO_FAILED; - } else if (ntohs(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", ntohs(dns_header->len), ilen); - return ALPROTO_UNKNOWN; - } - - int r = DNSTCPRequestParseProbe(input, ilen); - if (r != 1) - return ALPROTO_FAILED; - - SCLogDebug("ALPROTO_DNS"); - return ALPROTO_DNS; -} - -void RegisterDNSTCPParsers(void) -{ - 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); - } else { - int have_cfg = AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP, - proto_name, ALPROTO_DNS, - 0, sizeof(DNSTcpHeader), - DNSTcpProbingParser); - /* 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); - } - } - } 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); - AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_DNS, DNSHasEvents); - AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_DNS, - DNSStateHasTxDetectState, - DNSGetTxDetectState, DNSSetTxDetectState); - - AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_DNS, DNSGetTx); - AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_DNS, DNSGetTxCnt); - AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_DNS, - DNSGetAlstateProgress); - AppLayerParserRegisterGetStateProgressCompletionStatus(IPPROTO_TCP, ALPROTO_DNS, - DNSGetAlstateProgressCompletionStatus); - DNSAppLayerRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_DNS); - } else { - SCLogInfo("Parsed disabled for %s protocol. Protocol detection" - "still on.", proto_name); - } - - return; -} - -/* UNITTESTS */ -#ifdef UNITTESTS -void DNSTCPParserRegisterTests(void) -{ -// UtRegisterTest("DNSTCPParserTest01", DNSTCPParserTest01, 1); -} -#endif |