//--------------------------------------------------------------------------
// Copyright (C) 2014-2025 Cisco and/or its affiliates. All rights reserved.
// Copyright (C) 2004-2013 Sourcefire, Inc.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License Version 2 as published
// by the Free Software Foundation.  You may not use, modify or distribute
// this program under any other version of the GNU General Public License.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//--------------------------------------------------------------------------

// dns.h author Steven Sturges

#ifndef DNS_H
#define DNS_H

#include <set>

#include "flow/flow.h"
#include "protocols/packet.h"
#include "pub_sub/dns_events.h"

#include "dns_module.h"

// Implementation header with definitions, datatypes and flowdata class for
// DNS service inspector.

// Directional defines
#define DNS_DIR_FROM_SERVER 1
#define DNS_DIR_FROM_CLIENT 2

struct DNSHdr
{
    uint16_t id = 0;
    uint16_t flags = 0;
    uint16_t questions = 0;
    uint16_t answers = 0;
    uint16_t authorities = 0;
    uint16_t additionals = 0;
};

#define DNS_HDR_FLAG_REPLY_CODE_MASK        0x000F
#define DNS_HDR_FLAG_NON_AUTHENTICATED_OK   0x0010
#define DNS_HDR_FLAG_ANS_AUTHENTICATED      0x0020
#define DNS_HDR_FLAG_RESERVED               0x0040
#define DNS_HDR_FLAG_RECURSION_AVAIL        0x0080
#define DNS_HDR_FLAG_RECURSION_DESIRED      0x0100
#define DNS_HDR_FLAG_TRUNCATED              0x0200
#define DNS_HDR_FLAG_AUTHORITATIVE          0x0400
#define DNS_HDR_FLAG_OPCODE_MASK            0x7800
#define DNS_HDR_FLAG_RESPONSE               0x8000

#define DNS_HDR_FLAG_Z (DNS_HDR_FLAG_NON_AUTHENTICATED_OK | DNS_HDR_FLAG_ANS_AUTHENTICATED | DNS_HDR_FLAG_RESERVED)
#define DNS_HDR_FLAG_Z_SHIFT 4

struct DNSQuestion
{
    uint16_t type = 0;
    uint16_t dns_class = 0;
};

struct DNSRR
{
    uint16_t type = 0;
    uint16_t dns_class = 0;
    uint32_t ttl = 0;
    uint16_t length = 0;
};

// FIXIT-L replace alerted/relative to bool?
struct DNSNameState
{
    uint32_t txt_count = 0;
    uint32_t total_txt_len = 0;
    uint8_t txt_len = 0;
    uint8_t txt_bytes_seen = 0;
    uint8_t name_state = 0;
    uint8_t alerted = 0;
    uint16_t offset = 0;
    uint8_t relative = 0;
    std::string dns_name;

    void get_dns_name(std::string& name) const
    {
        if (dns_name.size())
            name = dns_name;
    }
};

#define DNS_RR_TYPE_A                       0x0001
#define DNS_RR_TYPE_NS                      0x0002
#define DNS_RR_TYPE_MD                      0x0003 // obsolete
#define DNS_RR_TYPE_MF                      0x0004 // obsolete
#define DNS_RR_TYPE_CNAME                   0x0005
#define DNS_RR_TYPE_SOA                     0x0006
#define DNS_RR_TYPE_MB                      0x0007 // experimental
#define DNS_RR_TYPE_MG                      0x0008 // experimental
#define DNS_RR_TYPE_MR                      0x0009 // experimental
#define DNS_RR_TYPE_NULL                    0x000a // experimental
#define DNS_RR_TYPE_WKS                     0x000b
#define DNS_RR_TYPE_PTR                     0x000c
#define DNS_RR_TYPE_HINFO                   0x000d
#define DNS_RR_TYPE_MINFO                   0x000e // experimental
#define DNS_RR_TYPE_MX                      0x000f
#define DNS_RR_TYPE_TXT                     0x0010
#define DNS_RR_TYPE_AAAA                    0x001c
#define DNS_RR_TYPE_LOC                     0x001d
#define DNS_RR_TYPE_SRV                     0x0021
#define DNS_RR_TYPE_OPT                     0x0029
#define DNS_RR_TYPE_RRSIG                   0x002e
#define DNS_RR_TYPE_NSEC                    0x002f
#define DNS_RR_TYPE_DS                      0x002b
#define DNS_RR_TYPE_SSHFP                   0x002c
#define DNS_RR_TYPE_DNSKEY                  0x0030
#define DNS_RR_TYPE_NSEC3                   0x0032
#define DNS_RR_TYPE_NSEC3PARAM              0x0033
#define DNS_RR_TYPE_SVCB                    0x0040
#define DNS_RR_TYPE_HTTPS                   0x0041
#define DNS_RR_TYPE_SPF                     0x0063
#define DNS_RR_TYPE_TKEY                    0x00f9
#define DNS_RR_TYPE_TSIG                    0x00fa
#define DNS_RR_TYPE_CAA                     0x0101
#define DNS_RR_TYPE_BIND9_SIGNING           0xfffe

