]>
Commit | Line | Data |
---|---|---|
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 | 42 | uint32_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 | 61 | class DNSName |
62 | { | |
63 | public: | |
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 | 149 | private: |
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 | 157 | size_t hash_value(DNSName const& d); |
6d8bc3c6 | 158 | |
ddb7e6c6 | 159 | |
160 | inline 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 | 218 | struct 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 | 226 | inline DNSName operator+(const DNSName& lhs, const DNSName& rhs) |
227 | { | |
228 | DNSName ret=lhs; | |
229 | ret += rhs; | |
230 | return ret; | |
231 | } | |
ceee6652 | 232 | |
40ea5b5b RG |
233 | template<typename T> |
234 | struct 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 */ | |
383 | struct 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 | 452 | std::ostream & operator<<(std::ostream &os, const DNSName& d); |
6106128d | 453 | namespace std { |
454 | template <> | |
455 | struct hash<DNSName> { | |
456 | size_t operator () (const DNSName& dn) const { return dn.hash(0); } | |
457 | }; | |
458 | } | |
bae1b0a2 | 459 | |
460 | DNSName::string_t segmentDNSNameRaw(const char* input); // from ragel | |
c21484df | 461 | bool 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 | ||
475 | extern const DNSName g_rootdnsname, g_wildcarddnsname; | |
9f618bcc | 476 | |
cc0e7aa9 | 477 | struct 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 | }; |