Skip to content

Commit

Permalink
EDNS
Browse files Browse the repository at this point in the history
- Refactor pull request #288
  - Implement additional indexers for EDNS(0) cookie, ECS, EDE and NSID
  - Add indicators for indexers to use to control is EDNS, and options, are parsed
- `hashtbl`: add `strtohex()`, convert fixed length string to hexadecimals
  • Loading branch information
jelu committed Jul 5, 2023
1 parent 7cabfd9 commit b5164fe
Show file tree
Hide file tree
Showing 32 changed files with 1,452 additions and 556 deletions.
6 changes: 2 additions & 4 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ dsc_SOURCES = asn_index.c certain_qnames_index.c client_index.c \
ip_version_index.c md_array.c md_array_json_printer.c \
md_array_xml_printer.c msglen_index.c null_index.c opcode_index.c \
parse_conf.c pcap.c qclass_index.c qname_index.c qnamelen_index.c label_count_index.c \
edns0_cookielen_index.c edns0_nsidlen_index.c edns0_edetextlen_index.c \
edns0_edecode_index.c edns0_ecsfamily_index.c \
edns_cookie_index.c edns_nsid_index.c edns_ede_index.c edns_ecs_index.c \
qr_aa_bits_index.c qtype_index.c query_classification_index.c rcode_index.c \
rd_bit_index.c server_ip_addr_index.c tc_bit_index.c tld_index.c \
transport_index.c xmalloc.c response_time_index.c tld_list.c \
Expand All @@ -40,8 +39,7 @@ dist_dsc_SOURCES = asn_index.h base64.h certain_qnames_index.h client_index.h \
idn_qname_index.h inX_addr.h ip_direction_index.h ip_proto_index.h \
ip_version_index.h md_array.h msglen_index.h null_index.h opcode_index.h \
parse_conf.h pcap.h qclass_index.h qname_index.h qnamelen_index.h label_count_index.h \
edns0_cookielen_index.h edns0_nsidlen_index.h edns0_edetextlen_index.h \
edns0_edecode_index.h edns0_ecsfamily_index.h \
edns_cookie_index.h edns_nsid_index.h edns_ede_index.h edns_ecs_index.h \
qr_aa_bits_index.h qtype_index.h query_classification_index.h rcode_index.h \
rd_bit_index.h server_ip_addr_index.h syslog_debug.h tc_bit_index.h \
tld_index.h transport_index.h xmalloc.h response_time_index.h tld_list.h \
Expand Down
52 changes: 36 additions & 16 deletions src/dns_message.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "xmalloc.h"
#include "syslog_debug.h"
#include "tld_list.h"
#include "dns_protocol.h"

