]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsname.hh
dnsdist: Add SetNegativeAndSOAAction() and its Lua binding
[thirdparty/pdns.git] / pdns / dnsname.hh
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
3c115e0f 22#pragma once
23#include <string>
97e5c6bc 24#include <vector>
ceee6652 25#include <set>
97e5c6bc 26#include <deque>
ceee6652 27#include <strings.h>
0fae5c1e 28#include <stdexcept>
9f618bcc
AD
29#include <sstream>
30#include <iterator>
cc0e7aa9 31#include <unordered_set>
bae1b0a2 32
9f0fd264
KM
33#include <boost/version.hpp>
34
c9186ca1 35// it crashes on OSX and doesn't compile on OpenBSD
46104a7e 36#if BOOST_VERSION >= 105300 && ! defined( __APPLE__ ) && ! defined(__OpenBSD__)
ca12836d 37#include <boost/container/string.hpp>
bae1b0a2 38#endif
ddb7e6c6 39
2b62292d
TF
40#include "ascii.hh"
41
5c57a75a 42uint32_t burtleCI(const unsigned char* k, uint32_t length, uint32_t init);
76cca09f 43
675fa24c 44// #include "dns.hh"
7abbc40f 45// #include "logger.hh"
3c115e0f 46
8ca15224 47//#include <ext/vstring.h>
d8b778ff 48
3c115e0f 49/* Quest in life:
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
53
54 Be able to turn them into ASCII and "DNS name in a packet" again on request
55
56 Provide some common operators for comparison, detection of being part of another domain
57
58 NOTE: For now, everything MUST be . terminated, otherwise it is an error
59*/
60
3c115e0f 61class DNSName
62{
63public:
ae14c1f3 64 DNSName() {} //!< Constructs an *empty* DNSName, NOT the root!
8171ab83 65 explicit DNSName(const char* p); //!< Constructs from a human formatted, escaped presentation
bae1b0a2 66 explicit DNSName(const std::string& str) : DNSName(str.c_str()) {}; //!< Constructs from a human formatted, escaped presentation
af9f750c 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.
3c115e0f 68
69 bool isPartOf(const DNSName& rhs) const; //!< Are we part of the rhs name?
c21484df 70 inline bool operator==(const DNSName& rhs) const; //!< DNS-native comparison (case insensitive) - empty compares to empty
5fca2e23
PD
71 bool operator!=(const DNSName& other) const { return !(*this == other); }
72
a61e8e59 73 std::string toString(const std::string& separator=".", const bool trailing=true) const; //!< Our human-friendly, escaped, representation
9ab84270 74 std::string toLogString() const; //!< like plain toString, but returns (empty) on empty names
a61e8e59 75 std::string toStringNoDot() const { return toString(".", false); }
3b295666 76 std::string toStringRootDot() const { if(isRoot()) return "."; else return toString(".", false); }
3c115e0f 77 std::string toDNSString() const; //!< Our representation in DNS native format
0ba4e1ee 78 std::string toDNSStringLC() const; //!< Our representation in DNS native format, lower cased
3c115e0f 79 void appendRawLabel(const std::string& str); //!< Append this unescaped label
e14febcf 80 void appendRawLabel(const char* start, unsigned int length); //!< Append this unescaped label
3c115e0f 81 void prependRawLabel(const std::string& str); //!< Prepend this unescaped label
97e5c6bc 82 std::vector<std::string> getRawLabels() const; //!< Individual raw unescaped labels
39c9bef5 83 std::string getRawLabel(unsigned int pos) const; //!< Get the specified raw unescaped label
41e01592 84 DNSName getLastLabel() const; //!< Get the DNSName of the last label
3c115e0f 85 bool chopOff(); //!< Turn www.powerdns.com. into powerdns.com., returns false for .
a61e8e59 86 DNSName makeRelative(const DNSName& zone) const;
d42896fb 87 DNSName makeLowerCase() const
88 {
8b63f61f
KM
89 DNSName ret(*this);
90 ret.makeUsLowerCase();
91 return ret;
92 }
93 void makeUsLowerCase()
94 {
95 for(auto & c : d_storage) {
2b62292d 96 c=dns_tolower(c);
d42896fb 97 }
d42896fb 98 }
8ca15224 99 void makeUsRelative(const DNSName& zone);
9b061cf5 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'
a61e8e59 101 DNSName labelReverse() const;
8aa5a28c 102 bool isWildcard() const;
32cd4eb1 103 bool isHostname() const;
97e5c6bc 104 unsigned int countLabels() const;
3155c04a 105 size_t wirelength() const; //!< Number of total bytes in the name
ae14c1f3 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(); }
c9262563 109 void trimToLabels(unsigned int);
7691e7df 110 size_t hash(size_t init=0) const
76cca09f 111 {
7691e7df 112 return burtleCI((const unsigned char*)d_storage.c_str(), d_storage.size(), init);
76cca09f 113 }
6dcedea6 114 DNSName& operator+=(const DNSName& rhs)
115 {
1106afd9 116 if(d_storage.size() + rhs.d_storage.size() > 256) // one extra byte for the second root label
ac7921b0 117 throw std::range_error("name too long");
ae14c1f3 118 if(rhs.empty())
119 return *this;
120
121 if(d_storage.empty())
122 d_storage+=rhs.d_storage;
123 else
124 d_storage.replace(d_storage.length()-1, rhs.d_storage.length(), rhs.d_storage);
ac7921b0 125
6dcedea6 126 return *this;
127 }
c9262563 128
6d8bc3c6 129 bool operator<(const DNSName& rhs) const // this delivers _some_ kind of ordering, but not one useful in a DNS context. Really fast though.
c9262563 130 {
97e5c6bc 131 return std::lexicographical_compare(d_storage.rbegin(), d_storage.rend(),
132 rhs.d_storage.rbegin(), rhs.d_storage.rend(),
1352c9b6 133 [](const unsigned char& a, const unsigned char& b) {
2b62292d 134 return dns_tolower(a) < dns_tolower(b);
6d8bc3c6 135 }); // note that this is case insensitive, including on the label lengths
c9262563 136 }
137
ddb7e6c6 138 inline bool canonCompare(const DNSName& rhs) const;
f3da403d 139 bool slowCanonCompare(const DNSName& rhs) const;
bae1b0a2 140
46104a7e 141#if BOOST_VERSION >= 105300 && ! defined( __APPLE__ ) && ! defined(__OpenBSD__)
ca12836d 142 typedef boost::container::string string_t;
9f0fd264
KM
143#else
144 typedef std::string string_t;
bae1b0a2 145#endif
c21484df 146 const string_t& getStorage() const {
147 return d_storage;
148 }
bae1b0a2 149private:
d8b778ff 150 string_t d_storage;
520eb5a0 151
83fc9d8a 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);
2430e53c 153 static void appendEscapedLabel(std::string& appendTo, const char* orig, size_t len);
3c115e0f 154 static std::string unescapeLabel(const std::string& orig);
155};
ceee6652 156
5e1031cd 157size_t hash_value(DNSName const& d);
6d8bc3c6 158
ddb7e6c6 159
160inline bool DNSName::canonCompare(const DNSName& rhs) const
161{
162 // 01234567890abcd
163 // us: 1a3www4ds9a2nl
164 // rhs: 3www6online3com
165 // to compare, we start at the back, is nl < com? no -> done
166 //
167 // 0,2,6,a
168 // 0,4,a
169
170 uint8_t ourpos[64], rhspos[64];
171 uint8_t ourcount=0, rhscount=0;
172 //cout<<"Asked to compare "<<toString()<<" to "<<rhs.toString()<<endl;
ae14c1f3 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)
f3da403d 174 ourpos[ourcount++]=(p-(const unsigned char*)d_storage.c_str());
ae14c1f3 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)
f3da403d 176 rhspos[rhscount++]=(p-(const unsigned char*)rhs.d_storage.c_str());
ddb7e6c6 177
178 if(ourcount == sizeof(ourpos) || rhscount==sizeof(rhspos)) {
179 return slowCanonCompare(rhs);
180 }
181
182 for(;;) {
183 if(ourcount == 0 && rhscount != 0)
184 return true;
5708a729 185 if(rhscount == 0)
ddb7e6c6 186 return false;
187 ourcount--;
188 rhscount--;
189
ddb7e6c6 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]),
1352c9b6 195 [](const unsigned char& a, const unsigned char& b) {
2b62292d 196 return dns_tolower(a) < dns_tolower(b);
ddb7e6c6 197 });
198
199 // cout<<"Forward: "<<res<<endl;
200 if(res)
201 return true;
202
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]),
1352c9b6 207 [](const unsigned char& a, const unsigned char& b) {
2b62292d 208 return dns_tolower(a) < dns_tolower(b);
ddb7e6c6 209 });
210 // cout<<"Reverse: "<<res<<endl;
211 if(res)
212 return false;
213 }
214 return false;
215}
216
217
6d8bc3c6 218struct CanonDNSNameCompare: public std::binary_function<DNSName, DNSName, bool>
219{
220 bool operator()(const DNSName&a, const DNSName& b) const
221 {
222 return a.canonCompare(b);
223 }
224};
225
6dcedea6 226inline DNSName operator+(const DNSName& lhs, const DNSName& rhs)
227{
228 DNSName ret=lhs;
229 ret += rhs;
230 return ret;
231}
ceee6652 232
40ea5b5b
RG
233template<typename T>
234struct SuffixMatchTree
235{
af619119 236 SuffixMatchTree(const std::string& name="", bool endNode_=false) : d_name(name), endNode(endNode_)
40ea5b5b
RG
237 {}
238
6c58e309 239 SuffixMatchTree(const SuffixMatchTree& rhs): d_name(rhs.d_name), children(rhs.children), endNode(rhs.endNode)
40ea5b5b 240 {
6c58e309
RG
241 if (endNode) {
242 d_value = rhs.d_value;
243 }
40ea5b5b 244 }
f238ce64
OM
245 SuffixMatchTree & operator=(const SuffixMatchTree &rhs)
246 {
247 d_name = rhs.d_name;
248 children = rhs.children;
249 endNode = rhs.endNode;
250 if (endNode) {
251 d_value = rhs.d_value;
252 }
253 return *this;
254 }
255
af619119 256 std::string d_name;
40ea5b5b
RG
257 mutable std::set<SuffixMatchTree> children;
258 mutable bool endNode;
259 mutable T d_value;
260 bool operator<(const SuffixMatchTree& rhs) const
261 {
af619119 262 return strcasecmp(d_name.c_str(), rhs.d_name.c_str()) < 0;
40ea5b5b
RG
263 }
264 typedef SuffixMatchTree value_type;
265
266 template<typename V>
267 void visit(const V& v) const {
268 for(const auto& c : children)
269 c.visit(v);
270 if(endNode)
271 v(*this);
272 }
273
274 void add(const DNSName& name, const T& t)
275 {
276 add(name.getRawLabels(), t);
277 }
278
279 void add(std::vector<std::string> labels, const T& value) const
280 {
281 if(labels.empty()) { // this allows insertion of the root
282 endNode=true;
283 d_value=value;
284 }
285 else if(labels.size()==1) {
6c58e309 286 auto res=children.emplace(*labels.begin(), true);
1a083287
RG
287 if(!res.second) {
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;
292 }
293 }
294 res.first->d_value = value;
40ea5b5b
RG
295 }
296 else {
6c58e309 297 auto res=children.emplace(*labels.rbegin(), false);
40ea5b5b
RG
298 labels.pop_back();
299 res.first->add(labels, value);
300 }
301 }
302
a364fbfb
PL
303 void remove(const DNSName &name) const
304 {
305 remove(name.getRawLabels());
306 }
307
308 /* Removes the node at `labels`, also make sure that no empty
309 * children will be left behind in memory
310 */
311 void remove(std::vector<std::string> labels) const
312 {
dbeebfee
RG
313 if (labels.empty()) { // this allows removal of the root
314 endNode = false;
315 return;
316 }
317
a364fbfb
PL
318 SuffixMatchTree smt(*labels.rbegin());
319 auto child = children.find(smt);
320 if (child == children.end()) {
321 // No subnode found, we're done
322 return;
323 }
324
325 // We have found a child
326 labels.pop_back();
327 if (labels.empty()) {
328 // The child is no longer an endnode
329 child->endNode = false;
67689ec7 330
a364fbfb
PL
331 // If the child has no further children, just remove it from the set.
332 if (child->children.empty()) {
333 children.erase(child);
334 }
335 return;
336 }
337
338 // We are not at the end, let the child figure out what to do
339 child->remove(labels);
340 }
341
40ea5b5b
RG
342 T* lookup(const DNSName& name) const
343 {
344 if(children.empty()) { // speed up empty set
345 if(endNode)
346 return &d_value;
99517c1b 347 return nullptr;
40ea5b5b 348 }
99517c1b 349
40ea5b5b
RG
350 return lookup(name.getRawLabels());
351 }
352
353 T* lookup(std::vector<std::string> labels) const
354 {
355 if(labels.empty()) { // optimization
356 if(endNode)
357 return &d_value;
99517c1b 358 return nullptr;
40ea5b5b
RG
359 }
360
361 SuffixMatchTree smn(*labels.rbegin());
362 auto child = children.find(smn);
363 if(child == children.end()) {
364 if(endNode)
365 return &d_value;
99517c1b 366 return nullptr;
40ea5b5b
RG
367 }
368 labels.pop_back();
99517c1b
RG
369 auto result = child->lookup(labels);
370 if (result) {
371 return result;
372 }
373 return endNode ? &d_value : nullptr;
40ea5b5b
RG
374 }
375
606400e7
PL
376 // Returns all end-nodes, fully qualified (not as separate labels)
377 std::vector<DNSName> getNodes() const {
378 std::vector<DNSName> ret;
379 if (endNode) {
380 ret.push_back(DNSName(d_name));
381 }
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));
386 }
387 }
388 return ret;
389 }
40ea5b5b
RG
390};
391
0eaa51e5
RG
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 */
394struct SuffixMatchNode
395{
15d0cc71
PL
396 public:
397 SuffixMatchNode()
398 {}
399 SuffixMatchTree<bool> d_tree;
400
401 void add(const DNSName& dnsname)
402 {
403 d_tree.add(dnsname, true);
404 d_nodes.insert(dnsname);
15d0cc71 405 }
0eaa51e5 406
41ff82c9
PL
407 void add(const std::string& name)
408 {
409 add(DNSName(name));
410 }
411
15d0cc71
PL
412 void add(std::vector<std::string> labels)
413 {
414 d_tree.add(labels, true);
415 DNSName tmp;
416 while (!labels.empty()) {
417 tmp.appendRawLabel(labels.back());
418 labels.pop_back(); // This is safe because we have a copy of labels
419 }
420 d_nodes.insert(tmp);
15d0cc71 421 }
0eaa51e5 422
15d0cc71
PL
423 void remove(const DNSName& name)
424 {
425 d_tree.remove(name);
426 d_nodes.erase(name);
15d0cc71 427 }
0eaa51e5 428
15d0cc71
PL
429 void remove(std::vector<std::string> labels)
430 {
431 d_tree.remove(labels);
432 DNSName tmp;
433 while (!labels.empty()) {
434 tmp.appendRawLabel(labels.back());
435 labels.pop_back(); // This is safe because we have a copy of labels
436 }
437 d_nodes.erase(tmp);
15d0cc71 438 }
a364fbfb 439
15d0cc71
PL
440 bool check(const DNSName& dnsname) const
441 {
442 return d_tree.lookup(dnsname) != nullptr;
443 }
a364fbfb 444
15d0cc71
PL
445 std::string toString() const
446 {
3f5115d1 447 std::string ret;
15d0cc71
PL
448 bool first = true;
449 for (const auto& n : d_nodes) {
450 if (!first) {
3f5115d1 451 ret += ", ";
15d0cc71
PL
452 }
453 first = false;
3f5115d1 454 ret += n.toString();
606400e7 455 }
3f5115d1 456 return ret;
606400e7 457 }
3f5115d1
PL
458
459 private:
3f5115d1 460 mutable std::set<DNSName> d_nodes; // Only used for string generation
0eaa51e5
RG
461};
462
8171ab83 463std::ostream & operator<<(std::ostream &os, const DNSName& d);
6106128d 464namespace std {
465 template <>
466 struct hash<DNSName> {
467 size_t operator () (const DNSName& dn) const { return dn.hash(0); }
468 };
469}
bae1b0a2 470
471DNSName::string_t segmentDNSNameRaw(const char* input); // from ragel
c21484df 472bool DNSName::operator==(const DNSName& rhs) const
473{
474 if(rhs.empty() != empty() || rhs.d_storage.size() != d_storage.size())
475 return false;
476
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) {
2b62292d 480 if(dns_tolower(*p) != dns_tolower(*us))
c21484df 481 return false;
482 }
483 return true;
484}
485
486extern const DNSName g_rootdnsname, g_wildcarddnsname;
9f618bcc 487
cc0e7aa9 488struct DNSNameSet: public std::unordered_set<DNSName> {
9f618bcc
AD
489 std::string toString() const {
490 std::ostringstream oss;
491 std::copy(begin(), end(), std::ostream_iterator<DNSName>(oss, "\n"));
492 return oss.str();
493 }
494};