• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.84
/pdns/dnsname.hh
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
 */
22
#pragma once
23
#include <array>
24
#include <cstring>
25
#include <optional>
26
#include <string>
27
#include <vector>
28
#include <set>
29
#include <strings.h>
30
#include <stdexcept>
31
#include <sstream>
32
#include <iterator>
33
#include <unordered_set>
34
#include <string_view>
35

36
#include <boost/version.hpp>
37
#include <boost/container/string.hpp>
38

39
inline bool dns_isspace(char c)
40
{
5,192,842✔
41
  return c == ' ' || c == '\t' || c == '\r' || c == '\n';
5,192,842!
42
}
5,192,842✔
43

44
extern const unsigned char dns_toupper_table[256],  dns_tolower_table[256];
45

46
inline unsigned char dns_toupper(unsigned char c)
47
{
931,431✔
48
  return dns_toupper_table[c];
931,431✔
49
}
931,431✔
50

51
inline unsigned char dns_tolower(unsigned char c)
52
{
5,913,757,572✔
53
  return dns_tolower_table[c];
5,913,757,572✔
54
}
5,913,757,572✔
55

56
#include "burtle.hh"
57
#include "views.hh"
58

59
// #include "dns.hh"
60
// #include "logger.hh"
61

62
//#include <ext/vstring.h>
63

64
/* Quest in life:
65
     accept escaped ascii presentations of DNS names and store them "natively"
66
     accept a DNS packet with an offset, and extract a DNS name from it
67
     build up DNSNames with prepend and append of 'raw' unescaped labels
68

69
   Be able to turn them into ASCII and "DNS name in a packet" again on request
70

71
   Provide some common operators for comparison, detection of being part of another domain
72

73
   NOTE: For now, everything MUST be . terminated, otherwise it is an error
74
*/
75