#include "null_index.h"
#include "qtype_index.h"
Expand All @@ -53,11 +54,10 @@
#include "server_ip_addr_index.h"
#include "qnamelen_index.h"
#include "label_count_index.h"
#include "edns0_cookielen_index.h"
#include "edns0_nsidlen_index.h"
#include "edns0_edetextlen_index.h"
#include "edns0_edecode_index.h"
#include "edns0_ecsfamily_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"
Expand Down Expand Up @@ -99,11 +99,6 @@ static indexer indexers[] = {
{ "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 },
{ "edns0_cookielen", 0, edns0_cookielen_indexer, edns0_cookielen_iterator, edns0_cookielen_reset },
{ "edns0_nsidlen", 0, edns0_nsidlen_indexer, edns0_nsidlen_iterator, edns0_nsidlen_reset },
{ "edns0_edetextlen", 0, edns0_edetextlen_indexer, edns0_edetextlen_iterator, edns0_edetextlen_reset },
{ "edns0_edecode", 0, edns0_edecode_indexer, edns0_edecode_iterator, edns0_edecode_reset },
{ "edns0_ecsfamily", 0, edns0_ecsfamily_indexer, edns0_ecsfamily_iterator, edns0_ecsfamily_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 },
Expand All @@ -114,8 +109,24 @@ static indexer indexers[] = {
{ "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", edns_version_init, edns_version_indexer, edns_version_iterator },
{ "edns_bufsiz", edns_bufsiz_init, edns_bufsiz_indexer, edns_bufsiz_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_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 },
{ "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 },
Expand Down Expand Up @@ -225,9 +236,7 @@ static int servfail_filter(const dns_message* m, const void* ctx)

static int edns0_filter(const dns_message* m, const void* ctx)
{
if ((m->edns.found == 1) && (m->edns.version == 0))
return 1;
return 0;
return m->edns.found && m->edns.version == 0;
}

/*
Expand Down Expand Up @@ -423,7 +432,7 @@ const char* dns_message_QnameToNld(const char* qname, int nld)
if (have_tld_list) {
// Use TLD list to find labels that are the "TLD"
const char *lt = 0, *ot = t;
int done = 0;
int done = 0;
while (t > qname) {
t--;
if ('.' == *t) {
Expand Down Expand Up @@ -529,3 +538,14 @@ int add_qname_filter(const char* name, const char* pat)
(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;
}
45 changes: 40 additions & 5 deletions src/dns_message.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,43 @@ struct dns_message {
unsigned int DO : 1; /* set if DNSSEC DO bit is set */
unsigned char version; /* version field from OPT RR */
unsigned short bufsiz; /* class field from OPT RR */
unsigned short cookie_len; /* cookie length from OPT RR, 0 means none, and (n>1) means (n-1) */
unsigned short nsid_len; /* nsid length from OPT RR, 0 means none, and (n>1) means (n-1) */
unsigned short ede_code; /* ede_code from OPT RR, 0 means none, and (n>1) means (n-1) */
unsigned short ede_text_len; /* ede_text_length from OPT RR, 0 means none, and (n>1) means (n-1) */
unsigned short ecs_family; /* ECS family from OPT RR, 0 means none, and (n>1) means (n-1) */

// bitmap of found EDNS(0) options
struct {
unsigned int cookie : 1;
unsigned int nsid : 1;
unsigned int ede : 1;
unsigned int ecs : 1;
} option;

// cookie rfc 7873
struct {
const u_char* client; // pointer to 8 byte client part
const u_char* server; // pointer to server part, may be null
unsigned short server_len; // length of server part, if any
} cookie;

// nsid rfc 5001
struct {
const u_char* data; // pointer to nsid payload, may be null
unsigned short len; // length of nsid, if any
} nsid;

// extended error codes rfc 8914
struct {
unsigned short code;
const u_char* text; // pointer to EXTRA-TEXT, may be null
unsigned short len; // length of text, if any
} ede;

// client subnet rfc 7871
struct {
unsigned short family;
unsigned char source_prefix;
unsigned char scope_prefix;
const u_char* address; // pointer to address, may be null
unsigned short len; // length of address, if any
} ecs;
} edns;
};

Expand All @@ -118,6 +150,9 @@ void dns_message_filters_init(void);
void dns_message_indexers_init(void);
int add_qname_filter(const char* name, const char* pat);

void indexer_want_edns(void);
void indexer_want_edns_options(void);

#include <arpa/nameser.h>
#ifdef HAVE_ARPA_NAMESER_COMPAT_H
#include <arpa/nameser_compat.h>
Expand Down
92 changes: 85 additions & 7 deletions src/dns_protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,75 @@ static off_t skip_question(const u_char* buf, int len, off_t offset)
return offset;
}

#define EDNS0_TYPE_NSID 3
#define EDNS0_TYPE_ECS 8
#define EDNS0_TYPE_COOKIE 10
#define EDNS0_TYPE_EXTENDED_ERROR 15

static void process_edns0_options(const u_char* buf, int len, struct dns_message* m)
{
unsigned short edns0_type;
unsigned short edns0_len;
off_t offset = 0;

while (len >= 4) {
edns0_type = nptohs(buf + offset);
edns0_len = nptohs(buf + offset + 2);
if (len < 4 + edns0_len)
break;
switch (edns0_type) {
case EDNS0_TYPE_COOKIE:
if (m->edns.option.cookie)
break;
if (edns0_len == 8) {
m->edns.option.cookie = 1;
m->edns.cookie.client = buf + offset + 4;
} else if (edns0_len >= 16 && edns0_len <= 40) {
m->edns.option.cookie = 1;
m->edns.cookie.client = buf + offset + 4;
m->edns.cookie.server = m->edns.cookie.client + 8;
m->edns.cookie.server_len = edns0_len - 8;
}
break;
case EDNS0_TYPE_NSID:
if (m->edns.option.nsid)
break;
m->edns.option.nsid = 1;
if (edns0_len) {
m->edns.nsid.data = buf + offset + 4;
m->edns.nsid.len = edns0_len;
}
break;
case EDNS0_TYPE_ECS:
if (m->edns.option.ecs || edns0_len < 4)
break;
m->edns.option.ecs = 1;
m->edns.ecs.family = nptohs(buf + offset + 4);
m->edns.ecs.source_prefix = *(buf + offset + 6);
m->edns.ecs.scope_prefix = *(buf + offset + 7);
if (edns0_len > 4) {
m->edns.ecs.address = buf + offset + 8;
m->edns.ecs.len = edns0_len - 4;
}
break;
case EDNS0_TYPE_EXTENDED_ERROR:
if (m->edns.option.ede || edns0_len < 2)
break;
m->edns.option.ede = 1;
m->edns.ede.code = nptohs(buf + offset + 4);
if (edns0_len > 2) {
m->edns.ede.text = buf + offset + 6;
m->edns.ede.len = edns0_len - 2;
}
break;
}
offset += 4 + edns0_len;
len -= 4 + edns0_len;
}
}

int dns_protocol_parse_edns_options = 0;

static off_t grok_additional_for_opt_rr(const u_char* buf, int len, off_t offset, dns_message* m)
{
unsigned short us;
Expand All @@ -225,11 +294,20 @@ static off_t grok_additional_for_opt_rr(const u_char* buf, int len, off_t offset
if (offset + 10 > len)
return 0;
if (nptohs(buf + offset) == T_OPT && !m->edns.found) {
m->edns.found = 1;
m->edns.bufsiz = nptohs(buf + offset + 2);
memcpy(&m->edns.version, buf + offset + 5, 1);
us = nptohs(buf + offset + 6);
m->edns.DO = (us >> 15) & 0x01; /* RFC 3225 */
m->edns.found = 1;
m->edns.bufsiz = nptohs(buf + offset + 2);
m->edns.version = *(buf + offset + 5);
us = nptohs(buf + offset + 6);
m->edns.DO = (us >> 15) & 0x01; /* RFC 3225 */

us = nptohs(buf + offset + 8); // rd len
offset += 10;
if (offset + us > len)
return 0;
if (dns_protocol_parse_edns_options && !m->edns.version && us > 0)
process_edns0_options(buf + offset, us, m);
offset += us;
return offset;
}
}
/* get rdlength */
Expand All @@ -255,7 +333,7 @@ static off_t skip_rr(const u_char* buf, int len, off_t offset)
return offset;
}

int dns_protocol_parse_edns0 = 0;
int dns_protocol_parse_edns = 0;

int dns_protocol_handler(const u_char* buf, int len, void* udata)
{
Expand Down Expand Up @@ -306,7 +384,7 @@ int dns_protocol_handler(const u_char* buf, int len, void* udata)
offset = new_offset;
qdcount--;
}
if (!dns_protocol_parse_edns0)
if (!dns_protocol_parse_edns)
goto handle_m;
assert(offset <= len);

Expand Down
3 changes: 2 additions & 1 deletion src/dns_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@

#include <sys/types.h>

extern int dns_protocol_parse_edns0;
extern int dns_protocol_parse_edns;
extern int dns_protocol_parse_edns_options;

int dns_protocol_handler(const u_char* buf, int len, void* udata);

Expand Down
72 changes: 51 additions & 21 deletions src/dsc.conf.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,54 @@ EDNS Version 0 is documented in RFC 2671.
\fBedns_bufsiz\fR
The EDNS buffer size per 512 chunks (0-511, 512-1023 etc).
.TP
\fBedns_cookie\fR
Indicates whether or not a EDNS(0) Cookie (RFC7873) was present with "yes" or "no".
.TP
\fBedns_cookie_len\fR
The combined length of the EDNS(0) client and server cookies.
.TP
\fBedns_cookie_client\fR
The EDNS(0) Client Cookie bytes as a hexadecimal string.
.TP
\fBedns_cookie_server\fR
The EDNS(0) Server Cookie bytes as a hexadecimal string.
.TP
\fBedns_ecs\fR
Indicates whether or not a EDNS(0) Client Subnet (RFC7871) was present with "yes" or "no".
.TP
\fBedns_ecs_family\fR
The EDNS(0) Client Subnet address family.
.TP
\fBedns_ecs_source_prefix\fR
The EDNS(0) Client Subnet source prefix-length.
.TP
\fBedns_ecs_scope_prefix\fR
The EDNS(0) Client Subnet scope prefix-length.
.TP
\fBedns_ecs_address\fR
The EDNS(0) Client Subnet address bytes as a hexadecimal string.
.TP
\fBedns_ede\fR
Indicates whether or not a EDNS(0) Extended DNS Errors (RFC8914) was present with "yes" or "no".
.TP
\fBedns_ede_code\fR
The EDNS(0) Extended DNS Errors code.
.TP
\fBedns_ede_textlen\fR
The length of the EDNS(0) Extended DNS Errors extra-text.
.TP
\fBedns_ede_text\fR
The EDNS(0) Extended DNS Errors extra-text.
.TP
\fBedns_nsid\fR
Indicates whether or not a EDNS(0) DNS Name Server Identifier (RFC5001) was present with "yes" or "no".
.TP
\fBedns_nsid_len\fR
The length of the EDNS(0) DNS Name Server Identifier (NSID).
.TP
\fBedns_nsid_data\fR
The EDNS(0) DNS Name Server Identifier (NSID) bytes as a hexadecimal string.
.TP
\fBidn_qname\fR
This indexer has only two values: 0 or 1.
It returns 1 when the first QNAME in the DNS message question section
Expand Down Expand Up @@ -553,27 +601,6 @@ The number of labels (between "." dots) in the first (and usually only)
QNAME in a DNS message question section.
Note that a value of 0 (zero) means DNS root (.).
.TP
\fBedns0_cookielen\fR
The length of the EDNS0 cookie option, when present. Will report
"none" for DNS messages without EDNS0 or cookies.
.TP
\fBedns0_nsidlen\fR
The length of the EDNS0 nsid option, when present. Will report
"none" for DNS messages without EDNS0 or nsid.
.TP
\fBedns0_edetextlen\fR
The length of the EDNS0 extended error text, when present. Will report
"none" for DNS messages without EDNS0, extended error option or
extended error text
.TP
\fBedns0_edecode\fR
The EDNS0 extended error code, when present. Will report
"none" for DNS messages without EDNS0 or extended error option
.TP
\fBedns0_ecsfamily\fR
The EDNS0 Client Subnet address family, when present. Will report
"none" for DNS messages without EDNS0 or Client Subnet option
.TP
\fBqtype\fR
The query type (QTYPE) for the first QNAME in the DNS message question
section.
Expand Down Expand Up @@ -727,6 +754,9 @@ Count only SERVFAIL responses.
.TP
\fBauthentic-data-only\fR
Count only DNS messages with the AD bit is set.
.TP
\fBedns0-only\fR
Count only DNS messages with EDNS(0) options.
.SH "QNAME FILTERS"
Defines a custom QNAME-based filter for DNS messages.
If you refer to this named filter on a dataset line, then only queries
Expand Down
Loading

0 comments on commit b5164fe

Please sign in to comment.