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); //!< Constructs from a human formatted, escaped presentation
66 explicit DNSName(const std::string& str) : DNSName(str.c_str()) {}; //!< Constructs from a human formatted, escaped presentation
67 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.
69 bool isPartOf(const DNSName& rhs) const; //!< Are we part of the rhs name?
70 inline bool operator==(const DNSName& rhs) const; //!< DNS-native comparison (case insensitive) - empty compares to empty
71 bool operator!=(const DNSName& other) const { return !(*this == other); }
73 std::string toString(const std::string& separator=".", const bool trailing=true) const; //!< Our human-friendly, escaped, representation
74 std::string toLogString() const; //!< like plain toString, but returns (empty) on empty names
75 std::string toStringNoDot() const { return toString(".", false); }
76 std::string toStringRootDot() const { if(isRoot()) return "."; else return toString(".", false); }
77 std::string toDNSString() const; //!< Our representation in DNS native format
78 std::string toDNSStringLC() const; //!< Our representation in DNS native format, lower cased
79 void appendRawLabel(const std::string& str); //!< Append this unescaped label
80 void appendRawLabel(const char* start, unsigned int length); //!< Append this unescaped label
81 void prependRawLabel(const std::string& str); //!< Prepend this unescaped label
82 std::vector<std::string> getRawLabels() const; //!< Individual raw unescaped labels
83 std::string getRawLabel(unsigned int pos) const; //!< Get the specified raw unescaped label
84 DNSName getLastLabel() const; //!< Get the DNSName of the last label
85 bool chopOff(); //!< Turn www.powerdns.com. into powerdns.com., returns false for .
86 DNSName makeRelative(const DNSName& zone) const;
87 DNSName makeLowerCase() const
90 ret.makeUsLowerCase();
93 void makeUsLowerCase()
95 for(auto & c : d_storage) {
99 void makeUsRelative(const DNSName& zone);
100 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'
101 DNSName labelReverse() const;
102 bool isWildcard() const;
103 bool isHostname() const;
104 unsigned int countLabels() const;
105 size_t wirelength() const; //!< Number of total bytes in the name
106 bool empty() const { return d_storage.empty(); }
107 bool isRoot() const { return d_storage.size()==1 && d_storage[0]==0; }
108 void clear() { d_storage.clear(); }
109 void trimToLabels(unsigned int);
110 size_t hash(size_t init=0) const
112 return burtleCI((const unsigned char*)d_storage.c_str(), d_storage.size(), init);
114 DNSName& operator+=(const DNSName& rhs)
116 if(d_storage.size() + rhs.d_storage.size() > 256) // one extra byte for the second root label
117 throw std::range_error("name too long");
121 if(d_storage.empty())
122 d_storage+=rhs.d_storage;
124 d_storage.replace(d_storage.length()-1, rhs.d_storage.length(), rhs.d_storage);
129 bool operator<(const DNSName& rhs) const // this delivers _some_ kind of ordering, but not one useful in a DNS context. Really fast though.
131 return std::lexicographical_compare(d_storage.rbegin(), d_storage.rend(),
132 rhs.d_storage.rbegin(), rhs.d_storage.rend(),
133 [](const unsigned char& a, const unsigned char& b) {
134 return dns_tolower(a) < dns_tolower(b);
135 }); // note that this is case insensitive, including on the label lengths
138 inline bool canonCompare(const DNSName& rhs) const;
139 bool slowCanonCompare(const DNSName& rhs) const;
141 #if BOOST_VERSION >= 105300 && ! defined( __APPLE__ ) && ! defined(__OpenBSD__)
142 typedef boost::container::string string_t;
144 typedef std::string string_t;
146 const string_t& getStorage() const {
152 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);
153 static void appendEscapedLabel(std::string& appendTo, const char* orig, size_t len);
154 static std::string unescapeLabel(const std::string& orig);
157 size_t hash_value(DNSName const& d);
160 inline bool DNSName::canonCompare(const DNSName& rhs) const
163 // us: 1a3www4ds9a2nl
164 // rhs: 3www6online3com
165 // to compare, we start at the back, is nl < com? no -> done
170 uint8_t ourpos[64], rhspos[64];
171 uint8_t ourcount=0, rhscount=0;
172 //cout<<"Asked to compare "<<toString()<<" to "<<rhs.toString()<<endl;
173 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)
174 ourpos[ourcount++]=(p-(const unsigned char*)d_storage.c_str());
175 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)
176 rhspos[rhscount++]=(p-(const unsigned char*)rhs.d_storage.c_str());
178 if(ourcount == sizeof(ourpos) || rhscount==sizeof(rhspos)) {
179 return slowCanonCompare(rhs);
183 if(ourcount == 0 && rhscount != 0)
190 bool res=std::lexicographical_compare(
191 d_storage.c_str() + ourpos[ourcount] + 1,
192 d_storage.c_str() + ourpos[ourcount] + 1 + *(d_storage.c_str() + ourpos[ourcount]),
193 rhs.d_storage.c_str() + rhspos[rhscount] + 1,
194 rhs.d_storage.c_str() + rhspos[rhscount] + 1 + *(rhs.d_storage.c_str() + rhspos[rhscount]),
195 [](const unsigned char& a, const unsigned char& b) {
196 return dns_tolower(a) < dns_tolower(b);
199 // cout<<"Forward: "<<res<<endl;
203 res=std::lexicographical_compare( rhs.d_storage.c_str() + rhspos[rhscount] + 1,
204 rhs.d_storage.c_str() + rhspos[rhscount] + 1 + *(rhs.d_storage.c_str() + rhspos[rhscount]),
205 d_storage.c_str() + ourpos[ourcount] + 1,
206 d_storage.c_str() + ourpos[ourcount] + 1 + *(d_storage.c_str() + ourpos[ourcount]),
207 [](const unsigned char& a, const unsigned char& b) {
208 return dns_tolower(a) < dns_tolower(b);
210 // cout<<"Reverse: "<<res<<endl;
218 struct CanonDNSNameCompare: public std::binary_function<DNSName, DNSName, bool>
220 bool operator()(const DNSName&a, const DNSName& b) const
222 return a.canonCompare(b);
226 inline DNSName operator+(const DNSName& lhs, const DNSName& rhs)
234 struct SuffixMatchTree
236 SuffixMatchTree(const std::string& name="", bool endNode_=false) : d_name(name), endNode(endNode_)
239 SuffixMatchTree(const SuffixMatchTree& rhs): d_name(rhs.d_name), children(rhs.children), endNode(rhs.endNode)
242 d_value = rhs.d_value;
245 SuffixMatchTree & operator=(const SuffixMatchTree &rhs)
248 children = rhs.children;
249 endNode = rhs.endNode;
251 d_value = rhs.d_value;
257 mutable std::set<SuffixMatchTree> children;
258 mutable bool endNode;
260 bool operator<(const SuffixMatchTree& rhs) const
262 return strcasecmp(d_name.c_str(), rhs.d_name.c_str()) < 0;
264 typedef SuffixMatchTree value_type;
267 void visit(const V& v) const {
268 for(const auto& c : children)
274 void add(const DNSName& name, const T& t)
276 add(name.getRawLabels(), t);
279 void add(std::vector<std::string> labels, const T& value) const
281 if(labels.empty()) { // this allows insertion of the root
285 else if(labels.size()==1) {
286 auto res=children.emplace(*labels.begin(), true);
288 // we might already have had the node as an
289 // intermediary one, but it's now an end node
290 if(!res.first->endNode) {
291 res.first->endNode = true;
294 res.first->d_value = value;
297 auto res=children.emplace(*labels.rbegin(), false);
299 res.first->add(labels, value);
303 void remove(const DNSName &name) const
305 remove(name.getRawLabels());
308 /* Removes the node at `labels`, also make sure that no empty
309 * children will be left behind in memory
311 void remove(std::vector<std::string> labels) const
313 if (labels.empty()) { // this allows removal of the root
318 SuffixMatchTree smt(*labels.rbegin());
319 auto child = children.find(smt);
320 if (child == children.end()) {
321 // No subnode found, we're done
325 // We have found a child
327 if (labels.empty()) {
328 // The child is no longer an endnode
329 child->endNode = false;
331 // If the child has no further children, just remove it from the set.
332 if (child->children.empty()) {
333 children.erase(child);
338 // We are not at the end, let the child figure out what to do
339 child->remove(labels);
342 T* lookup(const DNSName& name) const
344 if(children.empty()) { // speed up empty set
350 return lookup(name.getRawLabels());
353 T* lookup(std::vector<std::string> labels) const
355 if(labels.empty()) { // optimization
361 SuffixMatchTree smn(*labels.rbegin());
362 auto child = children.find(smn);
363 if(child == children.end()) {
369 auto result = child->lookup(labels);
373 return endNode ? &d_value : nullptr;
376 // Returns all end-nodes, fully qualified (not as separate labels)
377 std::vector<DNSName> getNodes() const {
378 std::vector<DNSName> ret;
380 ret.push_back(DNSName(d_name));
382 for (const auto& child : children) {
383 auto nodes = child.getNodes();
384 for (const auto &node: nodes) {
385 ret.push_back(node + DNSName(d_name));
392 /* Quest in life: serve as a rapid block list. If you add a DNSName to a root SuffixMatchNode,
393 anything part of that domain will return 'true' in check */
394 struct SuffixMatchNode
399 SuffixMatchTree<bool> d_tree;
401 void add(const DNSName& dnsname)
403 d_tree.add(dnsname, true);
404 d_nodes.insert(dnsname);
407 void add(const std::string& name)
412 void add(std::vector<std::string> labels)
414 d_tree.add(labels, true);
416 while (!labels.empty()) {
417 tmp.appendRawLabel(labels.back());
418 labels.pop_back(); // This is safe because we have a copy of labels
423 void remove(const DNSName& name)
429 void remove(std::vector<std::string> labels)
431 d_tree.remove(labels);
433 while (!labels.empty()) {
434 tmp.appendRawLabel(labels.back());
435 labels.pop_back(); // This is safe because we have a copy of labels
440 bool check(const DNSName& dnsname) const
442 return d_tree.lookup(dnsname) != nullptr;
445 std::string toString() const
449 for (const auto& n : d_nodes) {
460 mutable std::set<DNSName> d_nodes; // Only used for string generation
463 std::ostream & operator<<(std::ostream &os, const DNSName& d);
466 struct hash<DNSName> {
467 size_t operator () (const DNSName& dn) const { return dn.hash(0); }
471 DNSName::string_t segmentDNSNameRaw(const char* input); // from ragel
472 bool DNSName::operator==(const DNSName& rhs) const
474 if(rhs.empty() != empty() || rhs.d_storage.size() != d_storage.size())
477 auto us = d_storage.cbegin();
478 auto p = rhs.d_storage.cbegin();
479 for(; us != d_storage.cend() && p != rhs.d_storage.cend(); ++us, ++p) {
480 if(dns_tolower(*p) != dns_tolower(*us))
486 extern const DNSName g_rootdnsname, g_wildcarddnsname;
488 struct DNSNameSet: public std::unordered_set<DNSName> {
489 std::string toString() const {
490 std::ostringstream oss;
491 std::copy(begin(), end(), std::ostream_iterator<DNSName>(oss, "\n"));