#pragma once #include #include #include #include #ifdef WIN32 #include #include #include #else #include #endif #include "./types.h" struct in6_addr; namespace tc::dns { namespace response { class DNSHeader; class DNSQuery; class DNSResourceRecords; } class DNSResponse; struct DNSResponseData; struct DNSResponseData { #ifdef WIN32 bool wide_string{false}; DNS_QUERY_RESULT data{}; #else uint8_t* buffer{nullptr}; size_t length{0}; std::string parse_dns_dn(std::string& /* error */, size_t& /* index */, bool /* compression allowed */); #endif ~DNSResponseData(); }; class DNSResponse { friend class Resolver; public: typedef std::vector> rr_list_t; typedef std::vector> q_list_t; DNSResponse(const DNSResponse&) = delete; DNSResponse(DNSResponse&&) = delete; bool parse(std::string& /* error */); #ifndef WIN32 [[nodiscard]] inline const std::string why_bogus() const { return this->bogus; } [[nodiscard]] inline const uint8_t* packet_data() const { return this->data->buffer; } [[nodiscard]] inline size_t packet_length() const { return this->data->length; } [[nodiscard]] inline bool is_secure() const { return this->secure_state > 0; } [[nodiscard]] inline bool is_secure_dnssec() const { return this->secure_state == 2; } [[nodiscard]] response::DNSHeader header() const; #endif [[nodiscard]] q_list_t queries() const { return this->parsed_queries; } [[nodiscard]] rr_list_t answers() const { return this->parsed_answers; } [[nodiscard]] rr_list_t authorities() const { return this->parsed_authorities; } [[nodiscard]] rr_list_t additionals() const { return this->parsed_additionals; } private: #ifndef WIN32 DNSResponse(uint8_t /* secure state */, const char* /* bogus */, void* /* packet */, size_t /* length */); std::shared_ptr parse_rr(std::string& /* error */, size_t& index, bool /* compression allowed dn */); std::string bogus; uint8_t secure_state{0}; #else DNSResponse(std::shared_ptr); #endif std::shared_ptr data{nullptr}; bool is_parsed{false}; std::string parse_error{}; q_list_t parsed_queries; rr_list_t parsed_answers; rr_list_t parsed_authorities; rr_list_t parsed_additionals; }; namespace response { #ifndef WIN32 class DNSHeader { friend class tc::dns::DNSResponse; public: [[nodiscard]] inline size_t id() const { return this->field(0); } [[nodiscard]] inline bool is_answer() const { return this->field(1) & 0x1UL; } [[nodiscard]] inline bool is_authoritative_answer() const { return (uint8_t) ((this->field(1) >> 5UL) & 0x01UL); } [[nodiscard]] inline bool is_truncation() const { return (uint8_t) ((this->field(1) >> 6UL) & 0x01UL); } [[nodiscard]] inline bool is_recursion_desired() const { return (uint8_t) ((this->field(1) >> 7UL) & 0x01UL); } [[nodiscard]] inline bool is_recursion_available() const { return (uint8_t) ((this->field(1) >> 8UL) & 0x01UL); } [[nodiscard]] inline uint8_t query_type() const { return (uint8_t) ((this->field(1) >> 1UL) & 0x07UL); } [[nodiscard]] inline uint8_t response_code() const { return (uint8_t) ((this->field(1) >> 12UL) & 0x07UL); } [[nodiscard]] inline uint16_t query_count() const { return ntohs(this->field(2)); } [[nodiscard]] inline uint16_t answer_count() const { return ntohs(this->field(3)); } [[nodiscard]] inline uint16_t authority_count() const { return ntohs(this->field(4)); } [[nodiscard]] inline uint16_t additional_count() const { return htons(this->field(5)); } private: [[nodiscard]] uint16_t field(int index) const; explicit DNSHeader(const DNSResponse* response) : response{response} {} const DNSResponse* response{nullptr}; }; #endif class DNSQuery { friend class tc::dns::DNSResponse; public: [[nodiscard]] inline std::string qname() const { return this->name; } [[nodiscard]] inline rrtype::value qtype() const { return this->type; } [[nodiscard]] inline rrclass::value qclass() const { return this->klass; } private: DNSQuery(std::string name, rrtype::value type, rrclass::value klass) : name{std::move(name)}, type{type}, klass{klass} {} std::string name; rrtype::value type; rrclass::value klass; }; class DNSResourceRecords { friend class tc::dns::DNSResponse; public: [[nodiscard]] inline std::string qname() const { #ifdef WIN32 return std::string{this->nrecord->pName}; #else return this->name; #endif } [[nodiscard]] inline rrtype::value atype() const { #ifdef WIN32 return static_cast(this->nrecord->wType); #else return this->type; #endif } [[nodiscard]] inline rrclass::value aclass() const { #ifdef WIN32 return static_cast(1); #else return this->klass; #endif } [[nodiscard]] inline uint16_t attl() const { #ifdef WIN32 return (uint16_t) this->nrecord->dwTtl; #else return this->ttl; #endif } #ifndef WIN32 [[nodiscard]] const uint8_t* payload_data() const; [[nodiscard]] inline size_t payload_length() const { return this->length; } [[nodiscard]] inline size_t payload_offset() const { return this->offset; } #else [[nodiscard]] inline PDNS_RECORDA native_record() const { return this->nrecord; } [[nodiscard]] bool is_wide_string() const; #endif [[nodiscard]] inline std::shared_ptr dns_data() const { return this->data; } template [[nodiscard]] inline T parse() const { if(T::type != this->atype()) throw std::logic_error{"parser type mismatch"}; return T{this}; } private: std::shared_ptr data{nullptr}; #ifdef WIN32 DNSResourceRecords(std::shared_ptr, PDNS_RECORDA); PDNS_RECORDA nrecord{nullptr}; #else DNSResourceRecords(std::shared_ptr, size_t, size_t, uint32_t, std::string , rrtype::value, rrclass::value); size_t offset{0}; size_t length{0}; uint32_t ttl; std::string name; rrtype::value type; rrclass::value klass; #endif }; namespace rrparser { struct base { protected: explicit base(const DNSResourceRecords* handle) : handle{handle} {} const DNSResourceRecords* handle{nullptr}; }; struct named_base : public base { public: [[nodiscard]] bool is_valid(); [[nodiscard]] std::string name(); protected: explicit named_base(const DNSResourceRecords* handle) : base{handle} {} }; #define define_parser(name, base, ...) \ struct name : public base { \ friend class response::DNSResourceRecords; \ public: \ static constexpr auto type = rrtype::name; \ __VA_ARGS__ \ private: \ explicit name(const DNSResourceRecords* handle) : base{handle} {} \ } define_parser(A, base, [[nodiscard]] bool is_valid(); [[nodiscard]] std::string address_string(); [[nodiscard]] in_addr address(); ); define_parser(AAAA, base, [[nodiscard]] bool is_valid(); [[nodiscard]] std::string address_string(); [[nodiscard]] in6_addr address(); ); define_parser(SRV, base, [[nodiscard]] bool is_valid(); [[nodiscard]] uint16_t priority(); [[nodiscard]] uint16_t weight(); [[nodiscard]] uint16_t target_port(); [[nodiscard]] std::string target_hostname(); ); define_parser(CNAME, named_base); }; } }