diff options
author | Daniel Baumann <daniel@debian.org> | 2024-12-12 22:18:22 +0100 |
---|---|---|
committer | Daniel Baumann <daniel@debian.org> | 2024-12-12 22:37:18 +0100 |
commit | d7dba196f14971b34d3a5558ef64f9765aa6a9c4 (patch) | |
tree | 5cd92190c79ef2be590218b93c5206f8ca9b749e /src/dns_message.c | |
parent | Initial commit. (diff) | |
download | dsc-d7dba196f14971b34d3a5558ef64f9765aa6a9c4.tar.xz dsc-d7dba196f14971b34d3a5558ef64f9765aa6a9c4.zip |
Adding upstream version 2.15.2.
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'src/dns_message.c')
-rw-r--r-- | src/dns_message.c | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/src/dns_message.c b/src/dns_message.c new file mode 100644 index 0000000..3041148 --- /dev/null +++ b/src/dns_message.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2008-2024 OARC, Inc. + * Copyright (c) 2007-2008, Internet Systems Consortium, Inc. + * Copyright (c) 2003-2007, The Measurement Factory, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "dns_message.h" +#include "xmalloc.h" +#include "syslog_debug.h" +#include "tld_list.h" +#include "dns_protocol.h" + +#include "null_index.h" +#include "qtype_index.h" +#include "qclass_index.h" +#include "country_index.h" +#include "asn_index.h" +#include "tld_index.h" +#include "rcode_index.h" +#include "client_index.h" +#include "client_subnet_index.h" +#include "server_ip_addr_index.h" +#include "qnamelen_index.h" +#include "label_count_index.h" +#include "edns_cookie_index.h" +#include "edns_nsid_index.h" +#include "edns_ede_index.h" +#include "edns_ecs_index.h" +#include "qname_index.h" +#include "msglen_index.h" +#include "certain_qnames_index.h" +#include "idn_qname_index.h" +#include "query_classification_index.h" +#include "edns_version_index.h" +#include "edns_bufsiz_index.h" +#include "do_bit_index.h" +#include "rd_bit_index.h" +#include "tc_bit_index.h" +#include "qr_aa_bits_index.h" +#include "opcode_index.h" +#include "transport_index.h" +#include "dns_ip_version_index.h" +#include "dns_source_port_index.h" +#include "response_time_index.h" +#include "encryption_index.h" + +#include "ip_direction_index.h" +#include "ip_proto_index.h" +#include "ip_version_index.h" + +#include <assert.h> +#include <ctype.h> +#include <string.h> +#include <regex.h> + +extern int debug_flag; +static md_array_list* Arrays = 0; +static filter_list* DNSFilters = 0; + +static indexer indexers[] = { + { "client", 0, client_indexer, client_iterator, client_reset }, + { "server", 0, sip_indexer, sip_iterator, sip_reset }, + { "country", country_init, country_indexer, country_iterator, country_reset }, + { "asn", asn_init, asn_indexer, asn_iterator, asn_reset }, + { "client_subnet", client_subnet_init, client_subnet_indexer, client_subnet_iterator, client_subnet_reset }, + { "null", 0, null_indexer, null_iterator }, + { "qclass", 0, qclass_indexer, qclass_iterator, qclass_reset }, + { "qnamelen", 0, qnamelen_indexer, qnamelen_iterator, qnamelen_reset }, + { "label_count", 0, label_count_indexer, label_count_iterator, label_count_reset }, + { "qname", 0, qname_indexer, qname_iterator, qname_reset }, + { "second_ld", 0, second_ld_indexer, second_ld_iterator, second_ld_reset }, + { "third_ld", 0, third_ld_indexer, third_ld_iterator, third_ld_reset }, + { "msglen", 0, msglen_indexer, msglen_iterator, msglen_reset }, + { "qtype", 0, qtype_indexer, qtype_iterator, qtype_reset }, + { "rcode", 0, rcode_indexer, rcode_iterator, rcode_reset }, + { "tld", 0, tld_indexer, tld_iterator, tld_reset }, + { "certain_qnames", 0, certain_qnames_indexer, certain_qnames_iterator }, + { "query_classification", 0, query_classification_indexer, query_classification_iterator }, + { "idn_qname", 0, idn_qname_indexer, idn_qname_iterator }, + { "edns_version", indexer_want_edns, edns_version_indexer, edns_version_iterator }, + { "edns_bufsiz", indexer_want_edns, edns_bufsiz_indexer, edns_bufsiz_iterator }, + { "edns_cookie", indexer_want_edns_options, edns_cookie_indexer, edns_cookie_iterator }, + { "edns_cookie_len", indexer_want_edns_options, edns_cookie_len_indexer, edns_cookie_len_iterator, edns_cookie_len_reset }, + { "edns_cookie_client", indexer_want_edns_options, edns_cookie_client_indexer, edns_cookie_client_iterator, edns_cookie_client_reset }, + { "edns_cookie_server", indexer_want_edns_options, edns_cookie_server_indexer, edns_cookie_server_iterator, edns_cookie_server_reset }, + { "edns_ecs", indexer_want_edns_options, edns_ecs_indexer, edns_ecs_iterator }, + { "edns_ecs_family", indexer_want_edns_options, edns_ecs_family_indexer, edns_ecs_family_iterator, edns_ecs_family_reset }, + { "edns_ecs_source_prefix", indexer_want_edns_options, edns_ecs_source_prefix_indexer, edns_ecs_source_prefix_iterator, edns_ecs_source_prefix_reset }, + { "edns_ecs_scope_prefix", indexer_want_edns_options, edns_ecs_scope_prefix_indexer, edns_ecs_scope_prefix_iterator, edns_ecs_scope_prefix_reset }, + { "edns_ecs_address", indexer_want_edns_options, edns_ecs_address_indexer, edns_ecs_address_iterator, edns_ecs_address_reset }, + { "edns_ecs_subnet", indexer_want_edns_options, edns_ecs_subnet_indexer, edns_ecs_subnet_iterator, edns_ecs_subnet_reset }, + { "edns_ede", indexer_want_edns_options, edns_ede_indexer, edns_ede_iterator }, + { "edns_ede_code", indexer_want_edns_options, edns_ede_code_indexer, edns_ede_code_iterator, edns_ede_code_reset }, + { "edns_ede_textlen", indexer_want_edns_options, edns_ede_textlen_indexer, edns_ede_textlen_iterator, edns_ede_textlen_reset }, + { "edns_ede_text", indexer_want_edns_options, edns_ede_text_indexer, edns_ede_text_iterator, edns_ede_text_reset }, + { "edns_nsid", indexer_want_edns_options, edns_nsid_indexer, edns_nsid_iterator }, + { "edns_nsid_len", indexer_want_edns_options, edns_nsid_len_indexer, edns_nsid_len_iterator, edns_nsid_len_reset }, + { "edns_nsid_data", indexer_want_edns_options, edns_nsid_data_indexer, edns_nsid_data_iterator, edns_nsid_data_reset }, + { "edns_nsid_text", indexer_want_edns_options, edns_nsid_text_indexer, edns_nsid_text_iterator, edns_nsid_text_reset }, + { "do_bit", 0, do_bit_indexer, do_bit_iterator }, + { "rd_bit", 0, rd_bit_indexer, rd_bit_iterator }, + { "tc_bit", 0, tc_bit_indexer, tc_bit_iterator }, + { "opcode", 0, opcode_indexer, opcode_iterator, opcode_reset }, + { "transport", 0, transport_indexer, transport_iterator }, + { "dns_ip_version", 0, dns_ip_version_indexer, dns_ip_version_iterator, dns_ip_version_reset }, + { "dns_source_port", 0, dns_source_port_indexer, dns_source_port_iterator, dns_source_port_reset }, + { "dns_sport_range", 0, dns_sport_range_indexer, dns_sport_range_iterator, dns_sport_range_reset }, + { "qr_aa_bits", 0, qr_aa_bits_indexer, qr_aa_bits_iterator }, + { "response_time", 0, response_time_indexer, response_time_iterator, response_time_reset, response_time_flush }, + { "ip_direction", 0, ip_direction_indexer, ip_direction_iterator }, + { "ip_proto", 0, ip_proto_indexer, ip_proto_iterator, ip_proto_reset }, + { "ip_version", 0, ip_version_indexer, ip_version_iterator, ip_version_reset }, + { "encryption", 0, encryption_indexer, encryption_iterator }, + { 0 } +}; + +/* + * Filters + */ + +static int queries_only_filter(const dns_message* m, const void* ctx) +{ + return m->qr ? 0 : 1; +} + +static int nxdomains_only_filter(const dns_message* m, const void* ctx) +{ + return m->rcode == 3; +} + +static int ad_filter(const dns_message* m, const void* ctx) +{ + return m->ad; +} + +static int popular_qtypes_filter(const dns_message* m, const void* ctx) +{ + switch (m->qtype) { + case 1: + case 2: + case 5: + case 6: + case 12: + case 15: + case 28: + case 33: + case 38: + case 255: + return 1; + default: + break; + } + return 0; +} + +static int aaaa_or_a6_filter(const dns_message* m, const void* ctx) +{ + switch (m->qtype) { + case T_AAAA: + case T_A6: + return 1; + default: + break; + } + return 0; +} + +static int idn_qname_filter(const dns_message* m, const void* ctx) +{ + return !strncmp(m->qname, "xn--", 4); +} + +static int root_servers_net_filter(const dns_message* m, const void* ctx) +{ + return !strcmp(m->qname + 1, ".root-servers.net"); +} + +static int chaos_class_filter(const dns_message* m, const void* ctx) +{ + return m->qclass == C_CHAOS; +} + +static int priming_query_filter(const dns_message* m, const void* ctx) +{ + if (m->qtype != T_NS) + return 0; + if (!strcmp(m->qname, ".")) + return 0; + return 1; +} + +static int replies_only_filter(const dns_message* m, const void* ctx) +{ + return m->qr ? 1 : 0; +} + +static int qname_filter(const dns_message* m, const void* ctx) +{ + return !regexec((const regex_t*)ctx, m->qname, 0, 0, 0); +} + +static int servfail_filter(const dns_message* m, const void* ctx) +{ + return m->rcode == 2; +} + +static int edns0_filter(const dns_message* m, const void* ctx) +{ + return m->edns.found && m->edns.version == 0; +} + +static int edns0_cookie_filter(const dns_message* m, const void* ctx) +{ + return m->edns.option.cookie; +} + +static int edns0_nsid_filter(const dns_message* m, const void* ctx) +{ + return m->edns.option.nsid; +} + +static int edns0_ede_filter(const dns_message* m, const void* ctx) +{ + return m->edns.option.ede; +} + +static int edns0_ecs_filter(const dns_message* m, const void* ctx) +{ + return m->edns.option.ecs; +} + +/* + * Helpers + */ + +static const char* printable_dnsname(const char* name) +{ + static char buf[MAX_QNAME_SZ]; + int i; + + for (i = 0; i < sizeof(buf) - 1;) { + if (!*name) + break; + if (isgraph(*name)) { + buf[i] = *name; + i++; + } else { + if (i + 3 > MAX_QNAME_SZ - 1) + break; /* expanded character would overflow buffer */ + snprintf(buf + i, sizeof(buf) - i - 1, "%%%02x", (unsigned char)*name); + i += 3; + } + name++; + } + buf[i] = '\0'; + return buf; +} + +static void dns_message_print(dns_message* m) +{ + char buf[128]; + inXaddr_ntop(m->qr ? &m->tm->dst_ip_addr : &m->tm->src_ip_addr, buf, 128); + fprintf(stderr, "%15s:%5d", buf, m->qr ? m->tm->dst_port : m->tm->src_port); + fprintf(stderr, "\t%s", (m->tm->proto == IPPROTO_UDP) ? "UDP" : (m->tm->proto == IPPROTO_TCP) ? "TCP" : "???"); + fprintf(stderr, "\tQT=%d", m->qtype); + fprintf(stderr, "\tQC=%d", m->qclass); + fprintf(stderr, "\tlen=%d", m->msglen); + fprintf(stderr, "\tqname=%s", printable_dnsname(m->qname)); + fprintf(stderr, "\ttld=%s", printable_dnsname(dns_message_tld(m))); + fprintf(stderr, "\topcode=%d", m->opcode); + fprintf(stderr, "\trcode=%d", m->rcode); + fprintf(stderr, "\tmalformed=%d", m->malformed); + fprintf(stderr, "\tqr=%d", m->qr); + fprintf(stderr, "\trd=%d", m->rd); + fprintf(stderr, "\n"); +} + +static indexer* dns_message_find_indexer(const char* in) +{ + indexer* indexer; + for (indexer = indexers; indexer->name; indexer++) { + if (0 == strcmp(in, indexer->name)) + return indexer; + } + dsyslogf(LOG_ERR, "unknown indexer '%s'", in); + return NULL; +} + +static int dns_message_find_filters(const char* fn, filter_list** fl) +{ + char* tok = 0; + char* t; + char* copy = xstrdup(fn); + filter_list* f; + if (NULL == copy) + return 0; + for (t = strtok_r(copy, ",", &tok); t; t = strtok_r(NULL, ",", &tok)) { + if (0 == strcmp(t, "any")) + continue; + for (f = DNSFilters; f; f = f->next) { + if (0 == strcmp(t, f->filter->name)) + break; + } + if (f) { + fl = md_array_filter_list_append(fl, f->filter); + continue; + } + dsyslogf(LOG_ERR, "unknown filter '%s'", t); + xfree(copy); + return 0; + } + xfree(copy); + return 1; +} + +/* + * Public + */ + +void dns_message_handle(dns_message* m) +{ + md_array_list* a; + if (debug_flag > 1) + dns_message_print(m); + for (a = Arrays; a; a = a->next) + md_array_count(a->theArray, m); +} + +int dns_message_add_array(const char* name, const char* fn, const char* fi, const char* sn, const char* si, const char* f, dataset_opt opts) +{ + filter_list* filters = NULL; + indexer * indexer1, *indexer2; + md_array_list* a; + + if (NULL == (indexer1 = dns_message_find_indexer(fi))) + return 0; + if (NULL == (indexer2 = dns_message_find_indexer(si))) + return 0; + if (0 == dns_message_find_filters(f, &filters)) + return 0; + + a = xcalloc(1, sizeof(*a)); + if (a == NULL) { + dsyslogf(LOG_ERR, "Cant allocate memory for '%s' DNS message array", name); + return 0; + } + a->theArray = md_array_create(name, filters, fn, indexer1, sn, indexer2); + if (NULL == a->theArray) { + dsyslogf(LOG_ERR, "Cant allocate memory for '%s' DNS message array", name); + xfree(a); + return 0; + } + a->theArray->opts = opts; + assert(a->theArray); + a->next = Arrays; + Arrays = a; + return 1; +} + +void dns_message_flush_arrays(void) +{ + md_array_list* a; + for (a = Arrays; a; a = a->next) { + if (a->theArray->d1.indexer->flush_fn || a->theArray->d2.indexer->flush_fn) + md_array_flush(a->theArray); + } +} + +void dns_message_report(FILE* fp, md_array_printer* printer) +{ + md_array_list* a; + for (a = Arrays; a; a = a->next) { + md_array_print(a->theArray, printer, fp); + } +} + +void dns_message_clear_arrays(void) +{ + md_array_list* a; + for (a = Arrays; a; a = a->next) + md_array_clear(a->theArray); +} + +/* + * QnameToNld + * + * qname is a 0-terminated string containing a DNS name + * nld is the domain level to find + * + * return value is a pointer into the qname string. + * + * Handles the following cases: + * qname is empty ("") + * qname ends with one or more dots + * qname begins with one or more dots + * multiple consequtive dots in qname + * + * TESTS + * assert(0 == strcmp(QnameToNld("a.b.c.d", 1), "d")); + * assert(0 == strcmp(QnameToNld("a.b.c.d", 2), "c.d")); + * assert(0 == strcmp(QnameToNld("a.b.c.d.", 2), "c.d.")); + * assert(0 == strcmp(QnameToNld("a.b.c.d....", 2), "c.d....")); + * assert(0 == strcmp(QnameToNld("c.d", 5), "c.d")); + * assert(0 == strcmp(QnameToNld(".c.d", 5), "c.d")); + * assert(0 == strcmp(QnameToNld(".......c.d", 5), "c.d")); + * assert(0 == strcmp(QnameToNld("", 1), "")); + * assert(0 == strcmp(QnameToNld(".", 1), ".")); + * assert(0 == strcmp(QnameToNld("a.b..c..d", 2), "c..d")); + * assert(0 == strcmp(QnameToNld("a.b................c..d", 3), "b................c..d")); + */ +const char* dns_message_QnameToNld(const char* qname, int nld) +{ + const char* e = qname + strlen(qname) - 1; + const char* t; + int dotcount = 0; + int state = 0; /* 0 = not in dots, 1 = in dots */ + while (*e == '.' && e > qname) + e--; + t = e; + if (0 == strcmp(t, ".arpa")) + dotcount--; + if (have_tld_list) { + // Use TLD list to find labels that are the "TLD" + const char *lt = 0, *ot = t; + int done = 0; + while (t > qname) { + t--; + if ('.' == *t) { + if (0 == state) { + int r = tld_list_find(t + 1); + if (r & 1) { + // this is a tld + lt = t; + } + if (!r || !(r & 2)) { + // no more children + if (lt) { + // reset to what we last found + t = lt; + dotcount++; + state = 1; + } else { + // or reset + t = ot; + state = 0; + } + done = 1; + break; + } + } + state = 1; + } else { + state = 0; + } + } + if (!done) { + // nothing found, reset t + t = e; + } + } + while (t > qname && dotcount < nld) { + t--; + if ('.' == *t) { + if (0 == state) + dotcount++; + state = 1; + } else { + state = 0; + } + } + while (*t == '.' && t < e) + t++; + return t; +} + +const char* dns_message_tld(dns_message* m) +{ + if (NULL == m->tld) + m->tld = dns_message_QnameToNld(m->qname, 1); + return m->tld; +} + +void dns_message_filters_init(void) +{ + filter_list** fl = &DNSFilters; + + fl = md_array_filter_list_append(fl, md_array_create_filter("queries-only", queries_only_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("replies-only", replies_only_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("nxdomains-only", nxdomains_only_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("popular-qtypes", popular_qtypes_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("idn-only", idn_qname_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("aaaa-or-a6-only", aaaa_or_a6_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("root-servers-net-only", root_servers_net_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("chaos-class", chaos_class_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("priming-query", priming_query_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("servfail-only", servfail_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("edns0-only", edns0_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("edns0-cookie-only", edns0_cookie_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("edns0-nsid-only", edns0_nsid_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("edns0-ede-only", edns0_ede_filter, 0)); + fl = md_array_filter_list_append(fl, md_array_create_filter("edns0-ecs-only", edns0_ecs_filter, 0)); + (void)md_array_filter_list_append(fl, md_array_create_filter("authentic-data-only", ad_filter, 0)); +} + +void dns_message_indexers_init(void) +{ + indexer* indexer; + + for (indexer = indexers; indexer->name; indexer++) { + if (indexer->init_fn) + indexer->init_fn(); + } +} + +int add_qname_filter(const char* name, const char* pat) +{ + filter_list** fl = &DNSFilters; + regex_t* r; + int x; + while ((*fl)) + fl = &((*fl)->next); + r = xcalloc(1, sizeof(*r)); + if (NULL == r) { + dsyslogf(LOG_ERR, "Cant allocate memory for '%s' qname filter", name); + return 0; + } + if (0 != (x = regcomp(r, pat, REG_EXTENDED | REG_ICASE))) { + char errbuf[512]; + regerror(x, r, errbuf, 512); + dsyslogf(LOG_ERR, "regcomp: %s", errbuf); + } + (void)md_array_filter_list_append(fl, md_array_create_filter(name, qname_filter, r)); + return 1; +} + +void indexer_want_edns(void) +{ + dns_protocol_parse_edns = 1; +} + +void indexer_want_edns_options(void) +{ + dns_protocol_parse_edns = 1; + dns_protocol_parse_edns_options = 1; +} |