mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
Merge remote-tracking branch 'origin/topic/awelzel/dns-naming-authority-pointer'
* origin/topic/awelzel/dns-naming-authority-pointer: DNS: Implement NAPTR RR support DNS: Move extract_char_string() helper around
This commit is contained in:
commit
fbeb3adfe6
13 changed files with 157 additions and 25 deletions
9
CHANGES
9
CHANGES
|
@ -1,3 +1,12 @@
|
||||||
|
8.0.0-dev.506 | 2025-06-24 17:44:06 +0200
|
||||||
|
|
||||||
|
* DNS: Implement NAPTR RR support (Arne Welzel, Corelight)
|
||||||
|
|
||||||
|
My phone is sending NAPTR queries and we reported an unknown RR type 35
|
||||||
|
in weird.log for the response, so figured I'd just add it.
|
||||||
|
|
||||||
|
* DNS: Move extract_char_string() helper around (Arne Welzel, Corelight)
|
||||||
|
|
||||||
8.0.0-dev.503 | 2025-06-24 17:14:28 +0200
|
8.0.0-dev.503 | 2025-06-24 17:14:28 +0200
|
||||||
|
|
||||||
* cluster/zeromq: Short-circuit DoPublishLogWrite() when not initialized (Arne Welzel, Corelight)
|
* cluster/zeromq: Short-circuit DoPublishLogWrite() when not initialized (Arne Welzel, Corelight)
|
||||||
|
|
3
NEWS
3
NEWS
|
@ -114,6 +114,9 @@ New Functionality
|
||||||
|
|
||||||
redef Intel::manage_seen_event_groups = F;
|
redef Intel::manage_seen_event_groups = F;
|
||||||
|
|
||||||
|
- The DNS analyzer was extended to support NAPTR RRs (RFC 2915, RFC 3403).
|
||||||
|
A corresponding ``dns_NAPTR_reply`` event was added.
|
||||||
|
|
||||||
Changed Functionality
|
Changed Functionality
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
8.0.0-dev.503
|
8.0.0-dev.506
|
||||||
|
|
|
@ -3048,6 +3048,20 @@ type dns_svcb_rr: record {
|
||||||
target_name: string; ##< Target name, the hostname of the service endpoint.
|
target_name: string; ##< Target name, the hostname of the service endpoint.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
## A NAPTR record.
|
||||||
|
##
|
||||||
|
## See also RFC 2915 - The Naming Authority Pointer (NAPTR) DNS Resource Record.
|
||||||
|
##
|
||||||
|
## .. zeek:see:: dns_NAPTR_reply
|
||||||
|
type dns_naptr_rr: record {
|
||||||
|
order: count; ##< Order in which to process NAPTR records.
|
||||||
|
preference: count; ##< Preference specifying processing order for *equal* :zeek:field:`dns_naptr_rr$order` fields.
|
||||||
|
flags: string; ##< Flags to control rewriting. E.g. "u", "a", "s" or "p".
|
||||||
|
service: string; ##< The services available down this rewrite path.
|
||||||
|
regexp: string; ##< Substitution expression to be applied to the original query.
|
||||||
|
replacement: string; ##< The next name to query, where the type is depending on the :zeek:field:`dns_naptr_rr$flags` field.
|
||||||
|
};
|
||||||
|
|
||||||
# DNS answer types.
|
# DNS answer types.
|
||||||
#
|
#
|
||||||
# .. zeek:see:: dns_answer
|
# .. zeek:see:: dns_answer
|
||||||
|
|
|
@ -537,6 +537,27 @@ event dns_SRV_reply(c: connection, msg: dns_msg, ans: dns_answer, target: string
|
||||||
hook DNS::do_reply(c, msg, ans, target);
|
hook DNS::do_reply(c, msg, ans, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event dns_NAPTR_reply(c: connection, msg: dns_msg, ans: dns_answer, naptr: dns_naptr_rr) &priority=5
|
||||||
|
{
|
||||||
|
# Just encode all the fields for NAPTR RR in the reply string.
|
||||||
|
local tmp = "";
|
||||||
|
|
||||||
|
if ( |naptr$regexp| > 0 )
|
||||||
|
tmp += naptr$regexp;
|
||||||
|
|
||||||
|
if ( |naptr$replacement| > 0 )
|
||||||
|
{
|
||||||
|
if ( |tmp| > 0 )
|
||||||
|
tmp += " ";
|
||||||
|
|
||||||
|
tmp += naptr$replacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
local r = fmt("NAPTR %s %s %s %s %s", naptr$order, naptr$preference, naptr$flags, naptr$service, tmp);
|
||||||
|
|
||||||
|
hook DNS::do_reply(c, msg, ans, r);
|
||||||
|
}
|
||||||
|
|
||||||
# TODO: figure out how to handle these
|
# TODO: figure out how to handle these
|
||||||
#event dns_EDNS(c: connection, msg: dns_msg, ans: dns_answer)
|
#event dns_EDNS(c: connection, msg: dns_msg, ans: dns_answer)
|
||||||
# {
|
# {
|
||||||
|
|
|
@ -11,10 +11,37 @@
|
||||||
#include "zeek/Event.h"
|
#include "zeek/Event.h"
|
||||||
#include "zeek/NetVar.h"
|
#include "zeek/NetVar.h"
|
||||||
#include "zeek/RunState.h"
|
#include "zeek/RunState.h"
|
||||||
|
#include "zeek/Val.h"
|
||||||
#include "zeek/ZeekString.h"
|
#include "zeek/ZeekString.h"
|
||||||
#include "zeek/analyzer/protocol/dns/events.bif.h"
|
#include "zeek/analyzer/protocol/dns/events.bif.h"
|
||||||
#include "zeek/session/Manager.h"
|
#include "zeek/session/Manager.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
zeek::StringValPtr extract_char_string(zeek::analyzer::Analyzer* analyzer, const u_char*& data, int& len, int& rdlen) {
|
||||||
|
if ( rdlen <= 0 )
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
uint8_t str_size = data[0];
|
||||||
|
|
||||||
|
--rdlen;
|
||||||
|
--len;
|
||||||
|
++data;
|
||||||
|
|
||||||
|
if ( str_size > rdlen ) {
|
||||||
|
analyzer->Weird("DNS_TXT_char_str_past_rdlen");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rval = zeek::make_intrusive<zeek::StringVal>(str_size, reinterpret_cast<const char*>(data));
|
||||||
|
|
||||||
|
rdlen -= str_size;
|
||||||
|
len -= str_size;
|
||||||
|
data += str_size;
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace zeek::analyzer::dns {
|
namespace zeek::analyzer::dns {
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
@ -273,6 +300,8 @@ bool DNS_Interpreter::ParseAnswer(detail::DNS_MsgInfo* msg, const u_char*& data,
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case detail::TYPE_NAPTR: status = ParseRR_NAPTR(msg, data, len, rdlength, msg_start); break;
|
||||||
|
|
||||||
case detail::TYPE_EDNS: status = ParseRR_EDNS(msg, data, len, rdlength, msg_start); break;
|
case detail::TYPE_EDNS: status = ParseRR_EDNS(msg, data, len, rdlength, msg_start); break;
|
||||||
|
|
||||||
case detail::TYPE_TKEY: status = ParseRR_TKEY(msg, data, len, rdlength, msg_start); break;
|
case detail::TYPE_TKEY: status = ParseRR_TKEY(msg, data, len, rdlength, msg_start); break;
|
||||||
|
@ -602,6 +631,50 @@ bool DNS_Interpreter::ParseRR_SRV(detail::DNS_MsgInfo* msg, const u_char*& data,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DNS_Interpreter::ParseRR_NAPTR(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
|
||||||
|
const u_char* msg_start) {
|
||||||
|
zeek_uint_t order = ExtractShort(data, len);
|
||||||
|
zeek_uint_t preference = ExtractShort(data, len);
|
||||||
|
rdlength -= 4;
|
||||||
|
|
||||||
|
if ( len <= 0 || rdlength <= 0 ) {
|
||||||
|
analyzer->AnalyzerViolation("DNS_NAPTR_too_short");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These all check rdlength and return nullptr if there's not enough data available.
|
||||||
|
auto flags = extract_char_string(analyzer, data, len, rdlength);
|
||||||
|
auto service = extract_char_string(analyzer, data, len, rdlength);
|
||||||
|
auto regexp = extract_char_string(analyzer, data, len, rdlength);
|
||||||
|
|
||||||
|
// The replacement string is a name. Compression shouldn't be used, but doesn't seem
|
||||||
|
// we have a helper that would allow to control this.
|
||||||
|
u_char replacement[513];
|
||||||
|
int replacement_len = sizeof(replacement) - 1;
|
||||||
|
u_char* replacement_end = ExtractName(data, len, replacement, replacement_len, msg_start, false);
|
||||||
|
|
||||||
|
if ( ! flags || ! service || ! regexp || ! replacement_end ) {
|
||||||
|
analyzer->AnalyzerViolation("DNS_NAPTR_RR_too_short");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( dns_NAPTR_reply && ! msg->skip_event ) {
|
||||||
|
static auto dns_naptr_rr = id::find_type<RecordType>("dns_naptr_rr");
|
||||||
|
auto r = make_intrusive<RecordVal>(dns_naptr_rr);
|
||||||
|
|
||||||
|
r->Assign(0, order);
|
||||||
|
r->Assign(1, preference);
|
||||||
|
r->Assign(2, std::move(flags));
|
||||||
|
r->Assign(3, std::move(service));
|
||||||
|
r->Assign(4, std::move(regexp));
|
||||||
|
r->Assign(5, zeek::make_intrusive<StringVal>(new String(replacement, replacement_end - replacement, true)));
|
||||||
|
|
||||||
|
analyzer->EnqueueConnEvent(dns_NAPTR_reply, analyzer->ConnVal(), msg->BuildHdrVal(), msg->BuildAnswerVal(), r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool DNS_Interpreter::ParseRR_EDNS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
|
bool DNS_Interpreter::ParseRR_EDNS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
|
||||||
const u_char* msg_start) {
|
const u_char* msg_start) {
|
||||||
if ( dns_EDNS_addl && ! msg->skip_event )
|
if ( dns_EDNS_addl && ! msg->skip_event )
|
||||||
|
@ -1413,30 +1486,6 @@ bool DNS_Interpreter::ParseRR_WKS(detail::DNS_MsgInfo* msg, const u_char*& data,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static StringValPtr extract_char_string(analyzer::Analyzer* analyzer, const u_char*& data, int& len, int& rdlen) {
|
|
||||||
if ( rdlen <= 0 )
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
uint8_t str_size = data[0];
|
|
||||||
|
|
||||||
--rdlen;
|
|
||||||
--len;
|
|
||||||
++data;
|
|
||||||
|
|
||||||
if ( str_size > rdlen ) {
|
|
||||||
analyzer->Weird("DNS_TXT_char_str_past_rdlen");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rval = make_intrusive<StringVal>(str_size, reinterpret_cast<const char*>(data));
|
|
||||||
|
|
||||||
rdlen -= str_size;
|
|
||||||
len -= str_size;
|
|
||||||
data += str_size;
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DNS_Interpreter::ParseRR_HINFO(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength) {
|
bool DNS_Interpreter::ParseRR_HINFO(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength) {
|
||||||
if ( ! dns_HINFO_reply || msg->skip_event ) {
|
if ( ! dns_HINFO_reply || msg->skip_event ) {
|
||||||
data += rdlength;
|
data += rdlength;
|
||||||
|
|
|
@ -364,6 +364,7 @@ protected:
|
||||||
bool ParseRR_MX(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start);
|
bool ParseRR_MX(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start);
|
||||||
bool ParseRR_NBS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start);
|
bool ParseRR_NBS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start);
|
||||||
bool ParseRR_SRV(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start);
|
bool ParseRR_SRV(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start);
|
||||||
|
bool ParseRR_NAPTR(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start);
|
||||||
bool ParseRR_EDNS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start);
|
bool ParseRR_EDNS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start);
|
||||||
bool ParseRR_EDNS_ECS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
|
bool ParseRR_EDNS_ECS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength,
|
||||||
const u_char* msg_start);
|
const u_char* msg_start);
|
||||||
|
|
|
@ -467,6 +467,23 @@ event dns_CAA_reply%(c: connection, msg: dns_msg, ans: dns_answer, flags: count,
|
||||||
## dns_skip_addl dns_skip_all_addl dns_skip_all_auth dns_skip_auth
|
## dns_skip_addl dns_skip_all_addl dns_skip_all_auth dns_skip_auth
|
||||||
event dns_SRV_reply%(c: connection, msg: dns_msg, ans: dns_answer, target: string, priority: count, weight: count, p: count%);
|
event dns_SRV_reply%(c: connection, msg: dns_msg, ans: dns_answer, target: string, priority: count, weight: count, p: count%);
|
||||||
|
|
||||||
|
## Generated for DNS replies of type *NAPTR*. 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.
|
||||||
|
##
|
||||||
|
## naptr: The parsed RDATA of NAPTR type record.
|
||||||
|
##
|
||||||
|
## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl
|
||||||
|
## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply
|
||||||
|
## dns_TSIG_addl dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_SRV_reply dns_end
|
||||||
|
event dns_NAPTR_reply%(c: connection, msg: dns_msg, ans: dns_answer, naptr: dns_naptr_rr%);
|
||||||
|
|
||||||
## Generated on DNS reply resource records when the type of record is not one
|
## Generated on DNS reply resource records when the type of record is not one
|
||||||
## that Zeek knows how to parse and generate another more specific event.
|
## that Zeek knows how to parse and generate another more specific event.
|
||||||
##
|
##
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
NAPTR, [id=20970, opcode=0, rcode=0, QR=T, AA=F, TC=F, RD=T, RA=T, Z=0, AD=F, CD=F, num_queries=1, num_answers=1, num_auth=0, num_addl=0], [answer_type=1, query=fp-de-carrier-vodafone.rcs.telephony.goog, qtype=35, qclass=1, TTL=2.0 mins 48.0 secs], [order=100, preference=100, flags=s, service=SIPS+D2T, regexp=, replacement=_sips._tcp.fp-de-carrier-vodafone.rcs.telephony.goog]
|
|
@ -0,0 +1,3 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
uid query qclass_name qtype_name answers
|
||||||
|
CHhAvVGS1DHFjwGM9 fp-de-carrier-vodafone.rcs.telephony.goog C_INTERNET NAPTR NAPTR 100 100 s SIPS+D2T _sips._tcp.fp-de-carrier-vodafone.rcs.telephony.goog
|
|
@ -0,0 +1,2 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
NAPTR, [id=20970, opcode=0, rcode=0, QR=T, AA=F, TC=F, RD=T, RA=T, Z=0, AD=F, CD=F, num_queries=1, num_answers=1, num_auth=0, num_addl=0], [answer_type=1, query=fp-de-carrier-vodafone.rcs.telephony.goog, qtype=35, qclass=1, TTL=2.0 mins 48.0 secs], [order=100, preference=100, flags=s, service=SIPS+D2T, regexp=, replacement=_sips._tcp.fp-de-carrier-vodafone.rcs.telephony.goog]
|
BIN
testing/btest/Traces/dns/naptr.pcap
Normal file
BIN
testing/btest/Traces/dns/naptr.pcap
Normal file
Binary file not shown.
11
testing/btest/scripts/base/protocols/dns/naptr.zeek
Normal file
11
testing/btest/scripts/base/protocols/dns/naptr.zeek
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# @TEST-EXEC: zeek -b -r $TRACES/dns/naptr.pcap %INPUT >out
|
||||||
|
# @TEST-EXEC: btest-diff out
|
||||||
|
# @TEST-EXEC: zeek-cut -m uid query qclass_name qtype_name answers < dns.log > dns.log.cut
|
||||||
|
# @TEST-EXEC: btest-diff dns.log.cut
|
||||||
|
|
||||||
|
@load base/protocols/dns
|
||||||
|
|
||||||
|
event dns_NAPTR_reply(c: connection, msg: dns_msg, ans: dns_answer, naptr: dns_naptr_rr)
|
||||||
|
{
|
||||||
|
print "NAPTR", msg, ans, naptr;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue