]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsname.hh
dnsdist: Add HTTPStatusAction to return a specific HTTP response
[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
83fc9d8a 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
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 }
af619119 245 std::string d_name;
40ea5b5b
RG
246 mutable std::set<SuffixMatchTree> children;
247 mutable bool endNode;
248 mutable T d_value;
249 bool operator<(const SuffixMatchTree& rhs) const
250 {
af619119 251 return strcasecmp(d_name.c_str(), rhs.d_name.c_str()) < 0;
40ea5b5b
RG
252 }
253 typedef SuffixMatchTree value_type;
254
255 template<typename V>
256 void visit(const V& v) const {
257 for(const auto& c : children)
258 c.visit(v);
259 if(endNode)
260 v(*this);
261 }
262
263 void add(const DNSName& name, const T& t)
264 {
265 add(name.getRawLabels(), t);
266 }
267
268 void add(std::vector<std::string> labels, const T& value) const
269 {
270 if(labels.empty()) { // this allows insertion of the root
271 endNode=true;
272 d_value=value;
273 }
274 else if(labels.size()==1) {
6c58e309 275 auto res=children.emplace(*labels.begin(), true);
1a083287
RG
276 if(!res.second) {
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;
281 }
282 }
283 res.first->d_value = value;
40ea5b5b
RG
284 }
285 else {
6c58e309 286 auto res=children.emplace(*labels.rbegin(), false);
40ea5b5b
RG
287 labels.pop_back();
288 res.first->add(labels, value);
289 }
290 }
291
a364fbfb
PL
292 void remove(const DNSName &name) const
293 {
294 remove(name.getRawLabels());
295 }
296
297 /* Removes the node at `labels`, also make sure that no empty
298 * children will be left behind in memory
299 */
300 void remove(std::vector<std::string> labels) const
301 {
dbeebfee
RG
302 if (labels.empty()) { // this allows removal of the root
303 endNode = false;
304 return;
305 }
306
a364fbfb
PL
307 SuffixMatchTree smt(*labels.rbegin());
308 auto child = children.find(smt);
309 if (child == children.end()) {
310 // No subnode found, we're done
311 return;
312 }
313
314 // We have found a child
315 labels.pop_back();
316 if (labels.empty()) {
317 // The child is no longer an endnode
318 child->endNode = false;
67689ec7 319
a364fbfb
PL
320 // If the child has no further children, just remove it from the set.
321 if (child->children.empty()) {
322 children.erase(child);
323 }
324 return;
325 }
326
327 // We are not at the end, let the child figure out what to do
328 child->remove(labels);
329 }
330
40ea5b5b
RG
331 T* lookup(const DNSName& name) const
332 {
333 if(children.empty()) { // speed up empty set
334 if(endNode)
335 return &d_value;
99517c1b 336 return nullptr;
40ea5b5b 337 }
99517c1b 338
40ea5b5b
RG
339 return lookup(name.getRawLabels());
340 }
341
342 T* lookup(std::vector<std::string> labels) const
343 {
344 if(labels.empty()) { // optimization
345 if(endNode)
346 return &d_value;
99517c1b 347 return nullptr;
40ea5b5b
RG
348 }
349
350 SuffixMatchTree smn(*labels.rbegin());
351 auto child = children.find(smn);
352 if(child == children.end()) {
353 if(endNode)
354 return &d_value;
99517c1b 355 return nullptr;
40ea5b5b
RG
356 }
357 labels.pop_back();
99517c1b
RG
358 auto result = child->lookup(labels);
359 if (result) {
360 return result;
361 }
362 return endNode ? &d_value : nullptr;
40ea5b5b
RG
363 }
364
606400e7
PL
365 // Returns all end-nodes, fully qualified (not as separate labels)
366 std::vector<DNSName> getNodes() const {
367 std::vector<DNSName> ret;
368 if (endNode) {
369 ret.push_back(DNSName(d_name));
370 }
371 for (const auto& child : children) {
372 auto nodes = child.getNodes();
373 for (const auto &node: nodes) {
374 ret.push_back(node + DNSName(d_name));
375 }
376 }
377 return ret;
378 }
40ea5b5b
RG
379};
380
0eaa51e5
RG
381/* Quest in life: serve as a rapid block list. If you add a DNSName to a root SuffixMatchNode,
382 anything part of that domain will return 'true' in check */
383struct SuffixMatchNode
384{
15d0cc71
PL
385 public:
386 SuffixMatchNode()
387 {}
388 SuffixMatchTree<bool> d_tree;
389
390 void add(const DNSName& dnsname)
391 {
392 d_tree.add(dnsname, true);
393 d_nodes.insert(dnsname);
15d0cc71 394 }
0eaa51e5 395
41ff82c9
PL
396 void add(const std::string& name)
397 {
398 add(DNSName(name));
399 }
400
15d0cc71
PL
401 void add(std::vector<std::string> labels)
402 {
403 d_tree.add(labels, true);
404 DNSName tmp;
405 while (!labels.empty()) {
406 tmp.appendRawLabel(labels.back());
407 labels.pop_back(); // This is safe because we have a copy of labels
408 }
409 d_nodes.insert(tmp);
15d0cc71 410 }
0eaa51e5 411
15d0cc71
PL
412 void remove(const DNSName& name)
413 {
414 d_tree.remove(name);
415 d_nodes.erase(name);
15d0cc71 416 }
0eaa51e5 417
15d0cc71
PL
418 void remove(std::vector<std::string> labels)
419 {
420 d_tree.remove(labels);
421 DNSName tmp;
422 while (!labels.empty()) {
423 tmp.appendRawLabel(labels.back());
424 labels.pop_back(); // This is safe because we have a copy of labels
425 }
426 d_nodes.erase(tmp);
15d0cc71 427 }
a364fbfb 428
15d0cc71
PL
429 bool check(const DNSName& dnsname) const
430 {
431 return d_tree.lookup(dnsname) != nullptr;
432 }
a364fbfb 433
15d0cc71
PL
434 std::string toString() const
435 {
3f5115d1 436 std::string ret;
15d0cc71
PL
437 bool first = true;
438 for (const auto& n : d_nodes) {
439 if (!first) {
3f5115d1 440 ret += ", ";
15d0cc71
PL
441 }
442 first = false;
3f5115d1 443 ret += n.toString();
606400e7 444 }
3f5115d1 445 return ret;
606400e7 446 }
3f5115d1
PL
447
448 private:
3f5115d1 449 mutable std::set<DNSName> d_nodes; // Only used for string generation
0eaa51e5
RG
450};
451
8171ab83 452std::ostream & operator<<(std::ostream &os, const DNSName& d);
6106128d 453namespace std {
454 template <>
455 struct hash<DNSName> {
456 size_t operator () (const DNSName& dn) const { return dn.hash(0); }
457 };
458}
bae1b0a2 459
460DNSName::string_t segmentDNSNameRaw(const char* input); // from ragel
c21484df 461bool DNSName::operator==(const DNSName& rhs) const
462{
463 if(rhs.empty() != empty() || rhs.d_storage.size() != d_storage.size())
464 return false;
465
466 auto us = d_storage.cbegin();
467 auto p = rhs.d_storage.cbegin();
468 for(; us != d_storage.cend() && p != rhs.d_storage.cend(); ++us, ++p) {
2b62292d 469 if(dns_tolower(*p) != dns_tolower(*us))
c21484df 470 return false;
471 }
472 return true;
473}
474
475extern const DNSName g_rootdnsname, g_wildcarddnsname;
9f618bcc 476
cc0e7aa9 477struct DNSNameSet: public std::unordered_set<DNSName> {
9f618bcc
AD
478 std::string toString() const {
479 std::ostringstream oss;
480 std::copy(begin(), end(), std::ostream_iterator<DNSName>(oss, "\n"));
481 return oss.str();
482 }
483};