2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <unordered_set>
33 #include <boost/version.hpp>
35 // it crashes on OSX and doesn't compile on OpenBSD
36 #if BOOST_VERSION >= 105300 && ! defined( __APPLE__ ) && ! defined(__OpenBSD__)
37 #include <boost/container/string.hpp>
42 uint32_t burtleCI(const unsigned char* k, uint32_t length, uint32_t init);
45 // #include "logger.hh"
47 //#include <ext/vstring.h>
50 accept escaped ascii presentations of DNS names and store them "natively"
51 accept a DNS packet with an offset, and extract a DNS name from it
52 build up DNSNames with prepend and append of 'raw' unescaped labels
54 Be able to turn them into ASCII and "DNS name in a packet" again on request
56 Provide some common operators for comparison, detection of being part of another domain
58 NOTE: For now, everything MUST be . terminated, otherwise it is an error
64 DNSName() {} //!< Constructs an *empty* DNSName, NOT the root!
65 explicit DNSName(const char* p): DNSName(p, strlen(p)) {} //!< Constructs from a human formatted, escaped presentation
66 explicit DNSName(const char* p, size_t len); //!< Constructs from a human formatted, escaped presentation
67 explicit DNSName(const std::string& str) : DNSName(str.c_str(), str.length()) {}; //!< Constructs from a human formatted, escaped presentation
68 DNSName(const char* p, int len, int offset, bool uncompress, uint16_t* qtype=nullptr, uint16_t* qclass=nullptr, unsigned int* consumed=nullptr, uint16_t minOffset=0); //!< Construct from a DNS Packet, taking the first question if offset=12. If supplied, consumed is set to the number of bytes consumed from the packet, which will not be equal to the wire length of the resulting name in case of compression.
70 bool isPartOf(const DNSName& rhs) const; //!< Are we part of the rhs name?
71 inline bool operator==(const DNSName& rhs) const; //!< DNS-native comparison (case insensitive) - empty compares to empty
72 bool operator!=(const DNSName& other) const { return !(*this == other); }
74 std::string toString(const std::string& separator=".", const bool trailing=true) const; //!< Our human-friendly, escaped, representation
75 std::string toLogString() const; //!< like plain toString, but returns (empty) on empty names
76 std::string toStringNoDot() const { return toString(".", false); }
77 std::string toStringRootDot() const { if(isRoot()) return "."; else return toString(".", false); }
78 std::string toDNSString() const; //!< Our representation in DNS native format
79 std::string toDNSStringLC() const; //!< Our representation in DNS native format, lower cased
80 void appendRawLabel(const std::string& str); //!< Append this unescaped label
81 void appendRawLabel(const char* start, unsigned int length); //!< Append this unescaped label
82 void prependRawLabel(const std::string& str); //!< Prepend this unescaped label
83 std::vector<std::string> getRawLabels() const; //!< Individual raw unescaped labels
84 std::string getRawLabel(unsigned int pos) const; //!< Get the specified raw unescaped label
85 DNSName getLastLabel() const; //!< Get the DNSName of the last label
86 bool chopOff(); //!< Turn www.powerdns.com. into powerdns.com., returns false for .
87 DNSName makeRelative(const DNSName& zone) const;
88 DNSName makeLowerCase() const
91 ret.makeUsLowerCase();
94 void makeUsLowerCase()
96 for(auto & c : d_storage) {
100 void makeUsRelative(const DNSName& zone);
101 DNSName getCommonLabels(const DNSName& other) const; //!< Return the list of common labels from the top, for example 'c.d' for 'a.b.c.d' and 'x.y.c.d'
102 DNSName labelReverse() const;
103 bool isWildcard() const;
104 bool isHostname() const;
105 unsigned int countLabels() const;
106 size_t wirelength() const; //!< Number of total bytes in the name
107 bool empty() const { return d_storage.empty(); }
108 bool isRoot() const { return d_storage.size()==1 && d_storage[0]==0; }
109 void clear() { d_storage.clear(); }
110 void trimToLabels(unsigned int);
111 size_t hash(size_t init=0) const
113 return burtleCI((const unsigned char*)d_storage.c_str(), d_storage.size(), init);
115 DNSName& operator+=(const DNSName& rhs)
117 if(d_storage.size() + rhs.d_storage.size() > 256) // one extra byte for the second root label
118 throw std::range_error("name too long");
122 if(d_storage.empty())
123 d_storage+=rhs.d_storage;
125 d_storage.replace(d_storage.length()-1, rhs.d_storage.length(), rhs.d_storage);
130 bool operator<(const DNSName& rhs) const // this delivers _some_ kind of ordering, but not one useful in a DNS context. Really fast though.
132 return std::lexicographical_compare(d_storage.rbegin(), d_storage.rend(),
133 rhs.d_storage.rbegin(), rhs.d_storage.rend(),
134 [](const unsigned char& a, const unsigned char& b) {
135 return dns_tolower(a) < dns_tolower(b);
136 }); // note that this is case insensitive, including on the label lengths
139 inline bool canonCompare(const DNSName& rhs) const;
140 bool slowCanonCompare(const DNSName& rhs) const;
142 #if BOOST_VERSION >= 105300 && ! defined( __APPLE__ ) && ! defined(__OpenBSD__)
143 typedef boost::container::string string_t;
145 typedef std::string string_t;
147 const string_t& getStorage() const {
153 void packetParser(const char* p, int len, int offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset);
154 static void appendEscapedLabel(std::string& appendTo, const char* orig, size_t len);
155 static std::string unescapeLabel(const std::string& orig);
158 size_t hash_value(DNSName const& d);
161 inline bool DNSName::canonCompare(const DNSName& rhs) const
164 // us: 1a3www4ds9a2nl
165 // rhs: 3www6online3com
166 // to compare, we start at the back, is nl < com? no -> done
171 uint8_t ourpos[64], rhspos[64];
172 uint8_t ourcount=0, rhscount=0;
173 //cout<<"Asked to compare "<<toString()<<" to "<<rhs.toString()<<endl;
174 for(const unsigned char* p = (const unsigned char*)d_storage.c_str(); p < (const unsigned char*)d_storage.c_str() + d_storage.size() && *p && ourcount < sizeof(ourpos); p+=*p+1)
175 ourpos[ourcount++]=(p-(const unsigned char*)d_storage.c_str());
176 for(const unsigned char* p = (const unsigned char*)rhs.d_storage.c_str(); p < (const unsigned char*)rhs.d_storage.c_str() + rhs.d_storage.size() && *p && rhscount < sizeof(rhspos); p+=*p+1)
177 rhspos[rhscount++]=(p-(const unsigned char*)rhs.d_storage.c_str());
179 if(ourcount == sizeof(ourpos) || rhscount==sizeof(rhspos)) {
180 return slowCanonCompare(rhs);
184 if(ourcount == 0 && rhscount != 0)
191 bool res=std::lexicographical_compare(
192 d_storage.c_str() + ourpos[ourcount] + 1,
193 d_storage.c_str() + ourpos[ourcount] + 1 + *(d_storage.c_str() + ourpos[ourcount]),
194 rhs.d_storage.c_str() + rhspos[rhscount] + 1,
195 rhs.d_storage.c_str() + rhspos[rhscount] + 1 + *(rhs.d_storage.c_str() + rhspos[rhscount]),
196 [](const unsigned char& a, const unsigned char& b) {
197 return dns_tolower(a) < dns_tolower(b);
200 // cout<<"Forward: "<<res<<endl;
204 res=std::lexicographical_compare( rhs.d_storage.c_str() + rhspos[rhscount] + 1,
205 rhs.d_storage.c_str() + rhspos[rhscount] + 1 + *(rhs.d_storage.c_str() + rhspos[rhscount]),
206 d_storage.c_str() + ourpos[ourcount] + 1,
207 d_storage.c_str() + ourpos[ourcount] + 1 + *(d_storage.c_str() + ourpos[ourcount]),
208 [](const unsigned char& a, const unsigned char& b) {
209 return dns_tolower(a) < dns_tolower(b);
211 // cout<<"Reverse: "<<res<<endl;
219 struct CanonDNSNameCompare: public std::binary_function<DNSName, DNSName, bool>
221 bool operator()(const DNSName&a, const DNSName& b) const
223 return a.canonCompare(b);
227 inline DNSName operator+(const DNSName& lhs, const DNSName& rhs)
235 struct SuffixMatchTree
237 SuffixMatchTree(const std::string& name="", bool endNode_=false) : d_name(name), endNode(endNode_)
240 SuffixMatchTree(const SuffixMatchTree& rhs): d_name(rhs.d_name), children(rhs.children), endNode(rhs.endNode)
243 d_value = rhs.d_value;
246 SuffixMatchTree & operator=(const SuffixMatchTree &rhs)
249 children = rhs.children;
250 endNode = rhs.endNode;
252 d_value = rhs.d_value;
258 mutable std::set<SuffixMatchTree> children;
259 mutable bool endNode;
261 bool operator<(const SuffixMatchTree& rhs) const
263 return strcasecmp(d_name.c_str(), rhs.d_name.c_str()) < 0;
265 typedef SuffixMatchTree value_type;
268 void visit(const V& v) const {
269 for(const auto& c : children)
275 void add(const DNSName& name, const T& t)
277 add(name.getRawLabels(), t);
280 void add(std::vector<std::string> labels, const T& value) const
282 if(labels.empty()) { // this allows insertion of the root
286 else if(labels.size()==1) {
287 auto res=children.emplace(*labels.begin(), true);
289 // we might already have had the node as an
290 // intermediary one, but it's now an end node
291 if(!res.first->endNode) {
292 res.first->endNode = true;
295 res.first->d_value = value;
298 auto res=children.emplace(*labels.rbegin(), false);
300 res.first->add(labels, value);
304 void remove(const DNSName &name) const
306 remove(name.getRawLabels());
309 /* Removes the node at `labels`, also make sure that no empty
310 * children will be left behind in memory
312 void remove(std::vector<std::string> labels) const
314 if (labels.empty()) { // this allows removal of the root
319 SuffixMatchTree smt(*labels.rbegin());
320 auto child = children.find(smt);
321 if (child == children.end()) {
322 // No subnode found, we're done
326 // We have found a child
328 if (labels.empty()) {
329 // The child is no longer an endnode
330 child->endNode = false;
332 // If the child has no further children, just remove it from the set.
333 if (child->children.empty()) {
334 children.erase(child);
339 // We are not at the end, let the child figure out what to do
340 child->remove(labels);
343 T* lookup(const DNSName& name) const
345 if(children.empty()) { // speed up empty set
351 return lookup(name.getRawLabels());
354 T* lookup(std::vector<std::string> labels) const
356 if(labels.empty()) { // optimization
362 SuffixMatchTree smn(*labels.rbegin());
363 auto child = children.find(smn);
364 if(child == children.end()) {
370 auto result = child->lookup(labels);
374 return endNode ? &d_value : nullptr;
377 // Returns all end-nodes, fully qualified (not as separate labels)
378 std::vector<DNSName> getNodes() const {
379 std::vector<DNSName> ret;
381 ret.push_back(DNSName(d_name));
383 for (const auto& child : children) {
384 auto nodes = child.getNodes();
385 for (const auto &node: nodes) {
386 ret.push_back(node + DNSName(d_name));
393 /* Quest in life: serve as a rapid block list. If you add a DNSName to a root SuffixMatchNode,
394 anything part of that domain will return 'true' in check */
395 struct SuffixMatchNode
400 SuffixMatchTree<bool> d_tree;
402 void add(const DNSName& dnsname)
404 d_tree.add(dnsname, true);
405 d_nodes.insert(dnsname);
408 void add(const std::string& name)
413 void add(std::vector<std::string> labels)
415 d_tree.add(labels, true);
417 while (!labels.empty()) {
418 tmp.appendRawLabel(labels.back());
419 labels.pop_back(); // This is safe because we have a copy of labels
424 void remove(const DNSName& name)
430 void remove(std::vector<std::string> labels)
432 d_tree.remove(labels);
434 while (!labels.empty()) {
435 tmp.appendRawLabel(labels.back());
436 labels.pop_back(); // This is safe because we have a copy of labels
441 bool check(const DNSName& dnsname) const
443 return d_tree.lookup(dnsname) != nullptr;
446 std::string toString() const
450 for (const auto& n : d_nodes) {
461 mutable std::set<DNSName> d_nodes; // Only used for string generation
464 std::ostream & operator<<(std::ostream &os, const DNSName& d);
467 struct hash<DNSName> {
468 size_t operator () (const DNSName& dn) const { return dn.hash(0); }
472 DNSName::string_t segmentDNSNameRaw(const char* input, size_t inputlen); // from ragel
473 bool DNSName::operator==(const DNSName& rhs) const
475 if(rhs.empty() != empty() || rhs.d_storage.size() != d_storage.size())
478 auto us = d_storage.cbegin();
479 auto p = rhs.d_storage.cbegin();
480 for(; us != d_storage.cend() && p != rhs.d_storage.cend(); ++us, ++p) {
481 if(dns_tolower(*p) != dns_tolower(*us))
487 extern const DNSName g_rootdnsname, g_wildcarddnsname;
489 struct DNSNameSet: public std::unordered_set<DNSName> {
490 std::string toString() const {
491 std::ostringstream oss;
492 std::copy(begin(), end(), std::ostream_iterator<DNSName>(oss, "\n"));