254 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			254 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								#pragma once
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <memory>
							 | 
						||
| 
								 | 
							
								#include <string>
							 | 
						||
| 
								 | 
							
								#include <cstdint>
							 | 
						||
| 
								 | 
							
								#include <vector>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef WIN32
							 | 
						||
| 
								 | 
							
								    #include <WinSock2.h>
							 | 
						||
| 
								 | 
							
								    #include <Windows.h>
							 | 
						||
| 
								 | 
							
								    #include <WinDNS.h>
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								    #include <netinet/in.h>
							 | 
						||
| 
								 | 
							
								#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<std::shared_ptr<response::DNSResourceRecords>> rr_list_t;
							 | 
						||
| 
								 | 
							
											typedef std::vector<std::shared_ptr<response::DNSQuery>> 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<response::DNSResourceRecords> 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<DNSResponseData>);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
											std::shared_ptr<DNSResponseData> 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<rrtype::value>(this->nrecord->wType);
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
													return this->type;
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												[[nodiscard]] inline rrclass::value aclass() const {
							 | 
						||
| 
								 | 
							
								#ifdef WIN32
							 | 
						||
| 
								 | 
							
												    return static_cast<rrclass::value>(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<DNSResponseData> dns_data() const {
							 | 
						||
| 
								 | 
							
													return this->data;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												template <typename T>
							 | 
						||
| 
								 | 
							
												[[nodiscard]] inline T parse() const {
							 | 
						||
| 
								 | 
							
													if(T::type != this->atype())
							 | 
						||
| 
								 | 
							
														throw std::logic_error{"parser type mismatch"};
							 | 
						||
| 
								 | 
							
													return T{this};
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											private:
							 | 
						||
| 
								 | 
							
												std::shared_ptr<DNSResponseData> data{nullptr};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											#ifdef WIN32
							 | 
						||
| 
								 | 
							
												DNSResourceRecords(std::shared_ptr<DNSResponseData>, PDNS_RECORDA);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												PDNS_RECORDA nrecord{nullptr};
							 | 
						||
| 
								 | 
							
											#else
							 | 
						||
| 
								 | 
							
												DNSResourceRecords(std::shared_ptr<DNSResponseData>, 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);
							 | 
						||
| 
								 | 
							
										};
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |