#pragma once #include #include #include #include #include #include #include "./types.h" #ifdef WIN32 #include #else struct ub_ctx; #endif namespace tc::dns { namespace response { class DNSHeader; class DNSQuery; class DNSResourceRecords; } 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 Resolver; 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; }; class Resolver { public: struct ResultState { enum value : uint8_t { SUCCESS = 0, INITIALISATION_FAILED = 0x01, DNS_TIMEOUT = 0x10, DNS_FAIL = 0x11, /* error detail is a DNS error code */ DNS_API_FAIL = 0x12, TSDNS_CONNECTION_FAIL = 0x20, TSDNS_EMPTY_RESPONSE = 0x21, ABORT = 0xFF /* request has been aborted */ }; }; typedef std::function /* response */)> dns_callback_t; typedef std::function tsdns_callback_t; Resolver(); virtual ~Resolver(); bool initialize(std::string& /* error */, bool /* use hosts */, bool /* use resolv */); void finalize(); void resolve_dns(const char* /* name */, const rrtype::value& /* rrtype */, const rrclass::value& /* rrclass */, const std::chrono::microseconds& /* timeout */, const dns_callback_t& /* callback */); void resolve_tsdns(const char* /* name */, const sockaddr_storage& /* server */, const std::chrono::microseconds& /* timeout */, const tsdns_callback_t& /* callback */); private: #ifdef WIN32 struct dns_request; struct dns_old_request_data { /* request might me nullptr if its beeing timeouted */ struct dns_request* request{nullptr}; /* protected by lock */ std::mutex* lock{nullptr}; }; struct { HMODULE libhandle{nullptr}; DNS_STATUS (*DnsQueryEx)( _In_ PDNS_QUERY_REQUEST pQueryRequest, _Inout_ PDNS_QUERY_RESULT pQueryResults, _Inout_opt_ PDNS_QUERY_CANCEL pCancelHandle ) = nullptr; DNS_STATUS (*DnsCancelQuery)( _In_ PDNS_QUERY_CANCEL pCancelHandle ) = nullptr; } dnsapi; #endif struct dns_request { Resolver* resolver{nullptr}; std::string host; dns::rrtype::value rrtype{dns::rrtype::Unassigned}; dns::rrclass::value rrclass{dns::rrclass::IN}; dns_callback_t callback{}; #ifdef WIN32 std::wstring whost; /* windows 8 or newer */ DNS_QUERY_REQUEST dns_query; DNS_QUERY_RESULT dns_result; DNS_QUERY_CANCEL dns_cancel; /* for old stuff */ //std::thread resolve_thread; std::mutex* threaded_lock{nullptr}; struct dns_old_request_data* thread_data{nullptr}; /* protected by threaded_lock */ #else int ub_id{0}; struct ::event* register_event{nullptr}; #endif struct ::event* timeout_event{nullptr}; struct ::event* processed_event{nullptr}; }; #ifndef WIN32 struct ub_ctx* ub_ctx = nullptr; #endif struct tsdns_request { Resolver* resolver{nullptr}; int socket{0}; struct ::event* timeout_event{nullptr}; struct ::event* event_read{nullptr}; struct ::event* event_write{nullptr}; std::mutex buffer_lock{}; std::string write_buffer{}; std::string read_buffer{}; tsdns_callback_t callback{}; }; struct { bool loop_active{false}; std::condition_variable condition{}; std::mutex lock{}; std::thread loop{}; event_base* base = nullptr; } event; std::vector tsdns_requests{}; std::vector dns_requests{}; std::recursive_mutex request_lock{}; /* this is recursive because due to the instance callback resolve_dns could be called recursively */ bool initialize_platform(std::string& /* error */, bool /* use hosts */, bool /* use resolv */); void finalize_platform(); void destroy_dns_request(dns_request*); void destroy_tsdns_request(tsdns_request*); void event_loop_runner(); void evtimer_dns_callback(dns_request* /* request */); #ifndef WIN32 void ub_callback(dns_request* /* request */, int /* rcode */, void* /* packet */, int /* packet_len */, int /* sec */, char* /* why_bogus */); #else void dns_callback(dns_request* /* request */); #endif void evtimer_tsdns_callback(tsdns_request* /* request */); void event_tsdns_write(tsdns_request* /* request */); void event_tsdns_read(tsdns_request* /* request */); }; }