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=0, uint16_t* qclass=0, unsigned int* consumed=0, uint16_t minOffset=0); //!< Construct from a DNS Packet, taking the first question if offset=12
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 std::string escapeLabel(const std::string& orig);
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;
246 mutable std::set<SuffixMatchTree> children;
247 mutable bool endNode;
249 bool operator<(const SuffixMatchTree& rhs) const
251 return strcasecmp(d_name.c_str(), rhs.d_name.c_str()) < 0;
253 typedef SuffixMatchTree value_type;
256 void visit(const V& v) const {
257 for(const auto& c : children)
263 void add(const DNSName& name, const T& t)
265 add(name.getRawLabels(), t);
268 void add(std::vector<std::string> labels, const T& value) const
270 if(labels.empty()) { // this allows insertion of the root
274 else if(labels.size()==1) {
275 auto res=children.emplace(*labels.begin(), true);
277 // we might already have had the node as an
278 // intermediary one, but it's now an end node
279 if(!res.first->endNode) {
280 res.first->endNode = true;
283 res.first->d_value = value;
286 auto res=children.emplace(*labels.rbegin(), false);
288 res.first->add(labels, value);
292 T* lookup(const DNSName& name) const
294 if(children.empty()) { // speed up empty set
299 return lookup(name.getRawLabels());
302 T* lookup(std::vector<std::string> labels) const
304 if(labels.empty()) { // optimization
310 SuffixMatchTree smn(*labels.rbegin());
311 auto child = children.find(smn);
312 if(child == children.end()) {
318 return child->lookup(labels);
323 /* Quest in life: serve as a rapid block list. If you add a DNSName to a root SuffixMatchNode,
324 anything part of that domain will return 'true' in check */
325 struct SuffixMatchNode
330 SuffixMatchTree<bool> d_tree;
332 void add(const DNSName& dnsname)
335 d_human.append(", ");
336 d_human += dnsname.toString();
338 d_tree.add(dnsname, true);
341 void add(std::vector<std::string> labels)
343 d_tree.add(labels, true);
346 bool check(const DNSName& dnsname) const
348 return d_tree.lookup(dnsname) != nullptr;
351 std::string toString() const
358 std::ostream & operator<<(std::ostream &os, const DNSName& d);
361 struct hash<DNSName> {
362 size_t operator () (const DNSName& dn) const { return dn.hash(0); }
366 DNSName::string_t segmentDNSNameRaw(const char* input); // from ragel
367 bool DNSName::operator==(const DNSName& rhs) const
369 if(rhs.empty() != empty() || rhs.d_storage.size() != d_storage.size())
372 auto us = d_storage.cbegin();
373 auto p = rhs.d_storage.cbegin();
374 for(; us != d_storage.cend() && p != rhs.d_storage.cend(); ++us, ++p) {
375 if(dns_tolower(*p) != dns_tolower(*us))
381 extern const DNSName g_rootdnsname, g_wildcarddnsname;
383 struct DNSNameSet: public std::unordered_set<DNSName> {
384 std::string toString() const {
385 std::ostringstream oss;
386 std::copy(begin(), end(), std::ostream_iterator<DNSName>(oss, "\n"));