#define DNS_RR_PTR 0xC0

#define DNS_FLAG_NOT_DNS                0x01

// DNSSessionData States
#define DNS_RESP_STATE_LENGTH           0x00 // 2 bytes - TCP only
#define DNS_RESP_STATE_LENGTH_PART      0x01 // Partial length

#define DNS_RESP_STATE_HDR              0x10 // 12 bytes
#define DNS_RESP_STATE_HDR_ID           0x11 //  (2 bytes)
#define DNS_RESP_STATE_HDR_ID_PART      0x12 //  (2 bytes)
#define DNS_RESP_STATE_HDR_FLAGS        0x13 //  (2 bytes)
#define DNS_RESP_STATE_HDR_FLAGS_PART   0x14 //  (2 bytes)
#define DNS_RESP_STATE_HDR_QS           0x15 //  (2 bytes)
#define DNS_RESP_STATE_HDR_QS_PART      0x16 //  (2 bytes)
#define DNS_RESP_STATE_HDR_ANSS         0x17 //  (2 bytes)
#define DNS_RESP_STATE_HDR_ANSS_PART    0x18 //  (2 bytes)
#define DNS_RESP_STATE_HDR_AUTHS        0x19 //  (2 bytes)
#define DNS_RESP_STATE_HDR_AUTHS_PART   0x1a //  (2 bytes)
#define DNS_RESP_STATE_HDR_ADDS         0x1b //  (2 bytes)
#define DNS_RESP_STATE_HDR_ADDS_PART    0x1c //  (2 bytes)

#define DNS_RESP_STATE_QUESTION         0x20 // 4 bytes
#define DNS_RESP_STATE_Q_NAME           0x21 // (size depends on data)
#define DNS_RESP_STATE_Q_NAME_COMPLETE  0x22 // (size depends on data)
#define DNS_RESP_STATE_Q_TYPE           0x23 //  (2 bytes)
#define DNS_RESP_STATE_Q_TYPE_PART      0x24 //  (2 bytes)
#define DNS_RESP_STATE_Q_CLASS          0x25 //  (2 bytes)
#define DNS_RESP_STATE_Q_CLASS_PART     0x26 //  (2 bytes)
#define DNS_RESP_STATE_Q_COMPLETE       0x27

#define DNS_RESP_STATE_NAME_SIZE        0x31 // (1 byte)
#define DNS_RESP_STATE_NAME             0x32 // (size depends on field)
#define DNS_RESP_STATE_NAME_COMPLETE    0x33

#define DNS_RESP_STATE_ANS_RR           0x40 // (size depends on field)
#define DNS_RESP_STATE_RR_NAME_SIZE     0x41 // (1 byte)
#define DNS_RESP_STATE_RR_NAME          0x42 // (size depends on field)
#define DNS_RESP_STATE_RR_NAME_COMPLETE 0x43
#define DNS_RESP_STATE_RR_TYPE          0x44 //  (2 bytes)
#define DNS_RESP_STATE_RR_TYPE_PART     0x45 //  (2 bytes)
#define DNS_RESP_STATE_RR_CLASS         0x46 //  (2 bytes)
#define DNS_RESP_STATE_RR_CLASS_PART    0x47 //  (2 bytes)
#define DNS_RESP_STATE_RR_TTL           0x48 //  (4 bytes)
#define DNS_RESP_STATE_RR_TTL_PART      0x49 //  (4 bytes)
#define DNS_RESP_STATE_RR_RDLENGTH      0x4a //  (2 bytes)
#define DNS_RESP_STATE_RR_RDLENGTH_PART 0x4b //  (2 bytes)
#define DNS_RESP_STATE_RR_RDATA_START   0x4c // (size depends on RDLENGTH)
#define DNS_RESP_STATE_RR_RDATA_MID     0x4d // (size depends on RDLENGTH)
#define DNS_RESP_STATE_RR_COMPLETE      0x4e

