// 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,
    Ed25519 = 15,
    Ed448 = 16,
    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<AddrVal> 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 TKEY_DATA {
    String* alg_name;
    unsigned long inception;
    unsigned long expiration;
    unsigned short mode;
    unsigned short error;
    String* key;
};

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 BuildTKEY_Val(struct TKEY_DATA*);
    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 final {
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_TKEY(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;
    bool is_netbios;
};

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
