Merge includes small changes, e.g. fixing the comsumption of remaining
raw data.

* 'master' of https://github.com/FlyingWithJerome/zeek:
  remove excussive fields in dns_svcb_rr
  address code reviews (formatting and type and intrusiveptr)
  newlines at the end of test outputs
  lazy commit
  use tabs in init-bare.zeek
  add svcb test case
  add a dns https test case
  remove test logs
  fix a few syntax errors
  initial commit for SVCB/HTTPS records
This commit is contained in:
Johanna Amann 2021-10-19 14:54:56 +02:00
commit 303e84ad86
14 changed files with 180 additions and 1 deletions

View file

@ -1,3 +1,7 @@
4.2.0-dev.271 | 2021-10-19 14:54:56 +0200
* Add parsing of DNS SVCB/HTTPS records (FlyingWithJerome)
4.2.0-dev.260 | 2021-10-15 09:45:45 +0100 4.2.0-dev.260 | 2021-10-15 09:45:45 +0100
* logging/writers/ascii: shadow files: Add fsync() before rename(). This * logging/writers/ascii: shadow files: Add fsync() before rename(). This

3
NEWS
View file

@ -35,6 +35,9 @@ New Functionality
specifying ``-O ZAM`` on the command line. See specifying ``-O ZAM`` on the command line. See
``src/script_opt/ZAM/README.md`` for more information. ``src/script_opt/ZAM/README.md`` for more information.
- The DNS analyzer has initial support for the SVCB and HTTPS types. The new events
are ``dns_SVCB`` and ``dns_HTTPS``.
Changed Functionality Changed Functionality
--------------------- ---------------------

View file

@ -1 +1 @@
4.2.0-dev.260 4.2.0-dev.271

View file

@ -3884,6 +3884,14 @@ type dns_loc_rr: record {
is_query: count; ##< The RR is a query/Response. is_query: count; ##< The RR is a query/Response.
}; };
## DNS SVCB and HTTPS RRs
##
## .. zeek:see:: dns_SVCB dns_HTTPS
type dns_svcb_rr: record {
svc_priority: count; ##< Service priority for the current record, 0 indicates that this record is in AliasMode and cannot carry svc_params; otherwise this is in ServiceMode, and may include svc_params
target_name: string; ##< Target name, the hostname of the service endpoint.
};
# DNS answer types. # DNS answer types.
# #
# .. zeek:see:: dns_answerr # .. zeek:see:: dns_answerr

View file

@ -172,4 +172,15 @@ export {
[4] = "SHA384", [4] = "SHA384",
} &default = function(n: count): string { return fmt("digest-%d", n); }; } &default = function(n: count): string { return fmt("digest-%d", n); };
## SVCB/HTTPS SvcParam keys, as defined in
## https://www.ietf.org/archive/id/draft-ietf-dnsop-svcb-https-07.txt, sec 14.3.2
const svcparam_keys = {
[0] = "mandatory",
[1] = "alpn",
[2] = "no-default-alpn",
[3] = "port",
[4] = "ipv4hint",
[5] = "ech",
[6] = "ipv6hint",
} &default = function(n: count): string { return fmt("key-%d", n); };
} }

View file