#define DNS_RESP_STATE_AUTH_RR          0x50
#define DNS_RESP_STATE_ADD_RR           0x60


class DnsConfig;
struct DNSData;

DNSData* get_dns_session_data(snort::Packet* p, bool from_server, DNSData& udpSessionData, bool udp);

class DnsResponseFqdn
{
public:
    DnsResponseFqdn()
    {}

    DnsResponseFqdn(const unsigned char* data, uint16_t bytes_unused, DNSData* dnsSessionData);

    FqdnTtl get_fqdn();
    void update_ttl(uint32_t ttl);

private:
    const unsigned char* data = nullptr;
    uint16_t bytes_unused = 0;
    std::shared_ptr<DNSData> dnsSessionData;
};

// Per-session data block containing current state
// of the DNS inspector for the session.
struct DNSData
{
    uint32_t state = 0;               // The current state of the session.
    uint16_t curr_rec = 0;            // Record number for the current record
    uint16_t curr_rec_length = 0;
    uint16_t bytes_seen_curr_rec = 0;
    uint16_t length = 0;
    uint16_t bytes_unused = 0;
    uint8_t curr_rec_state = 0;
    DNSHdr hdr;                   // Copy of the data from the DNS Header
    DNSQuestion curr_q;
    DNSRR curr_rr;
    DNSNameState curr_txt;
    uint8_t flags = 0;
    std::vector<unsigned char> data;
    const DnsConfig* dns_config = nullptr;
    snort::DnsResponseDataEvents dns_events;
    DnsResponseFqdn cur_fqdn_event;
    std::string resp_query;
    std::vector<uint16_t> answer_tabs;
    std::vector<uint16_t> auth_tabs;
    std::vector<uint16_t> addl_tabs;

    bool publish_response() const;
    bool has_events() const;
    bool valid_dns(const DNSHdr&) const;

    void decode_rdata(const snort::Packet* p, const uint8_t* rr, const uint8_t* rdata, uint16_t rdlength,
        uint16_t type, std::string& rdata_str) const;
    void get_rr_data(const snort::Packet *p, const std::vector<uint16_t>& tabs,
        std::string& rrs, std::string* ttls = nullptr) const;
    void get_answers(const snort::Packet *p, std::string& answers, std::string& ttls) const
    { get_rr_data(p, answer_tabs, answers, &ttls); }
    void get_auth(const snort::Packet *p, std::string& auth) const { get_rr_data(p, auth_tabs, auth); }
    void get_addl(const snort::Packet *p, std::string& addl) const { get_rr_data(p, addl_tabs, addl); }

    static const std::string& qtype_name(uint16_t query_type, bool* is_unknown = nullptr);
};

class DnsResponseIp
{
public:
    DnsResponseIp()
    {}

    DnsResponseIp(const unsigned char* data, uint16_t type) :
        data(data), type(type)
    {}

    snort::SfIp get_ip();

private:
    const unsigned char* data = nullptr;
    uint16_t type = 0;
};

// Flow data class for DNS over TCP
class DnsFlowData : public snort::FlowData
{
public:
    DnsFlowData();
    ~DnsFlowData() override;

    static void init()
    { inspector_id = snort::FlowData::create_flow_data_id(); }

public:
    static unsigned inspector_id;
    DNSData session;
};

// Flow data class for DNS over UDP
class DnsUdpFlowData : public snort::FlowData
{
public:
    DnsUdpFlowData();

    static void init()
    { inspector_id = snort::FlowData::create_flow_data_id(); }

public:
    static unsigned inspector_id;
    std::set<uint16_t> trans_ids;
};

class Dns : public snort::Inspector
{
public:
    Dns(DnsModule*);
    ~Dns() override;

    void eval(snort::Packet*) override;
    snort::StreamSplitter* get_splitter(bool) override;
    bool configure(snort::SnortConfig*) override;
    void show(const snort::SnortConfig*) const override;
    static unsigned get_pub_id()
    { return pub_id; }

    bool supports_no_ips() const override
    { return true; }

    void snort_dns(snort::Packet* p, bool udp, bool is_payload = false);

    const DnsConfig* get_config() const
    { return config; }

private:
    const DnsConfig* config = nullptr;
    static unsigned pub_id;
};

#endif

