#pragma once #include #include #include #include #include #include #include #include #include #include "nenum.hh" #include "comboaddress.hh" // enums enum class RCode { Noerror = 0, Servfail = 2, Nxdomain = 3, Notimp = 4, Refused = 5, Badvers=16 }; // this makes enums printable, which is nice SMARTENUMSTART(RCode) SENUM6(RCode, Noerror, Servfail, Nxdomain, Notimp, Refused, Badvers) SMARTENUMEND(RCode) enum class DNSType : uint16_t { A = 1, NS = 2, CNAME = 5, SOA=6, PTR=12, MX=15, TXT=16, AAAA = 28, SRV=33, DS=43, RRSIG=46, NSEC=47, OPT=41, IXFR = 251, AXFR = 252, ANY = 255 }; SMARTENUMSTART(DNSType) SENUM13(DNSType, A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, SRV,DS, RRSIG, NSEC, OPT) SENUM3(DNSType, IXFR, AXFR, ANY) SMARTENUMEND(DNSType) enum class DNSClass : uint16_t { IN=1, CHAOS=3 }; SMARTENUMSTART(DNSClass) SENUM2(DNSClass, IN, CHAOS) SMARTENUMEND(DNSClass) COMBOENUM4(DNSSection, Question, 0, Answer, 1, Authority, 2, Additional, 3) //! Represents a DNS label, which is part of a DNS Name class DNSLabel { public: DNSLabel() {} DNSLabel(const char* s) : DNSLabel(std::string(s)) {} DNSLabel(const std::string& s) : d_s(s) { if(d_s.size() > 63) throw std::out_of_range("label too long"); } //! Equality and comparison are case insensitive bool operator<(const DNSLabel& rhs) const { return std::lexicographical_compare(d_s.begin(), d_s.end(), rhs.d_s.begin(), rhs.d_s.end(), charcomp); } bool operator==(const DNSLabel &rhs) const { return !(*this < rhs) && !(rhs<*this); } auto size() const { return d_s.size(); } std::string d_s; private: static bool charcomp(char a, char b) { if(a >= 0x61 && a <= 0x7A) a -= 0x20; if(b >= 0x61 && b <= 0x7A) b -= 0x20; return a < b; } }; std::ostream & operator<<(std::ostream &os, const DNSLabel& d); //! A DNS Name with helpful methods. Inherits case insensitivity from DNSLabel struct DNSName { DNSName() {} DNSName(std::initializer_list dls) : d_name(dls) {} void push_back(const DNSLabel& l) { d_name.push_back(l); } auto back() const { return d_name.back(); } auto begin() const { return d_name.begin(); } bool empty() const { return d_name.empty(); } auto end() const { return d_name.end(); } auto front() const { return d_name.front(); } void pop_back() { d_name.pop_back(); } void pop_front() { d_name.pop_front(); } auto push_front(const DNSLabel& dn) { return d_name.push_front(dn); } auto size() { return d_name.size(); } void clear() { d_name.clear(); } bool makeRelative(const DNSName& root); std::string toString() const; bool operator==(const DNSName& rhs) const { return std::lexicographical_compare(begin(), end(), rhs.begin(), rhs.end())==0; } std::deque d_name; }; // printing, concatenation std::ostream & operator<<(std::ostream &os, const DNSName& d); DNSName operator+(const DNSName& a, const DNSName& b); class DNSMessageWriter; /* this is the how all resource records are stored, as generators that can convert their content to a human readable string or to a DNSMessage */ struct RRGen { virtual void toMessage(DNSMessageWriter& dpw) = 0; virtual std::string toString() const = 0; virtual DNSType getType() const = 0; }; /* Resource records are treated as a set and have one TTL for the whole set */ struct RRSet { std::vector> contents; void add(std::unique_ptr&& rr) { contents.emplace_back(std::move(rr)); } uint32_t ttl{3600}; }; /* A node in the DNS tree */ struct DNSNode { ~DNSNode(); //! This is the key function that finds names, returns where it found them and if any zonecuts were passsed const DNSNode* find(DNSName& name, DNSName& last, bool wildcards=false, const DNSNode** passedZonecut=0, DNSName* zonecutname=0) const; //! This is an idempotent way to add a node to a DNS tree DNSNode* add(DNSName name); // add one or more generators to this node void addRRs(std::unique_ptr&&a); template void addRRs(std::unique_ptr&&a, Types&&... args) { addRRs(std::move(a)); addRRs(std::forward(args)...); } void visit(std::function visitor, DNSName name) const; // children, found by DNSLabel std::map children; // the RRSets, grouped by type std::map rrsets; std::unique_ptr zone; // if this is set, this node is a zone uint16_t namepos{0}; //!< for label compression, we also use DNSNodes }; //! Called by main() to load zone information void loadZones(DNSNode& zones); std::unique_ptr retrieveZone(const ComboAddress& remote, const DNSName& zone);