@ -345,6 +345,14 @@ bool DNS_Interpreter::ParseAnswer(detail::DNS_MsgInfo* msg, const u_char*& data,
status = ParseRR_LOC(msg, data, len, rdlength, msg_start); status = ParseRR_LOC(msg, data, len, rdlength, msg_start);
break; break;
case detail::TYPE_SVCB:
status = ParseRR_SVCB(msg, data, len, rdlength, msg_start, TYPE_SVCB);
break;
case detail::TYPE_HTTPS:
status = ParseRR_SVCB(msg, data, len, rdlength, msg_start, TYPE_HTTPS);
break;
default: default:
if ( dns_unknown_reply && ! msg->skip_event ) if ( dns_unknown_reply && ! msg->skip_event )
@ -1687,6 +1695,65 @@ bool DNS_Interpreter::ParseRR_CAA(detail::DNS_MsgInfo* msg, const u_char*& data,
return rdlength == 0; return rdlength == 0;
} }
bool DNS_Interpreter::ParseRR_SVCB(detail::DNS_MsgInfo* msg, const u_char*& data, int& len,
int rdlength, const u_char* msg_start, const RR_Type& svcb_type)
{
const u_char* data_start = data;
// the smallest SVCB/HTTPS rr is 3 bytes:
// the first 2 bytes are for the svc priority, and the third byte is root (0x0)
if ( len < 3 )
{
analyzer->Weird("DNS_SVBC_wrong_length");
return false;
}
uint16_t svc_priority = ExtractShort(data, len);
u_char target_name[513];
int name_len = sizeof(target_name) - 1;
u_char* name_end = ExtractName(data, len, target_name, name_len, msg_start, false);
if ( ! name_end )
return false;
// target name can be root - in this case the alternative endpoint is
// qname itself. make sure that we print "." instead of an empty string
if ( name_end - target_name == 0 )
{
target_name[0] = '.';
target_name[1] = '\0';
name_end = target_name+1;
}
SVCB_DATA svcb_data = {
.svc_priority = svc_priority,
.target_name = make_intrusive<StringVal>(new String(target_name, name_end - target_name, true)),
};
// TODO: parse svcparams
// we consume all the remaining raw data (svc params) but do nothing.
// this should be removed if the svc param parser is ready
std::ptrdiff_t parsed_bytes = data - data_start;
if ( parsed_bytes < rdlength )
{
len -= ( rdlength - parsed_bytes );
data += ( rdlength - parsed_bytes );
}
switch( svcb_type )
{
case detail::TYPE_SVCB:
analyzer->EnqueueConnEvent(dns_SVCB, analyzer->ConnVal(), msg->BuildHdrVal(),
msg->BuildAnswerVal(), msg->BuildSVCB_Val(svcb_data));
break;
case detail::TYPE_HTTPS:
analyzer->EnqueueConnEvent(dns_HTTPS, analyzer->ConnVal(), msg->BuildHdrVal(),
msg->BuildAnswerVal(), msg->BuildSVCB_Val(svcb_data));
break;
default: break; // unreachable. for suppressing compiler warnings.
}
return true;
}
void DNS_Interpreter::SendReplyOrRejectEvent(detail::DNS_MsgInfo* msg, EventHandlerPtr event, void DNS_Interpreter::SendReplyOrRejectEvent(detail::DNS_MsgInfo* msg, EventHandlerPtr event,
const u_char*& data, int& len, String* question_name, const u_char*& data, int& len, String* question_name,
String* original_name) String* original_name)
@ -1987,6 +2054,18 @@ RecordValPtr DNS_MsgInfo::BuildLOC_Val(LOC_DATA* loc)
return r; return r;
} }
RecordValPtr DNS_MsgInfo::BuildSVCB_Val(const SVCB_DATA& svcb)
{
static auto dns_svcb_rr = id::find_type<RecordType>("dns_svcb_rr");
auto r = make_intrusive<RecordVal>(dns_svcb_rr);
r->Assign(0, svcb.svc_priority);
r->Assign(1, svcb.target_name);
// TODO: assign values to svcparams
return r;
}
} // namespace detail } // namespace detail
Contents_DNS::Contents_DNS(Connection* conn, bool orig, detail::DNS_Interpreter* arg_interp) Contents_DNS::Contents_DNS(Connection* conn, bool orig, detail::DNS_Interpreter* arg_interp)

View file

@ -71,6 +71,8 @@ enum RR_Type
TYPE_DS = 43, ///< Delegation signer (RFC 4034) TYPE_DS = 43, ///< Delegation signer (RFC 4034)
TYPE_NSEC3 = 50, TYPE_NSEC3 = 50,
TYPE_NSEC3PARAM = 51, ///< Contains the NSEC3 parameters (RFC 5155) TYPE_NSEC3PARAM = 51, ///< Contains the NSEC3 parameters (RFC 5155)
TYPE_SVCB = 64, ///< SerViCe Binding (RFC draft: https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-07#section-1.1)
TYPE_HTTPS = 65, ///< HTTPS record (HTTPS specific SVCB resource record)
// Obsoleted // Obsoleted
TYPE_SPF = 99, ///< Alternative: storing SPF data in TXT records, using the same format (RFC TYPE_SPF = 99, ///< Alternative: storing SPF data in TXT records, using the same format (RFC
///< 4408). Support for it was discontinued in RFC 7208 ///< 4408). Support for it was discontinued in RFC 7208
@ -148,6 +150,18 @@ enum DNSSEC_Digest
SHA384 = 4, SHA384 = 4,
}; };
///< all keys are defined in RFC draft https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-07#section-14.3.2
enum SVCPARAM_Key
{
mandatory = 0,
alpn = 1,
no_default_alpn = 2,
port = 3,
ipv4hint = 4,
ech = 5,
ipv6hint = 6,
};
struct DNS_RawMsgHdr struct DNS_RawMsgHdr
{ {
unsigned short id; unsigned short id;
@ -269,6 +283,12 @@ struct LOC_DATA
unsigned long altitude; // 32 unsigned long altitude; // 32
}; };
struct SVCB_DATA
{
uint16_t svc_priority; // 2
StringValPtr target_name;
};
class DNS_MsgInfo class DNS_MsgInfo
{ {
public: public:
@ -288,6 +308,7 @@ public:
RecordValPtr BuildDS_Val(struct DS_DATA*); RecordValPtr BuildDS_Val(struct DS_DATA*);
RecordValPtr BuildBINDS_Val(struct BINDS_DATA*); RecordValPtr BuildBINDS_Val(struct BINDS_DATA*);
RecordValPtr BuildLOC_Val(struct LOC_DATA*); RecordValPtr BuildLOC_Val(struct LOC_DATA*);
RecordValPtr BuildSVCB_Val(const struct SVCB_DATA&);
int id; int id;
int opcode; ///< query type, see DNS_Opcode int opcode; ///< query type, see DNS_Opcode
@ -393,6 +414,8 @@ protected:
const u_char* msg_start); const u_char* msg_start);
bool ParseRR_LOC(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, bool ParseRR_LOC(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start); const u_char* msg_start);
bool ParseRR_SVCB(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
const u_char* msg_start, const RR_Type& svcb_type);
void SendReplyOrRejectEvent(detail::DNS_MsgInfo* msg, EventHandlerPtr event, void SendReplyOrRejectEvent(detail::DNS_MsgInfo* msg, EventHandlerPtr event,
const u_char*& data, int& len, String* question_name, const u_char*& data, int& len, String* question_name,
String* original_name); String* original_name);

View file

@ -721,6 +721,37 @@ event dns_SSHFP%(c: connection, msg: dns_msg, ans: dns_answer, algo: count, fpty
## loc: The parsed RDATA of LOC type record. ## loc: The parsed RDATA of LOC type record.
event dns_LOC%(c: connection, msg: dns_msg, ans: dns_answer, loc: dns_loc_rr%); event dns_LOC%(c: connection, msg: dns_msg, ans: dns_answer, loc: dns_loc_rr%);
## Generated for DNS replies of type *SVCB* (General Purpose Service Endpoints).
## See `RFC draft for DNS SVCB/HTTPS <https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-07>`__
## for more information about DNS SVCB/HTTPS resource records.
## For replies with multiple answers, an individual event of the corresponding type is raised for each.
##
## c: The connection, which may be UDP or TCP depending on the type of the
## transport-layer session being analyzed.
##
## msg: The parsed DNS message header.
##
## ans: The type-independent part of the parsed answer record.
##
## svcb: The parsed RDATA of SVCB type record.
event dns_SVCB%(c: connection, msg: dns_msg, ans: dns_answer, svcb: dns_svcb_rr%);
## Generated for DNS replies of type *HTTPS* (HTTPS Specific Service Endpoints).
## See `RFC draft for DNS SVCB/HTTPS <https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-07>`__
## for more information about DNS SVCB/HTTPS resource records.
## Since SVCB and HTTPS records share the same wire format layout, the argument https is dns_svcb_rr.
## For replies with multiple answers, an individual event of the corresponding type is raised for each.
##
## c: The connection, which may be UDP or TCP depending on the type of the
## transport-layer session being analyzed.
##
## msg: The parsed DNS message header.
##
## ans: The type-independent part of the parsed answer record.
##
## https: The parsed RDATA of HTTPS type record.
event dns_HTTPS%(c: connection, msg: dns_msg, ans: dns_answer, https: dns_svcb_rr%);
## Generated at the end of processing a DNS packet. This event is the last ## Generated at the end of processing a DNS packet. This event is the last
## ``dns_*`` event that will be raised for a DNS query/reply and signals that ## ``dns_*`` event that will be raised for a DNS query/reply and signals that
## all resource records have been passed on. ## all resource records have been passed on.

View file

@ -0,0 +1 @@
[svc_priority=1, target_name=.]

View file

@ -0,0 +1 @@
[svc_priority=0, target_name=foo.example.com]

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,9 @@
# @TEST-EXEC: zeek -C -r $TRACES/dns-https.pcap %INPUT > output
# @TEST-EXEC: btest-diff output
@load policy/protocols/dns/auth-addl
event dns_HTTPS(c: connection, msg: dns_msg, ans: dns_answer, https: dns_svcb_rr)
{
print https;
}

View file

@ -0,0 +1,9 @@
# @TEST-EXEC: zeek -C -r $TRACES/dns-svcb.pcap %INPUT > output
# @TEST-EXEC: btest-diff output
@load policy/protocols/dns/auth-addl
event dns_SVCB(c: connection, msg: dns_msg, ans: dns_answer, svcb: dns_svcb_rr)
{
print svcb;
}