76
class DNSName
77
{
78
public:
79
  static const size_t s_maxDNSNameLength = 255;
80

81
  DNSName() = default; //!< Constructs an *empty* DNSName, NOT the root!
699,471,218✔
82
  // Work around assertion in some boost versions that do not like self-assignment of boost::container::string
83
  DNSName& operator=(const DNSName& rhs)
84
  {
20,532,865✔
85
    if (this != &rhs) {
20,540,700✔
86
      d_storage = rhs.d_storage;
20,537,318✔
87
    }
20,537,318✔
88
    return *this;
20,532,865✔
89
  }
20,532,865✔
90
  DNSName& operator=(DNSName&& rhs) noexcept
91
  {
57,293,660✔
92
    if (this != &rhs) {
57,328,744✔
93
      d_storage = std::move(rhs.d_storage);
57,293,411✔
94
    }
57,293,411✔
95
    return *this;
57,293,660✔
96
  }
57,293,660✔
97
  DNSName(const DNSName& a) = default;
186,963,352✔
98
  DNSName(DNSName&& a) = default;
102,017,793✔
99

100
  explicit DNSName(std::string_view sw); //!< Constructs from a human formatted, escaped presentation
101
  DNSName(const char* p, size_t len, size_t 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.
102

103
  bool isPartOf(const DNSName& rhs) const;   //!< Are we part of the rhs name? Note that name.isPartOf(name).
104
  inline bool operator==(const DNSName& rhs) const; //!< DNS-native comparison (case insensitive) - empty compares to empty
105
  bool operator!=(const DNSName& other) const { return !(*this == other); }
3,492,226✔
106

107
  std::string toString(const std::string& separator=".", const bool trailing=true) const;              //!< Our human-friendly, escaped, representation
108
  void toString(std::string& output, const std::string& separator=".", const bool trailing=true) const;
109
  std::string toLogString() const; //!< like plain toString, but returns (empty) on empty names
110
  std::string toStringNoDot() const { return toString(".", false); }
9,391✔
111
  std::string toStringRootDot() const { if(isRoot()) return "."; else return toString(".", false); }
150,332✔
112
  std::string toDNSString() const;           //!< Our representation in DNS native format
113
  std::string toDNSStringLC() const;           //!< Our representation in DNS native format, lower cased
114
  void appendRawLabel(const std::string& str); //!< Append this unescaped label
115
  void appendRawLabel(const char* start, unsigned int length); //!< Append this unescaped label
116
  void prependRawLabel(const std::string& str); //!< Prepend this unescaped label
117
  std::vector<std::string> getRawLabels() const; //!< Individual raw unescaped labels
118
  std::string getRawLabel(unsigned int pos) const; //!< Get the specified raw unescaped label
119
  DNSName getLastLabel() const; //!< Get the DNSName of the last label
120
  bool chopOff();                               //!< Turn www.powerdns.com. into powerdns.com., returns false for .
121
  DNSName makeRelative(const DNSName& zone) const;
122
  DNSName makeLowerCase() const
123
  {
80,355✔
124
    DNSName ret(*this);
80,355✔
125
    ret.makeUsLowerCase();
80,355✔
126
    return ret;
80,355✔
127
  }
80,355✔
128
  void makeUsLowerCase()
129
  {
80,929✔
130
    for(auto & c : d_storage) {
1,381,835✔
131
      c=dns_tolower(c);
1,381,835✔
132
    }
1,381,835✔
133
  }
80,929✔
134
  void makeUsRelative(const DNSName& zone);
135
  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'
136
  DNSName labelReverse() const;
137
  bool isWildcard() const;
138
  bool isHostname() const;
139
  unsigned int countLabels() const;
140
  size_t wirelength() const; //!< Number of total bytes in the name
141
  bool empty() const { return d_storage.empty(); }
916,229,139✔
142
  bool isRoot() const { return d_storage.size()==1 && d_storage[0]==0; }
27,471,537✔
143
  void clear() { d_storage.clear(); }
3,914✔
144
  void trimToLabels(unsigned int);
145
  size_t hash(size_t init=0) const
146
  {
12,860,107✔
147
    return burtleCI((const unsigned char*)d_storage.c_str(), d_storage.size(), init);
12,860,107✔
148
  }
12,860,107✔
149
  DNSName& operator+=(const DNSName& rhs)
150
  {
8,648,453✔
151
    if(d_storage.size() + rhs.d_storage.size() > s_maxDNSNameLength + 1) // one extra byte for the second root label
8,648,453✔
152
      throwSafeRangeError("resulting name too long", rhs.d_storage.data(), rhs.d_storage.size());
3✔
153
    if(rhs.empty())
8,648,453✔
154
      return *this;
20✔
155

156
    if(d_storage.empty())
8,648,433!
157
      d_storage+=rhs.d_storage;
×
158
    else
8,648,433✔
159
      d_storage.replace(d_storage.length()-1, rhs.d_storage.length(), rhs.d_storage);
8,648,433✔
160

161
    return *this;
8,648,433✔
162
  }
8,648,453✔
163

164
  bool operator<(const DNSName& rhs)  const // this delivers _some_ kind of ordering, but not one useful in a DNS context. Really fast though.
165
  {
10,153,971✔
166
    return std::lexicographical_compare(d_storage.rbegin(), d_storage.rend(),
10,153,971✔
167
                                 rhs.d_storage.rbegin(), rhs.d_storage.rend(),
10,153,971✔
168
                                 [](const unsigned char& a, const unsigned char& b) {
230,104,483✔
169
                                          return dns_tolower(a) < dns_tolower(b);
230,104,483✔
170
                                        }); // note that this is case insensitive, including on the label lengths
230,104,483✔
171
  }
10,153,971✔
172

173
  inline bool canonCompare(const DNSName& rhs) const;
174
  bool slowCanonCompare(const DNSName& rhs) const;
175

176
  typedef boost::container::string string_t;
177

178
  const string_t& getStorage() const {
46,164,383✔
179
    return d_storage;
46,164,383✔
180
  }
46,164,383✔
181

182
  bool has8bitBytes() const; /* returns true if at least one byte of the labels forming the name is not included in [A-Za-z0-9_*./@ \\:-] */
183

184
  class RawLabelsVisitor
185
  {
186
  public:
187
    /* Zero-copy, zero-allocation raw labels visitor.
188
       The general idea is that we walk the labels in the constructor,
189
       filling up our array of labels position and setting the initial
190
       value of d_position at the number of labels.
191
       We then can easily provide string_view into the first and last label.
192
       pop_back() moves d_position one label closer to the start, so we
193
       can also easily walk back the labels in reverse order.
194
       There is no copy because we use a reference into the DNSName storage,
195
       so it is absolutely forbidden to alter the DNSName for as long as we
196
       exist, and no allocation because we use a static array (there cannot
197
       be more than 128 labels in a DNSName).
198
    */
199
    RawLabelsVisitor(const string_t& storage);
200
    std::string_view front() const;
201
    std::string_view back() const;
202
    bool pop_back();
203
    bool empty() const;
204
  private:
205
    std::array<uint8_t, 128> d_labelPositions;
206
    const string_t& d_storage;
207
    size_t d_position{0};
208
  };
209
  RawLabelsVisitor getRawLabelsVisitor() const;
210

211
private:
212
  string_t d_storage;
213

214
  void packetParser(const char* qpos, size_t len, size_t offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset);
215
  size_t parsePacketUncompressed(const pdns::views::UnsignedCharView& view, size_t position, bool uncompress);
216
  static void appendEscapedLabel(std::string& appendTo, const char* orig, size_t len);
217
  static std::string unescapeLabel(const std::string& orig);
218
  static void throwSafeRangeError(const std::string& msg, const char* buf, size_t length);
219
};
220

221
size_t hash_value(DNSName const& d);
222

223

224
inline bool DNSName::canonCompare(const DNSName& rhs) const
225
{
54,867,233✔
226
  //      01234567890abcd
227
  // us:  1a3www4ds9a2nl
228
  // rhs: 3www6online3com
229
  // to compare, we start at the back, is nl < com? no -> done
230
  //
231
  // 0,2,6,a
232
  // 0,4,a
233

234
  uint8_t ourpos[64], rhspos[64];
54,867,233✔
235
  uint8_t ourcount=0, rhscount=0;
54,867,233✔
236
  //cout<<"Asked to compare "<<toString()<<" to "<<rhs.toString()<<endl;
237
  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)
165,510,896✔
238
    ourpos[ourcount++]=(p-(const unsigned char*)d_storage.c_str());
110,643,663✔
239
  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)
