diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000000..e48e2b73cd --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "${default}", + "${workspaceFolder}/**" + ], + "defines": [], + "macFrameworkPath": [], + "compilerPath": "/usr/local/bin/gcc-11", + "cStandard": "gnu17", + "cppStandard": "gnu++17", + "intelliSenseMode": "macos-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..ed208baa59 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "thread": "cpp" + } +} \ No newline at end of file diff --git a/auxil/highwayhash b/auxil/highwayhash index ca0ff296b1..2361494e04 160000 --- a/auxil/highwayhash +++ b/auxil/highwayhash @@ -1 +1 @@ -Subproject commit ca0ff296b116354957276cef2c61be171ac2f897 +Subproject commit 2361494e0400d52eb76d2c6c62db72168ebe69d0 diff --git a/doc b/doc index fefd7e6ceb..f562d75aef 160000 --- a/doc +++ b/doc @@ -1 +1 @@ -Subproject commit fefd7e6ceb67dd011c268c658171967f1281b970 +Subproject commit f562d75aef2e7c401913de122e4f888d42ae5f8e diff --git a/scripts/base/init-bare.zeek b/scripts/base/init-bare.zeek index dc6d8ce802..57ddd514db 100644 --- a/scripts/base/init-bare.zeek +++ b/scripts/base/init-bare.zeek @@ -3884,6 +3884,12 @@ type dns_loc_rr: record { is_query: count; ##< The RR is a query/Response. }; +type dns_svcb_rr: record { + svc_priority: count; ##< Service priority. (AliasMode? ServiceMode?) + target_name: string; ##< Target name, the hostname of the service endpoint. + svc_params: table[count] of vector of string; ##< service parameters as key-value pairs +} + # DNS answer types. # # .. zeek:see:: dns_answerr diff --git a/scripts/base/protocols/dns/consts.zeek b/scripts/base/protocols/dns/consts.zeek index a1e0f53a0b..283c2fc0b7 100644 --- a/scripts/base/protocols/dns/consts.zeek +++ b/scripts/base/protocols/dns/consts.zeek @@ -172,4 +172,15 @@ export { [4] = "SHA384", } &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); }; } diff --git a/src/analyzer/protocol/dns/DNS.cc b/src/analyzer/protocol/dns/DNS.cc index fe5424c00e..72c88cc488 100644 --- a/src/analyzer/protocol/dns/DNS.cc +++ b/src/analyzer/protocol/dns/DNS.cc @@ -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); 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: if ( dns_unknown_reply && ! msg->skip_event ) @@ -1687,6 +1695,39 @@ bool DNS_Interpreter::ParseRR_CAA(detail::DNS_MsgInfo* msg, const u_char*& data, 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) + { + unsigned short 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; + + SVCB_DATA svcb_data = { + .svc_priority = svc_priority, + .target_name = new String(target_name, name_end - target_name, true), + .svc_params = nullptr, + }; + + // TODO: parse svcparams + + 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; + } + return true; + } + void DNS_Interpreter::SendReplyOrRejectEvent(detail::DNS_MsgInfo* msg, EventHandlerPtr event, const u_char*& data, int& len, String* question_name, String* original_name) @@ -1987,6 +2028,18 @@ RecordValPtr DNS_MsgInfo::BuildLOC_Val(LOC_DATA* loc) return r; } +RecordValPtr DNS_MsgInfo::BuildSVCB_Val(SVCB_DATA* svcb) + { + static auto dns_svcb_rr = id::find_type("dns_svcb_rr"); + auto r = make_intrusive(dns_svcb_rr); + + r->Assign(0, svcb->svc_priority); + r->Assign(1, make_intrusive(svcb->target_name)); + + // TODO: assign svcparams + return dns_svcb_rr; + } + } // namespace detail Contents_DNS::Contents_DNS(Connection* conn, bool orig, detail::DNS_Interpreter* arg_interp) diff --git a/src/analyzer/protocol/dns/DNS.h b/src/analyzer/protocol/dns/DNS.h index c1abaa06e2..0a0476c546 100644 --- a/src/analyzer/protocol/dns/DNS.h +++ b/src/analyzer/protocol/dns/DNS.h @@ -71,6 +71,8 @@ enum RR_Type TYPE_DS = 43, ///< Delegation signer (RFC 4034) TYPE_NSEC3 = 50, 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 TYPE_SPF = 99, ///< Alternative: storing SPF data in TXT records, using the same format (RFC ///< 4408). Support for it was discontinued in RFC 7208 @@ -148,6 +150,18 @@ enum DNSSEC_Digest 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 { unsigned short id; @@ -269,6 +283,20 @@ struct LOC_DATA unsigned long altitude; // 32 }; +struct SVCB_DATA + { + unsigned short svc_priority; // 2 + String* target_name; + SVCPARAM_KV* svc_params; + } + +struct SVCPARAM_KV + { + String* key; + String* value; + SVCPARAM_KV* next; + }; + class DNS_MsgInfo { public: @@ -288,6 +316,7 @@ public: RecordValPtr BuildDS_Val(struct DS_DATA*); RecordValPtr BuildBINDS_Val(struct BINDS_DATA*); RecordValPtr BuildLOC_Val(struct LOC_DATA*); + RecordValPtr BuildSVCB_Val(struct SVCB_DATA*); int id; int opcode; ///< query type, see DNS_Opcode @@ -393,6 +422,8 @@ protected: const u_char* msg_start); bool ParseRR_LOC(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, 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, const u_char*& data, int& len, String* question_name, String* original_name); diff --git a/src/analyzer/protocol/dns/events.bif b/src/analyzer/protocol/dns/events.bif index 40c6cf9f66..7e241c4d01 100644 --- a/src/analyzer/protocol/dns/events.bif +++ b/src/analyzer/protocol/dns/events.bif @@ -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. event dns_LOC%(c: connection, msg: dns_msg, ans: dns_answer, loc: dns_loc_rr%); +## Generated for DNS replies of type *SVCB*. +## See `RFC draft `__ +## for more information about the 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*. +## See `RFC draft `__ +## for more information about the DNS SVCB/HTTPS resource records. +## Since SVCB and HTTPS record 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 SVCB 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 ## ``dns_*`` event that will be raised for a DNS query/reply and signals that ## all resource records have been passed on.