// See the file "COPYING" in the main distribution directory for copyright. #pragma once #include "zeek/analyzer/protocol/tcp/TCP.h" #include "zeek/binpac_zeek.h" namespace zeek::analyzer::dns { namespace detail { enum DNS_Opcode { DNS_OP_QUERY = 0, ///< standard query DNS_OP_IQUERY = 1, ///< reverse query // ### Is server status 2 or 3? RFC 1035 says it's 2 // DNS_OP_SERVER_STATUS = 3, ///< server status request DNS_OP_SERVER_STATUS = 2, ///< server status request // Netbios operations (query = 0). NETBIOS_REGISTRATION = 5, NETBIOS_RELEASE = 6, NETBIOS_WACK = 7, // wait for ACK NETBIOS_REFRESH = 8, }; enum DNS_Code { DNS_CODE_OK = 0, ///< no error DNS_CODE_FORMAT_ERR = 1, ///< format error DNS_CODE_SERVER_FAIL = 2, ///< server failure DNS_CODE_NAME_ERR = 3, ///< no such domain DNS_CODE_NOT_IMPL = 4, ///< not implemented DNS_CODE_REFUSED = 5, ///< refused }; enum RR_Type { TYPE_A = 1, ///< host address TYPE_NS = 2, ///< authoritative name server TYPE_CNAME = 5, ///< canonical name TYPE_SOA = 6, ///< start of authority TYPE_WKS = 11, ///< well known service TYPE_PTR = 12, ///< domain name pointer TYPE_HINFO = 13, ///< host information TYPE_MX = 15, ///< mail routing information TYPE_TXT = 16, ///< text strings TYPE_SIG = 24, ///< digital signature (RFC 2535) TYPE_KEY = 25, ///< public key (RFC 2535) TYPE_PX = 26, ///< pointer to X.400/RFC822 mapping info (RFC 1664) TYPE_AAAA = 28, ///< IPv6 address (RFC 1886) TYPE_LOC = 29, ///< Location information about hosts (RFC 1876) TYPE_NBS = 32, ///< Netbios name (RFC 1002) TYPE_SRV = 33, ///< service location (RFC 2052) TYPE_NAPTR = 35, ///< naming authority pointer (RFC 2168) TYPE_KX = 36, ///< Key Exchange (RFC 2230) TYPE_CERT = 37, ///< Certificate (RFC 2538) TYPE_A6 = 38, ///< IPv6 address with indirection (RFC 2874) TYPE_DNAME = 39, ///< Non-terminal DNS name redirection (RFC 2672) TYPE_EDNS = 41, ///< OPT pseudo-RR (RFC 2671) TYPE_SSHFP = 44, ///< SSH Public Key Fingerprint (RFC 4255) TYPE_TKEY = 249, ///< Transaction Key (RFC 2930) TYPE_TSIG = 250, ///< Transaction Signature (RFC 2845) TYPE_CAA = 257, ///< Certification Authority Authorization (RFC 6844) // DNSSEC RR's TYPE_RRSIG = 46, ///< RR Signature record type (RFC4043) TYPE_NSEC = 47, ///< Next Secure record (RFC4043) TYPE_DNSKEY = 48, ///< DNS Key record (RFC 4034) 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 // The following are only valid in queries. TYPE_AXFR = 252, TYPE_ALL = 255, TYPE_WINS = 65281, ///< Microsoft's WINS RR TYPE_WINSR = 65282, ///< Microsoft's WINS-R RR // Private use RR TYPE range: 65280 - 65534 TYPE_BINDS = 65534, ///< Bind9's Private Type Rec for signaling state of signing process }; #define DNS_CLASS_IN 1 #define DNS_CLASS_ANY 255 enum DNS_AnswerType { DNS_QUESTION, DNS_ANSWER, DNS_AUTHORITY, DNS_ADDITIONAL, }; // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml // DNS EDNS0 Option Codes (OPT) enum EDNS_OPT_Type { TYPE_LLQ = 1, ///< https://www.iana.org/go/draft-sekar-dns-llq-06 TYPE_UL = 2, ///< http://files.dns-sd.org/draft-sekar-dns-ul.txt TYPE_NSID = 3, ///< RFC5001 TYPE_DAU = 5, ///< RFC6975 TYPE_DHU = 6, ///< RFC6975 TYPE_N3U = 7, ///< RFC6975 TYPE_ECS = 8, ///< RFC7871 TYPE_EXPIRE = 9, ///< RFC7314 TYPE_COOKIE = 10, ///< RFC7873 TYPE_TCP_KA = 11, ///< RFC7828 TYPE_PAD = 12, ///< RFC7830 TYPE_CHAIN = 13, ///< RFC7901 TYPE_KEY_TAG = 14, ///< RFC8145 TYPE_ERROR = 15, ///< https://www.iana.org/go/draft-ietf-dnsop-extended-error-16 TYPE_CLIENT_TAG = 16, ///< https://www.iana.org/go/draft-bellis-dnsop-edns-tags TYPE_SERVER_TAG = 17, ///< https://www.iana.org/go/draft-bellis-dnsop-edns-tags TYPE_DEVICE_ID = 26946 ///< https://docs.umbrella.com/developer/networkdevices-api/identifying-dns-traffic2 }; enum DNSSEC_Algo { reserved0 = 0, RSA_MD5 = 1, ///< [RFC2537] NOT RECOMMENDED Diffie_Hellman = 2, ///< [RFC2539] DSA_SHA1 = 3, ///< [RFC2536] OPTIONAL Elliptic_Curve = 4, RSA_SHA1 = 5, ///< [RFC3110] MANDATORY DSA_NSEC3_SHA1 = 6, RSA_SHA1_NSEC3_SHA1 = 7, RSA_SHA256 = 8, RSA_SHA512 = 10, GOST_R_34_10_2001 = 12, ECDSA_curveP256withSHA256 = 13, ECDSA_curveP384withSHA384 = 14, Indirect = 252, ///< PrivateDNS = 253, ///< OPTIONAL PrivateOID = 254, ///< OPTIONAL reserved255 = 255, }; enum DNSSEC_Digest { reserved = 0, SHA1 = 1, ///< [RFC3110] MANDATORY SHA256 = 2, GOST_R_34_11_94 = 3, 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; unsigned short flags; unsigned short qdcount; unsigned short ancount; unsigned short nscount; unsigned short arcount; }; struct EDNS_ADDITIONAL { // size unsigned short name; // - unsigned short type; // 16 : ExtractShort(data, len) unsigned short payload_size; // 16 unsigned short extended_rcode; // 8 unsigned short version; // 8 unsigned short z; // 16 unsigned short rdata_len; // 16 }; struct EDNS_ECS { StringValPtr ecs_family; ///< EDNS client subnet address family uint16_t ecs_src_pfx_len; ///< EDNS client subnet source prefix length uint16_t ecs_scp_pfx_len; ///< EDNS client subnet scope prefix length IntrusivePtr ecs_addr; ///< EDNS client subnet address }; struct EDNS_TCP_KEEPALIVE { bool keepalive_timeout_omitted; ///< whether the keepalive timeout is omitted uint16_t keepalive_timeout; ///< the timeout value (in 100ms) sent by the client/server }; struct EDNS_COOKIE { zeek::String* client_cookie; ///< cookie value sent by the client (8 bytes) zeek::String* server_cookie; ///< cookie value sent by the server (0 or 8-32 bytes) }; struct TSIG_DATA { String* alg_name; unsigned long time_s; unsigned short time_ms; String* sig; unsigned short fudge; unsigned short orig_id; unsigned short rr_error; }; struct RRSIG_DATA { unsigned short type_covered; // 16 : ExtractShort(data, len) unsigned short algorithm; // 8 unsigned short labels; // 8 uint32_t orig_ttl; // 32 unsigned long sig_exp; // 32 unsigned long sig_incep; // 32 unsigned short key_tag; // 16 String* signer_name; String* signature; }; struct DNSKEY_DATA { unsigned short dflags; // 16 : ExtractShort(data, len) unsigned short dalgorithm; // 8 unsigned short dprotocol; // 8 String* public_key; // Variable length Public Key }; struct NSEC3_DATA { unsigned short nsec_flags; unsigned short nsec_hash_algo; unsigned short nsec_iter; unsigned short nsec_salt_len; String* nsec_salt; unsigned short nsec_hlen; String* nsec_hash; VectorValPtr bitmaps; }; struct NSEC3PARAM_DATA { unsigned short nsec_flags; // 8 unsigned short nsec_hash_algo; // 8 unsigned short nsec_iter; // 16 : ExtractShort(data, len) unsigned short nsec_salt_len; // 8 String* nsec_salt; // Variable length salt }; struct DS_DATA { unsigned short key_tag; // 16 : ExtractShort(data, len) unsigned short algorithm; // 8 unsigned short digest_type; // 8 String* digest_val; // Variable length Digest of DNSKEY RR }; struct BINDS_DATA { unsigned short algorithm; // 8 unsigned short key_id; // 16 : ExtractShort(data, len) unsigned short removal_flag; // 8 String* complete_flag; // 8 }; struct LOC_DATA { unsigned short version; // 8 unsigned short size; // 8 unsigned short horiz_pre; // 8 unsigned short vert_pre; // 8 unsigned long latitude; // 32 unsigned long longitude; // 32 unsigned long altitude; // 32 }; struct SVCB_DATA { uint16_t svc_priority; // 2 StringValPtr target_name; }; class DNS_MsgInfo { public: DNS_MsgInfo(DNS_RawMsgHdr* hdr, int is_query); RecordValPtr BuildHdrVal(); RecordValPtr BuildAnswerVal(); RecordValPtr BuildEDNS_Val(); RecordValPtr BuildEDNS_ECS_Val(struct EDNS_ECS*); RecordValPtr BuildEDNS_TCP_KA_Val(struct EDNS_TCP_KEEPALIVE*); RecordValPtr BuildEDNS_COOKIE_Val(struct EDNS_COOKIE*); RecordValPtr BuildTSIG_Val(struct TSIG_DATA*); RecordValPtr BuildRRSIG_Val(struct RRSIG_DATA*); RecordValPtr BuildDNSKEY_Val(struct DNSKEY_DATA*); RecordValPtr BuildNSEC3_Val(struct NSEC3_DATA*); RecordValPtr BuildNSEC3PARAM_Val(struct NSEC3PARAM_DATA*); RecordValPtr BuildDS_Val(struct DS_DATA*); RecordValPtr BuildBINDS_Val(struct BINDS_DATA*); RecordValPtr BuildLOC_Val(struct LOC_DATA*); RecordValPtr BuildSVCB_Val(const struct SVCB_DATA&); int id; int opcode; ///< query type, see DNS_Opcode int rcode; ///< return code, see DNS_Code int QR; ///< query record flag int AA; ///< authoritative answer flag int TC; ///< truncated - size > 512 bytes for udp int RD; ///< recursion desired int RA; ///< recursion available int Z; ///< 3 bit field (includes AD and CD) int AD; ///< authentic data int CD; ///< checking disabled int qdcount; ///< number of questions int ancount; ///< number of answers int nscount; ///< number of authority RRs int arcount; ///< number of additional RRs int is_query; ///< whether it came from the session initiator StringValPtr query_name; RR_Type atype; int aclass; ///< normally = 1, inet uint32_t ttl; DNS_AnswerType answer_type; int skip_event; ///< if true, don't generate corresponding events // int answer_count; ///< count of responders. if >1 and not ///< identical answer, there may be problems // uint32* addr; ///< cache value to pass back results ///< for forward lookups }; class DNS_Interpreter { public: explicit DNS_Interpreter(analyzer::Analyzer* analyzer); void ParseMessage(const u_char* data, int len, int is_query); void Timeout() { } protected: void EndMessage(detail::DNS_MsgInfo* msg); bool ParseQuestions(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, const u_char* start); bool ParseAnswers(detail::DNS_MsgInfo* msg, int n, detail::DNS_AnswerType answer_type, const u_char*& data, int& len, const u_char* start); bool ParseQuestion(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, const u_char* start); bool ParseAnswer(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, const u_char* start); u_char* ExtractName(const u_char*& data, int& len, u_char* label, int label_len, const u_char* msg_start, bool downcase = true); bool ExtractLabel(const u_char*& data, int& len, u_char*& label, int& label_len, const u_char* msg_start); uint16_t ExtractShort(const u_char*& data, int& len); uint32_t ExtractLong(const u_char*& data, int& len); void ExtractOctets(const u_char*& data, int& len, String** p); String* ExtractStream(const u_char*& data, int& len, int sig_len); bool ParseRR_Name(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_SOA(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_SRV(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, const u_char* msg_start); bool ParseRR_A(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength); bool ParseRR_AAAA(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength); bool ParseRR_WKS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength); bool ParseRR_HINFO(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength); bool ParseRR_TXT(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_SPF(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_CAA(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_TSIG(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_RRSIG(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_DNSKEY(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_NSEC(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_NSEC3(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_NSEC3PARAM(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_DS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_BINDS(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start); bool ParseRR_SSHFP(detail::DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, 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); analyzer::Analyzer* analyzer; bool first_message; }; enum TCP_DNS_state { DNS_LEN_HI, ///< looking for the high-order byte of the length DNS_LEN_LO, ///< looking for the low-order byte of the length DNS_MESSAGE_BUFFER, ///< building up the message in the buffer }; } // namespace detail // Support analyzer which chunks the TCP stream into "packets". // ### This should be merged with TCP_Contents_RPC. class Contents_DNS final : public analyzer::tcp::TCP_SupportAnalyzer { public: Contents_DNS(Connection* c, bool orig, detail::DNS_Interpreter* interp); ~Contents_DNS() override; void Flush(); ///< process any partially-received data detail::TCP_DNS_state State() const { return state; } protected: void DeliverStream(int len, const u_char* data, bool orig) override; void ProcessChunk(int& len, const u_char*& data, bool orig); detail::DNS_Interpreter* interp; u_char* msg_buf; int buf_n; ///< number of bytes in msg_buf int buf_len; ///< size of msg_buf int msg_size; ///< expected size of message detail::TCP_DNS_state state; }; // Works for both TCP and UDP. class DNS_Analyzer final : public analyzer::tcp::TCP_ApplicationAnalyzer { public: explicit DNS_Analyzer(Connection* conn); ~DNS_Analyzer() override; void DeliverPacket(int len, const u_char* data, bool orig, uint64_t seq, const IP_Hdr* ip, int caplen) override; void Init() override; void Done() override; void ConnectionClosed(analyzer::tcp::TCP_Endpoint* endpoint, analyzer::tcp::TCP_Endpoint* peer, bool gen_event) override; void ExpireTimer(double t); static analyzer::Analyzer* Instantiate(Connection* conn) { return new DNS_Analyzer(conn); } protected: detail::DNS_Interpreter* interp; Contents_DNS* contents_dns_orig; Contents_DNS* contents_dns_resp; }; } // namespace zeek::analyzer::dns