165,128,217✔
240
    rhspos[rhscount++]=(p-(const unsigned char*)rhs.d_storage.c_str());
110,260,984✔
241

242
  if(ourcount == sizeof(ourpos) || rhscount==sizeof(rhspos)) {
54,906,763✔
243
    return slowCanonCompare(rhs);
×
244
  }
×
245

246
  for(;;) {
62,394,303✔
247
    if(ourcount == 0 && rhscount != 0)
62,394,303✔
248
      return true;
71,639✔
249
    if(rhscount == 0)
62,322,664✔
250
      return false;
3,117,532✔
251
    ourcount--;
59,205,132✔
252
    rhscount--;
59,205,132✔
253

254
    bool res=std::lexicographical_compare(
59,205,132✔
255
                                          d_storage.c_str() + ourpos[ourcount] + 1,
59,205,132✔
256
                                          d_storage.c_str() + ourpos[ourcount] + 1 + *(d_storage.c_str() + ourpos[ourcount]),
59,205,132✔
257
                                          rhs.d_storage.c_str() + rhspos[rhscount] + 1,
59,205,132✔
258
                                          rhs.d_storage.c_str() + rhspos[rhscount] + 1 + *(rhs.d_storage.c_str() + rhspos[rhscount]),
59,205,132✔
259
                                          [](const unsigned char& a, const unsigned char& b) {
229,556,102✔
260
                                            return dns_tolower(a) < dns_tolower(b);
229,556,102✔
261
                                          });
229,556,102✔
262

263
    //    cout<<"Forward: "<<res<<endl;
264
    if(res)
59,205,132✔
265
      return true;
30,349,070✔
266

267
    res=std::lexicographical_compare(          rhs.d_storage.c_str() + rhspos[rhscount] + 1,
28,856,062✔
268
                                          rhs.d_storage.c_str() + rhspos[rhscount] + 1 + *(rhs.d_storage.c_str() + rhspos[rhscount]),
28,856,062✔
269
                                          d_storage.c_str() + ourpos[ourcount] + 1,
28,856,062✔
270
                                          d_storage.c_str() + ourpos[ourcount] + 1 + *(d_storage.c_str() + ourpos[ourcount]),
28,856,062✔
271
                                          [](const unsigned char& a, const unsigned char& b) {
144,644,973✔
272
                                            return dns_tolower(a) < dns_tolower(b);
144,644,973✔
273
                                          });
144,644,973✔
274
    //    cout<<"Reverse: "<<res<<endl;
275
    if(res)
28,856,062✔
276
      return false;
21,413,378✔
277
  }
28,856,062✔
278
  return false;
4,294,967,294✔
279
}
54,867,233✔
280

281

282
struct CanonDNSNameCompare
283
{
284
  bool operator()(const DNSName&a, const DNSName& b) const
285
  {
54,240,876✔
286
    return a.canonCompare(b);
54,240,876✔
287
  }
54,240,876✔
288
};
289

290
inline DNSName operator+(const DNSName& lhs, const DNSName& rhs)
291
{
8,311,372✔
292
  DNSName ret=lhs;
8,311,372✔
293
  ret += rhs;
8,311,372✔
294
  return ret;
8,311,372✔
295
}
8,311,372✔
296

297
extern const DNSName g_rootdnsname, g_wildcarddnsname;
298

299
template<typename T>
300
struct SuffixMatchTree
301
{
302
  SuffixMatchTree(const std::string& name="", bool endNode_=false) : d_name(name), endNode(endNode_)
303
  {}
20,050✔
304

305
  SuffixMatchTree(const SuffixMatchTree& rhs): d_name(rhs.d_name), children(rhs.children), endNode(rhs.endNode)
306
  {
6,403✔
307
    if (endNode) {
6,403✔
308
      d_value = rhs.d_value;
1,859✔
309
    }
1,859✔
310
  }
6,403✔
311
  SuffixMatchTree & operator=(const SuffixMatchTree &rhs)
312
  {
603✔
313
    d_name = rhs.d_name;
603✔
314
    children = rhs.children;
603✔
315
    endNode = rhs.endNode;
603✔
316
    if (endNode) {
603!
317
      d_value = rhs.d_value;
318
    }
319
    return *this;
603✔
320
  }
603✔
321
  bool operator<(const SuffixMatchTree& rhs) const
322
  {
16,390✔
323
    return strcasecmp(d_name.c_str(), rhs.d_name.c_str()) < 0;
16,390✔
324
  }
16,390✔
325

326
  std::string d_name;
327
  mutable std::set<SuffixMatchTree, std::less<>> children;
328
  mutable bool endNode;
329
  mutable T d_value;
330

331
  /* this structure is used to do a lookup without allocating and
332
     copying a string, using C++14's heterogeneous lookups in ordered
333
     containers */
334
  struct LightKey
335
  {
336
    std::string_view d_name;
337
    bool operator<(const SuffixMatchTree& smt) const
338
    {
161,757✔
339
      auto compareUpTo = std::min(this->d_name.size(), smt.d_name.size());
161,757✔
340
      auto ret = strncasecmp(this->d_name.data(), smt.d_name.data(), compareUpTo);
161,757✔
341
      if (ret != 0) {
161,757✔
342
        return ret < 0;
11,429✔
343
      }
11,429✔
344
      if (this->d_name.size() == smt.d_name.size()) {
150,328!
345
        return ret < 0;
149,994✔
346
      }
149,994✔
347
      return this->d_name.size() < smt.d_name.size();
334✔
348
    }
150,328✔
349
  };
350

351
  bool operator<(const LightKey& lk) const
352
  {
185,822✔
353
    auto compareUpTo = std::min(this->d_name.size(), lk.d_name.size());
185,822✔
354
    auto ret = strncasecmp(this->d_name.data(), lk.d_name.data(), compareUpTo);
185,822✔
355
    if (ret != 0) {
185,822✔
356
      return ret < 0;
34,477✔
357
    }
34,477✔
358
    if (this->d_name.size() == lk.d_name.size()) {
151,348!
359
      return ret < 0;
149,996✔
360
    }
149,996✔
361
    return this->d_name.size() < lk.d_name.size();
2,147,484,999✔
362
  }
151,345✔
363

364
  template<typename V>
365
  void visit(const V& v) const {
3,666✔
366
    for(const auto& c : children) {
3,666✔
367
      c.visit(v);
2,451✔
368
    }
2,451✔
369

370
    if (endNode) {
3,666!
371
      v(*this);
1,521✔
372
    }
1,521✔
373
  }
3,666✔
374

375
  void add(const DNSName& name, T&& t)
376
  {
2,054✔
377
    auto labels = name.getRawLabels();
2,054✔
378
    add(labels, std::move(t));
2,054✔
379
  }
2,054✔
380

381
  void add(std::vector<std::string>& labels, T&& value) const
382
  {
8,794✔
383
    if (labels.empty()) { // this allows insertion of the root
8,794✔
384
      endNode = true;
13✔
385
      d_value = std::move(value);
13✔
386
    }
13✔
387
    else if(labels.size()==1) {
8,781✔
388
      auto res = children.emplace(*labels.begin(), true);
2,050✔
389
      if (!res.second) {
2,050✔
390
        // we might already have had the node as an
391
        // intermediary one, but it's now an end node
392
        if (!res.first->endNode) {
32!
393
          res.first->endNode = true;
32✔
394
        }
32✔
395
      }
32✔
396
      res.first->d_value = std::move(value);
2,050✔
397
    }
2,050✔
398
    else {
6,731✔
399
      auto res = children.emplace(*labels.rbegin(), false);
6,731✔
400
      labels.pop_back();
6,731✔
401
      res.first->add(labels, std::move(value));
6,731✔
402
    }
6,731✔
403
  }
8,794✔
404

405
  void remove(const DNSName &name, bool subtree=false) const
406
  {
1,608✔
407
    auto labels = name.getRawLabels();
1,608✔
408
    remove(labels, subtree);
1,608✔
409
  }
1,608✔
410

411
  /* Removes the node at `labels`, also make sure that no empty
412
   * children will be left behind in memory
413
   */
414
  void remove(std::vector<std::string>& labels, bool subtree = false) const
415
  {
3,207✔
416
    if (labels.empty()) { // this allows removal of the root
3,207✔
417
      endNode = false;
235✔
418
      if (subtree) {
235!
419
        children.clear();
229✔
420
      }
229✔
421
      return;
235✔
422
    }
235✔
423

424
    SuffixMatchTree smt(*labels.rbegin());
2,972✔
425
    auto child = children.find(smt);
2,972✔
426
    if (child == children.end()) {
2,972!
427
      // No subnode found, we're done
428
      return;
787✔
429
    }
787✔
430

431
    // We have found a child
432
    labels.pop_back();
2,185✔
433
    if (labels.empty()) {
2,185✔
434
      // The child is no longer an endnode
435
      child->endNode = false;
586✔
436

437
      if (subtree) {
586!
438
        child->children.clear();
10✔
439
      }
10✔
440

441
      // If the child has no further children, just remove it from the set.
442
      if (child->children.empty()) {
586!
443
        children.erase(child);
566✔
444
      }
566✔
445
      return;
586✔
446
    }
586✔
447

448
    // We are not at the end, let the child figure out what to do
449
    child->remove(labels);
1,599✔
450
  }
1,599✔
451

452
  T* lookup(const DNSName& name) const
453
  {
115,023✔
454
    auto bestNode = getBestNode(name);
115,023✔
455
    if (bestNode) {
115,023✔
456
      return &bestNode->d_value;
53,018✔
457
    }
53,018✔
458
    return nullptr;
62,005✔
459
  }
115,023✔
460

461
  std::optional<DNSName> getBestMatch(const DNSName& name) const
462
  {
15✔
463
    if (children.empty()) { // speed up empty set
15!
464
      return endNode ? std::optional<DNSName>(g_rootdnsname) : std::nullopt;
×
465
    }
×
466

467
    auto visitor = name.getRawLabelsVisitor();
15✔
468
    return getBestMatch(visitor);
15✔
469
  }
15✔
470

471
  // Returns all end-nodes, fully qualified (not as separate labels)
472
  std::vector<DNSName> getNodes() const {
16✔
473
    std::vector<DNSName> ret;
16✔
474
    if (endNode) {
16✔
475
      ret.push_back(DNSName(d_name));
6✔
476
    }
6✔
477
    for (const auto& child : children) {
16✔
478
      auto nodes = child.getNodes();
12✔
479
      ret.reserve(ret.size() + nodes.size());
12✔
480
      for (const auto &node: nodes) {
12✔
481
        ret.push_back(node + DNSName(d_name));
6✔
482
      }
6✔
483
    }
12✔
484
    return ret;
16✔
485
  }
16✔
486

487
private:
488
  const SuffixMatchTree* getBestNode(const DNSName& name)  const
489
  {
115,021✔
490
    if (children.empty()) { // speed up empty set
115,021✔
491
      if (endNode) {
49,457!
492
        return this;
10✔
493
      }
10✔
494
      return nullptr;
49,447✔
495
    }
49,457✔
496

497
    auto visitor = name.getRawLabelsVisitor();
65,564✔
498
    return getBestNode(visitor);
65,564✔
499
  }
115,021✔
500

501
  const SuffixMatchTree* getBestNode(DNSName::RawLabelsVisitor& visitor) const
502
  {
215,523✔
503
    if (visitor.empty()) { // optimization
215,523✔
504
      if (endNode) {
44,910✔
505
        return this;
44,825✔
506
      }
44,825✔
507
      return nullptr;
85✔
508
    }
44,910✔
509

510
    const LightKey lk{visitor.back()};
170,613✔
511
    auto child = children.find(lk);
170,613✔
512
    if (child == children.end()) {
170,613✔
513
      if (endNode) {
20,655✔
514
        return this;
8,180✔
515
      }
8,180✔
516
      return nullptr;
12,475✔
517
    }
20,655✔
518
    visitor.pop_back();
149,958✔
519
    auto result = child->getBestNode(visitor);
149,958✔
520
    if (result) {
149,958✔
521
      return result;
111,311✔
522
    }
111,311✔
523
    return endNode ? this : nullptr;
38,647!
524
  }
149,958✔
525

526
  std::optional<DNSName> getBestMatch(DNSName::RawLabelsVisitor& visitor) const
527
  {
51✔
528
    if (visitor.empty()) { // optimization
51✔
529
      if (endNode) {
3!
530
        return std::optional<DNSName>(d_name);
3✔
531
      }
3✔
532
      return std::nullopt;
×
533
    }
3✔
534

535
    const LightKey lk{visitor.back()};
48✔
536
    auto child = children.find(lk);
48✔
537
    if (child == children.end()) {
48✔
538
      if (endNode) {
12✔
539
        return std::optional<DNSName>(d_name);
6✔
540
      }
6✔
541
      return std::nullopt;
6✔
542
    }
12✔
543
    visitor.pop_back();
36✔
544
    auto result = child->getBestMatch(visitor);
36✔
545
    if (result) {
36✔
546
      if (!d_name.empty()) {
24✔
547
        result->appendRawLabel(d_name);
18✔
548
      }
18✔
549
      return result;
24✔
550
    }
24✔
551
    return endNode ? std::optional<DNSName>(d_name) : std::nullopt;
12!
552
  }
36✔
553
};
554

555
/* Quest in life: serve as a rapid block list. If you add a DNSName to a root SuffixMatchNode,
556
   anything part of that domain will return 'true' in check */
557
struct SuffixMatchNode
558
{
559
  public:
560
    SuffixMatchNode() = default;
3,332✔
561
    SuffixMatchTree<bool> d_tree;
562

563
    void add(const DNSName& dnsname)
564
    {
1,287✔
565
      d_tree.add(dnsname, true);
1,287✔
566
      d_nodes.insert(dnsname);
1,287✔
567
    }
1,287✔
568

569
    void add(const std::string& name)
570
    {
15✔
571
      add(DNSName(name));
15✔
572
    }
15✔
573

574
    void add(std::vector<std::string> labels)
575
    {
6✔
576
      d_tree.add(labels, true);
6✔
577
      DNSName tmp;
6✔
578
      while (!labels.empty()) {
12✔
579
        tmp.appendRawLabel(labels.back());
6✔
580
        labels.pop_back(); // This is safe because we have a copy of labels
6✔
581
      }
6✔
582
      d_nodes.insert(tmp);
6✔
583
    }
6✔
584

585
    void remove(const DNSName& name)
586
    {
6✔
587
      d_tree.remove(name);
6✔
588
      d_nodes.erase(name);
6✔
589
    }
6✔
590

591
    void remove(std::vector<std::string> labels)
592
    {
×
593
      d_tree.remove(labels);
×
594
      DNSName tmp;
×
595
      while (!labels.empty()) {
×
596
        tmp.appendRawLabel(labels.back());
×
597
        labels.pop_back(); // This is safe because we have a copy of labels
×
598
      }
×
599
      d_nodes.erase(tmp);
×
600
    }
×
601

602
    bool check(const DNSName& dnsname) const
603
    {
48,042✔
604
      return d_tree.lookup(dnsname) != nullptr;
48,042✔
605
    }
48,042✔
606

607
    std::optional<DNSName> getBestMatch(const DNSName& name) const
608
    {
15✔
609
      return d_tree.getBestMatch(name);
15✔
610
    }
15✔
611

612
    std::string toString() const
613
    {
1,028✔
614
      std::string ret;
1,028✔
615
      bool first = true;
1,028✔
616
      for (const auto& n : d_nodes) {
1,028✔
617
        if (!first) {
1,028!
618
          ret += ", ";
×
619
        }
×
620
        first = false;
1,028✔
621
        ret += n.toString();
1,028✔
622
      }
1,028✔
623
      return ret;
1,028✔
624
    }
1,028✔
625

626
  private:
627
    mutable std::set<DNSName> d_nodes; // Only used for string generation
628
};
629

630
std::ostream & operator<<(std::ostream &os, const DNSName& d);
631
namespace std {
632
    template <>
633
    struct hash<DNSName> {
634
        size_t operator () (const DNSName& dn) const { return dn.hash(0); }
754,006✔
635
    };
636
}
637

638
DNSName::string_t segmentDNSNameRaw(const char* input, size_t inputlen); // from ragel
639

640
bool DNSName::operator==(const DNSName& rhs) const
641
{
274,297,434✔
642
  if (rhs.empty() != empty() || rhs.d_storage.size() != d_storage.size()) {
274,328,890✔
643
    return false;
202,477,730✔
644
  }
202,477,730✔
645

646
  const auto* us = d_storage.cbegin();
71,819,704✔
647
  const auto* p = rhs.d_storage.cbegin();
71,819,704✔
648
  for (; us != d_storage.cend() && p != rhs.d_storage.cend(); ++us, ++p) {
1,362,158,318✔
649
    if (dns_tolower(*p) != dns_tolower(*us)) {
1,292,177,752✔
650
      return false;
1,839,138✔
651
    }
1,839,138✔
652
  }
1,292,177,752✔
653
  return true;
69,980,566✔
654
}
71,819,704✔
655

656
struct DNSNameSet: public std::unordered_set<DNSName> {
657
    std::string toString() const {
×
658
        std::ostringstream oss;
×
659
        std::copy(begin(), end(), std::ostream_iterator<DNSName>(oss, "\n"));
×
660
        return oss.str();
×
661
    }
×
662
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc