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

PowerDNS / pdns / 19741624072

27 Nov 2025 03:45PM UTC coverage: 73.086% (+0.02%) from 73.065%
19741624072

Pull #16570

github

web-flow
Merge 08a2cdb1d into f94a3f63f
Pull Request #16570: rec: rewrite all unwrap calls in web.rs

38523 of 63408 branches covered (60.75%)

Branch coverage included in aggregate %.

128044 of 164496 relevant lines covered (77.84%)

6531485.83 hits per line

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

57.72
/pdns/dnsdistdist/dnsdist-rules-factory.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

24
#include <boost/multi_index_container.hpp>
25
#include <boost/multi_index/ordered_index.hpp>
26
#include <boost/multi_index/sequenced_index.hpp>
27
#include <boost/multi_index/key_extractors.hpp>
28

29
#include "cachecleaner.hh"
30
#include "dnsdist-ecs.hh"
31
#include "dnsdist-kvs.hh"
32
#include "dnsdist-lua.hh"
33
#include "dnsdist-lua-ffi.hh"
34
#include "dnsdist-rules.hh"
35
#include "dolog.hh"
36
#include "dnsparser.hh"
37
#include "dns_random.hh"
38
#include "uuid-utils.hh"
39

40
namespace dnsdist::selectors
41
{
42
using LuaSelectorFunction = std::function<bool(const DNSQuestion* dq)>;
43
using LuaSelectorFFIFunction = std::function<bool(dnsdist_ffi_dnsquestion_t* dq)>;
44
}
45

46
class MaxQPSIPRule : public DNSRule
47
{
48
public:
49
  MaxQPSIPRule(unsigned int qps, unsigned int ipv4trunc = 32, unsigned int ipv6trunc = 64, unsigned int burst = 0, unsigned int expiration = 300, unsigned int cleanupDelay = 60, unsigned int scanFraction = 10, size_t shardsCount = 10) :
50
    d_shards(shardsCount), d_qps(qps), d_burst(burst == 0 ? qps : burst), d_ipv4trunc(ipv4trunc), d_ipv6trunc(ipv6trunc), d_cleanupDelay(cleanupDelay), d_expiration(expiration), d_scanFraction(scanFraction)
3!
51
  {
3✔
52
    d_cleaningUp.clear();
3✔
53
    gettime(&d_lastCleanup, true);
3✔
54
  }
3✔
55

56
  void clear()
57
  {
3✔
58
    for (auto& shard : d_shards) {
3✔
59
      shard.lock()->clear();
3✔
60
    }
3✔
61
  }
3✔
62

63
  size_t cleanup(const struct timespec& cutOff, size_t* scannedCount = nullptr) const
64
  {
12✔
65
    size_t removed = 0;
12✔
66
    if (scannedCount != nullptr) {
12✔
67
      *scannedCount = 0;
9✔
68
    }
9✔
69

70
    for (auto& shard : d_shards) {
12✔
71
      auto limits = shard.lock();
12✔
72
      const size_t toLook = std::round((1.0 * limits->size()) / d_scanFraction) + 1;
12✔
73
      size_t lookedAt = 0;
12✔
74

75
      auto& sequence = limits->get<SequencedTag>();
12✔
76
      for (auto entry = sequence.begin(); entry != sequence.end() && lookedAt < toLook; lookedAt++) {
19,680✔
77
        if (entry->d_limiter.seenSince(cutOff)) {
19,671✔
78
          /* entries are ordered from least recently seen to more recently
79
             seen, as soon as we see one that has not expired yet, we are
80
             done */
81
          lookedAt++;
3✔
82
          break;
3✔
83
        }
3✔
84

85
        entry = sequence.erase(entry);
19,668✔
86
        removed++;
19,668✔
87
      }
19,668✔
88

89
      if (scannedCount != nullptr) {
12✔
90
        *scannedCount += lookedAt;
9✔
91
      }
9✔
92
    }
12✔
93

94
    return removed;
12✔
95
  }
12✔
96

97
  void cleanupIfNeeded(const struct timespec& now) const
98
  {
196,644✔
99
    if (d_cleanupDelay > 0) {
196,644!
100
      struct timespec cutOff = d_lastCleanup;
196,644✔
101
      cutOff.tv_sec += d_cleanupDelay;
196,644✔
102

103
      if (cutOff < now) {
196,644!
104
        try {
×
105
          if (d_cleaningUp.test_and_set()) {
×
106
            return;
×
107
          }
×
108

109
          d_lastCleanup = now;
×
110
          /* the QPS Limiter doesn't use realtime, be careful! */
111
          gettime(&cutOff, false);
×
112
          cutOff.tv_sec -= d_expiration;
×
113

114
          cleanup(cutOff);
×
115
          d_cleaningUp.clear();
×
116
        }
×
117
        catch (...) {
×
118
          d_cleaningUp.clear();
×
119
          throw;
×
120
        }
×
121
      }
×
122
    }
196,644✔
123
  }
196,644✔
124

125
  bool matches(const DNSQuestion* dq) const override
126
  {
196,644✔
127
    cleanupIfNeeded(dq->getQueryRealTime());
196,644✔
128

129
    ComboAddress zeroport(dq->ids.origRemote);
196,644✔
130
    zeroport.sin4.sin_port = 0;
196,644✔
131
    zeroport.truncate(zeroport.sin4.sin_family == AF_INET ? d_ipv4trunc : d_ipv6trunc);
196,644!
132
    auto hash = ComboAddress::addressOnlyHash()(zeroport);
196,644✔
133
    auto& shard = d_shards[hash % d_shards.size()];
196,644✔
134
    {
196,644✔
135
      auto limits = shard.lock();
196,644✔
136
      auto iter = limits->find(zeroport);
196,644✔
137
      if (iter == limits->end()) {
196,644✔
138
        Entry e(zeroport, QPSLimiter(d_qps, d_burst));
196,614✔
139
        iter = limits->insert(e).first;
196,614✔
140
      }
196,614✔
141

142
      moveCacheItemToBack<SequencedTag>(*limits, iter);
196,644✔
143
      return !iter->d_limiter.check(d_qps, d_burst);
196,644✔
144
    }
196,644✔
145
  }
196,644✔
146

147
  string toString() const override
148
  {
×
149
    return "IP (/" + std::to_string(d_ipv4trunc) + ", /" + std::to_string(d_ipv6trunc) + ") match for QPS over " + std::to_string(d_qps) + " burst " + std::to_string(d_burst);
×
150
  }
×
151

152
  size_t getEntriesCount() const
153
  {
51✔
154
    size_t count = 0;
51✔
155
    for (auto& shard : d_shards) {
51✔
156
      count += shard.lock()->size();
51✔
157
    }
51✔
158
    return count;
51✔
159
  }
51✔
160

161
  size_t getNumberOfShards() const
162
  {
6✔
163
    return d_shards.size();
6✔
164
  }
6✔
165

166
private:
167
  struct HashedTag
168
  {
169
  };
170
  struct SequencedTag
171
  {
172
  };
173
  struct Entry
174
  {
175
    Entry(const ComboAddress& addr, BasicQPSLimiter&& limiter) :
176
      d_limiter(limiter), d_addr(addr)
196,614✔
177
    {
196,614✔
178
    }
196,614✔
179
    mutable BasicQPSLimiter d_limiter;
180
    ComboAddress d_addr;
181
  };
182

183
  using qpsContainer_t = multi_index_container<
184
    Entry,
185
    indexed_by<
186
      hashed_unique<tag<HashedTag>, member<Entry, ComboAddress, &Entry::d_addr>, ComboAddress::addressOnlyHash>,
187
      sequenced<tag<SequencedTag>>>>;
188

189
  mutable std::vector<LockGuarded<qpsContainer_t>> d_shards;
190
  mutable struct timespec d_lastCleanup;
191
  const unsigned int d_qps, d_burst, d_ipv4trunc, d_ipv6trunc, d_cleanupDelay, d_expiration;
192
  const unsigned int d_scanFraction{10};
193
  mutable std::atomic_flag d_cleaningUp;
194
};
195

196
class MaxQPSRule : public DNSRule
197
{
198
public:
199
  MaxQPSRule(unsigned int qps) :
200
    d_qps(qps, qps)
201
  {
×
202
  }
×
203

204
  MaxQPSRule(unsigned int qps, unsigned int burst) :
205
    d_qps(qps, burst > 0 ? burst : qps)
4!
206
  {
4✔
207
  }
4✔
208

209
  bool matches(const DNSQuestion* dnsQuestion) const override
210
  {
4✔
211
    (void)dnsQuestion;
4✔
212
    return d_qps.check();
4✔
213
  }
4✔
214

215
  string toString() const override
216
  {
×
217
    return "Max " + std::to_string(d_qps.getRate()) + " qps";
×
218
  }
×
219

220
private:
221
  mutable QPSLimiter d_qps;
222
};
223

224
class NetmaskGroupRule : public DNSRule
225
{
226
public:
227
  NetmaskGroupRule(const NetmaskGroup& nmg, bool src, bool quiet = false) :
228
    d_nmg(nmg)
14✔
229
  {
14✔
230
    d_src = src;
14✔
231
    d_quiet = quiet;
14✔
232
  }
14✔
233
  bool matches(const DNSQuestion* dq) const override
234
  {
14✔
235
    if (!d_src) {
14!
236
      return d_nmg.match(dq->ids.origDest);
×
237
    }
×
238
    return d_nmg.match(dq->ids.origRemote);
14✔
239
  }
14✔
240

241
  string toString() const override
242
  {
×
243
    string ret = "Src: ";
×
244
    if (!d_src) {
×
245
      ret = "Dst: ";
×
246
    }
×
247
    if (d_quiet) {
×
248
      return ret + "in-group";
×
249
    }
×
250
    return ret + d_nmg.toString();
×
251
  }
×
252

253
private:
254
  NetmaskGroup d_nmg;
255
  bool d_src;
256
  bool d_quiet;
257
};
258

259
class TimedIPSetRule : public DNSRule, boost::noncopyable
260
{
261
private:
262
  struct IPv6
263
  {
264
    IPv6(const ComboAddress& ca)
265
    {
×
266
      static_assert(sizeof(*this) == 16, "IPv6 struct has wrong size");
×
267
      memcpy((char*)this, ca.sin6.sin6_addr.s6_addr, 16);
×
268
    }
×
269
    bool operator==(const IPv6& rhs) const
270
    {
×
271
      return a == rhs.a && b == rhs.b;
×
272
    }
×
273
    uint64_t a, b;
274
  };
275

276
public:
277
  TimedIPSetRule()
278
  {
4✔
279
  }
4✔
280
  ~TimedIPSetRule()
281
  {
1✔
282
  }
1✔
283
  bool matches(const DNSQuestion* dq) const override
284
  {
12✔
285
    if (dq->ids.origRemote.sin4.sin_family == AF_INET) {
12!
286
      auto ip4s = d_ip4s.read_lock();
12✔
287
      auto fnd = ip4s->find(dq->ids.origRemote.sin4.sin_addr.s_addr);
12✔
288
      if (fnd == ip4s->end()) {
12✔
289
        return false;
4✔
290
      }
4✔
291
      return time(nullptr) < fnd->second;
8✔
292
    }
12✔
293
    else {
×
294
      auto ip6s = d_ip6s.read_lock();
×
295
      auto fnd = ip6s->find({dq->ids.origRemote});
×
296
      if (fnd == ip6s->end()) {
×
297
        return false;
×
298
      }
×
299
      return time(nullptr) < fnd->second;
×
300
    }
×
301
  }
12✔
302

303
  void add(const ComboAddress& ca, time_t ttd)
304
  {
2✔
305
    // think twice before adding templates here
306
    if (ca.sin4.sin_family == AF_INET) {
2!
307
      auto res = d_ip4s.write_lock()->insert({ca.sin4.sin_addr.s_addr, ttd});
2✔
308
      if (!res.second && (time_t)res.first->second < ttd) {
2!
309
        res.first->second = (uint32_t)ttd;
×
310
      }
×
311
    }
2✔
312
    else {
×
313
      auto res = d_ip6s.write_lock()->insert({{ca}, ttd});
×
314
      if (!res.second && (time_t)res.first->second < ttd) {
×
315
        // coverity[store_truncates_time_t]
316
        res.first->second = (uint32_t)ttd;
×
317
      }
×
318
    }
×
319
  }
2✔
320

321
  void remove(const ComboAddress& ca)
322
  {
×
323
    if (ca.sin4.sin_family == AF_INET) {
×
324
      d_ip4s.write_lock()->erase(ca.sin4.sin_addr.s_addr);
×
325
    }
×
326
    else {
×
327
      d_ip6s.write_lock()->erase({ca});
×
328
    }
×
329
  }
×
330

331
  void clear()
332
  {
×
333
    d_ip4s.write_lock()->clear();
×
334
    d_ip6s.write_lock()->clear();
×
335
  }
×
336

337
  void cleanup()
338
  {
×
339
    time_t now = time(nullptr);
×
340
    {
×
341
      auto ip4s = d_ip4s.write_lock();
×
342
      for (auto iter = ip4s->begin(); iter != ip4s->end();) {
×
343
        if (iter->second < now) {
×
344
          iter = ip4s->erase(iter);
×
345
        }
×
346
        else {
×
347
          ++iter;
×
348
        }
×
349
      }
×
350
    }
×
351

352
    {
×
353
      auto ip6s = d_ip6s.write_lock();
×
354
      for (auto iter = ip6s->begin(); iter != ip6s->end();) {
×
355
        if (iter->second < now) {
×
356
          iter = ip6s->erase(iter);
×
357
        }
×
358
        else {
×
359
          ++iter;
×
360
        }
×
361
      }
×
362
    }
×
363
  }
×
364

365
  string toString() const override
366
  {
×
367
    time_t now = time(nullptr);
×
368
    uint64_t count = 0;
×
369

370
    for (const auto& ip : *(d_ip4s.read_lock())) {
×
371
      if (now < ip.second) {
×
372
        ++count;
×
373
      }
×
374
    }
×
375

376
    for (const auto& ip : *(d_ip6s.read_lock())) {
×
377
      if (now < ip.second) {
×
378
        ++count;
×
379
      }
×
380
    }
×
381

382
    return "Src: " + std::to_string(count) + " ips";
×
383
  }
×
384

385
private:
386
  struct IPv6Hash
387
  {
388
    std::size_t operator()(const IPv6& ip) const
389
    {
×
390
      auto ah = std::hash<uint64_t>{}(ip.a);
×
391
      auto bh = std::hash<uint64_t>{}(ip.b);
×
392
      return ah & (bh << 1);
×
393
    }
×
394
  };
395
  mutable SharedLockGuarded<std::unordered_map<IPv6, time_t, IPv6Hash>> d_ip6s;
396
  mutable SharedLockGuarded<std::unordered_map<uint32_t, time_t>> d_ip4s;
397
};
398

399
class AllRule : public DNSRule
400
{
401
public:
402
  AllRule() {}
315✔
403
  bool matches(const DNSQuestion* dnsQuestion) const override
404
  {
1,494✔
405
    (void)dnsQuestion;
1,494✔
406
    return true;
1,494✔
407
  }
1,494✔
408

409
  string toString() const override
410
  {
4✔
411
    return "All";
4✔
412
  }
4✔
413
};
414

415
class DNSSECRule : public DNSRule
416
{
417
public:
418
  DNSSECRule()
419
  {
2✔
420
  }
2✔
421
  bool matches(const DNSQuestion* dq) const override
422
  {
4✔
423
    return dq->getHeader()->cd || (dnsdist::getEDNSZ(*dq) & EDNS_HEADER_FLAG_DO); // turns out dig sets ad by default..
4!
424
  }
4✔
425

426
  string toString() const override
427
  {
×
428
    return "DNSSEC";
×
429
  }
×
430
};
431

432
class AndRule : public DNSRule
433
{
434
public:
435
  AndRule(const std::vector<std::shared_ptr<DNSRule>>& rules) :
436
    d_rules(rules)
64✔
437
  {
64✔
438
  }
64✔
439

440
  bool matches(const DNSQuestion* dq) const override
441
  {
271✔
442
    for (const auto& rule : d_rules) {
380✔
443
      if (!rule->matches(dq)) {
380✔
444
        return false;
217✔
445
      }
217✔
446
    }
380✔
447
    return true;
54✔
448
  }
271✔
449

450
  string toString() const override
451
  {
×
452
    string ret;
×
453
    for (const auto& rule : d_rules) {
×
454
      if (!ret.empty()) {
×
455
        ret += " && ";
×
456
      }
×
457
      ret += "(" + rule->toString() + ")";
×
458
    }
×
459
    return ret;
×
460
  }
×
461

462
private:
463
  std::vector<std::shared_ptr<DNSRule>> d_rules;
464
};
465

466
class OrRule : public DNSRule
467
{
468
public:
469
  OrRule(const std::vector<std::shared_ptr<DNSRule>>& rules) :
470
    d_rules(rules)
2✔
471
  {
2✔
472
  }
2✔
473

474
  bool matches(const DNSQuestion* dq) const override
475
  {
4✔
476
    for (const auto& rule : d_rules) {
6✔
477
      if (rule->matches(dq)) {
6✔
478
        return true;
3✔
479
      }
3✔
480
    }
6✔
481
    return false;
1✔
482
  }
4✔
483

484
  string toString() const override
485
  {
×
486
    string ret;
×
487
    for (const auto& rule : d_rules) {
×
488
      if (!ret.empty()) {
×
489
        ret += " || ";
×
490
      }
×
491
      ret += "(" + rule->toString() + ")";
×
492
    }
×
493
    return ret;
×
494
  }
×
495

496
private:
497
  std::vector<std::shared_ptr<DNSRule>> d_rules;
498
};
499

500
class RegexRule : public DNSRule
501
{
502
public:
503
  RegexRule(const std::string& regex) :
504
    d_visual(regex)
3✔
505
  {
3✔
506
    try {
3✔
507
      d_regex = Regex(regex);
3✔
508
    }
3✔
509
    catch (const PDNSException& exp) {
3✔
510
      throw std::runtime_error("Error compiling expression in RegexRule: " + exp.reason);
×
511
    }
×
512
  }
3✔
513
  bool matches(const DNSQuestion* dq) const override
514
  {
33✔
515
    return d_regex->match(dq->ids.qname.toStringNoDot());
33✔
516
  }
33✔
517

518
  string toString() const override
519
  {
×
520
    return "Regex: " + d_visual;
×
521
  }
×
522

523
private:
524
  std::optional<Regex> d_regex{std::nullopt};
525
  string d_visual;
526
};
527

528
#if defined(HAVE_RE2)
529
#include <re2/re2.h>
530
class RE2Rule : public DNSRule
531
{
532
public:
533
  RE2Rule(const std::string& re2) :
534
    d_re2(re2, RE2::Latin1), d_visual(re2)
2✔
535
  {
2✔
536
  }
2✔
537
  bool matches(const DNSQuestion* dq) const override
538
  {
4✔
539
    return RE2::FullMatch(dq->ids.qname.toStringNoDot(), d_re2);
4✔
540
  }
4✔
541

542
  string toString() const override
543
  {
544
    return "RE2 match: " + d_visual;
545
  }
546

547
private:
548
  RE2 d_re2;
549
  string d_visual;
550
};
551
#else /* HAVE_RE2 */
552
class RE2Rule : public DNSRule
553
{
554
public:
555
  RE2Rule(const std::string& /* re2 */)
556
  {
557
    throw std::runtime_error("RE2 support is disabled");
558
  }
559
  bool matches(const DNSQuestion* /* dq */) const override
560
  {
561
    return false;
562
  }
563

564
  string toString() const override
565
  {
566
    return "Unsupported RE2";
567
  }
568
};
569
#endif /* HAVE_RE2 */
570

571
class HTTPHeaderRule : public DNSRule
572
{
573
public:
574
  HTTPHeaderRule(const std::string& header, const std::string& regex);
575
  bool matches(const DNSQuestion* dnsQuestion) const override;
576
  string toString() const override;
577

578
private:
579
  string d_header;
580
  std::optional<Regex> d_regex{std::nullopt};
581
  string d_visual;
582
};
583

584
class HTTPPathRule : public DNSRule
585
{
586
public:
587
  HTTPPathRule(std::string path);
588
  bool matches(const DNSQuestion* dnsQuestion) const override;
589
  string toString() const override;
590

591
private:
592
  string d_path;
593
};
594

595
class HTTPPathRegexRule : public DNSRule
596
{
597
public:
598
  HTTPPathRegexRule(const std::string& regex);
599
  bool matches(const DNSQuestion* dnsQuestion) const override;
600
  string toString() const override;
601

602
private:
603
  std::optional<Regex> d_regex{std::nullopt};
604
  std::string d_visual;
605
};
606

607
class SNIRule : public DNSRule
608
{
609
public:
610
  SNIRule(const std::string& name) :
611
    d_sni(name)
8✔
612
  {
8✔
613
  }
8✔
614
  bool matches(const DNSQuestion* dq) const override
615
  {
448✔
616
    return dq->sni == d_sni;
448✔
617
  }
448✔
618
  string toString() const override
619
  {
×
620
    return "SNI == " + d_sni;
×
621
  }
×
622

623
private:
624
  std::string d_sni;
625
};
626

627
class SuffixMatchNodeRule : public DNSRule
628
{
629
public:
630
  SuffixMatchNodeRule(const SuffixMatchNode& smn, bool quiet = false) :
631
    d_smn(smn), d_quiet(quiet)
433✔
632
  {
433✔
633
  }
433✔
634
  bool matches(const DNSQuestion* dq) const override
635
  {
4,275✔
636
    return d_smn.check(dq->ids.qname);
4,275✔
637
  }
4,275✔
638
  string toString() const override
639
  {
537✔
640
    if (d_quiet)
537!
641
      return "qname==in-set";
×
642
    else
537✔
643
      return "qname in " + d_smn.toString();
537✔
644
  }
537✔
645

646
private:
647
  SuffixMatchNode d_smn;
648
  bool d_quiet;
649
};
650

651
class QNameRule : public DNSRule
652
{
653
public:
654
  QNameRule(const DNSName& qname) :
655
    d_qname(qname)
60✔
656
  {
60✔
657
  }
60✔
658

659
  bool matches(const DNSQuestion* dq) const override
660
  {
341✔
661
    return d_qname == dq->ids.qname;
341✔
662
  }
341✔
663
  string toString() const override
664
  {
×
665
    return "qname==" + d_qname.toString();
×
666
  }
×
667

668
private:
669
  DNSName d_qname;
670
};
671

672
class QNameSetRule : public DNSRule
673
{
674
public:
675
  QNameSetRule(const DNSNameSet& names) :
676
    qname_idx(names) {}
10✔
677

678
  bool matches(const DNSQuestion* dq) const override
679
  {
18✔
680
    return qname_idx.find(dq->ids.qname) != qname_idx.end();
18✔
681
  }
18✔
682

683
  string toString() const override
684
  {
×
685
    std::stringstream ss;
×
686
    ss << "qname in DNSNameSet(" << qname_idx.size() << " FQDNs)";
×
687
    return ss.str();
×
688
  }
×
689

690
private:
691
  DNSNameSet qname_idx;
692
};
693

694
class QTypeRule : public DNSRule
695
{
696
public:
697
  QTypeRule(uint16_t qtype) :
698
    d_qtype(qtype)
56✔
699
  {
56✔
700
  }
56✔
701
  bool matches(const DNSQuestion* dq) const override
702
  {
233✔
703
    return d_qtype == dq->ids.qtype;
233✔
704
  }
233✔
705
  string toString() const override
706
  {
×
707
    QType qt(d_qtype);
×
708
    return "qtype==" + qt.toString();
×
709
  }
×
710

711
private:
712
  uint16_t d_qtype;
713
};
714

715
class QClassRule : public DNSRule
716
{
717
public:
718
  QClassRule(uint16_t qclass) :
719
    d_qclass(qclass)
4✔
720
  {
4✔
721
  }
4✔
722
  bool matches(const DNSQuestion* dq) const override
723
  {
6✔
724
    return d_qclass == dq->ids.qclass;
6✔
725
  }
6✔
726
  string toString() const override
727
  {
×
728
    return "qclass==" + std::to_string(d_qclass);
×
729
  }
×
730

731
private:
732
  uint16_t d_qclass;
733
};
734

735
class OpcodeRule : public DNSRule
736
{
737
public:
738
  OpcodeRule(uint8_t opcode) :
739
    d_opcode(opcode)
4✔
740
  {
4✔
741
  }
4✔
742
  bool matches(const DNSQuestion* dq) const override
743
  {
8✔
744
    return d_opcode == dq->getHeader()->opcode;
8✔
745
  }
8✔
746
  string toString() const override
747
  {
×
748
    return "opcode==" + std::to_string(d_opcode);
×
749
  }
×
750

751
private:
752
  uint8_t d_opcode;
753
};
754

755
class DSTPortRule : public DNSRule
756
{
757
public:
758
  DSTPortRule(uint16_t port) :
759
    d_port(port)
2✔
760
  {
2✔
761
  }
2✔
762
  bool matches(const DNSQuestion* dq) const override
763
  {
2✔
764
    return htons(d_port) == dq->ids.origDest.sin4.sin_port;
2✔
765
  }
2✔
766
  string toString() const override
767
  {
×
768
    return "dst port==" + std::to_string(d_port);
×
769
  }
×
770

771
private:
772
  uint16_t d_port;
773
};
774

775
class TCPRule : public DNSRule
776
{
777
public:
778
  TCPRule(bool tcp) :
779
    d_tcp(tcp)
7✔
780
  {
7✔
781
  }
7✔
782
  bool matches(const DNSQuestion* dq) const override
783
  {
11✔
784
    return dq->overTCP() == d_tcp;
11✔
785
  }
11✔
786
  string toString() const override
787
  {
×
788
    return (d_tcp ? "TCP" : "UDP");
×
789
  }
×
790

791
private:
792
  bool d_tcp;
793
};
794

795
class NotRule : public DNSRule
796
{
797
public:
798
  NotRule(const std::shared_ptr<DNSRule>& rule) :
799
    d_rule(rule)
30✔
800
  {
30✔
801
  }
30✔
802
  bool matches(const DNSQuestion* dq) const override
803
  {
105✔
804
    return !d_rule->matches(dq);
105✔
805
  }
105✔
806
  string toString() const override
807
  {
×
808
    return "!(" + d_rule->toString() + ")";
×
809
  }
×
810

811
private:
812
  std::shared_ptr<DNSRule> d_rule;
813
};
814

815
class RecordsCountRule : public DNSRule
816
{
817
public:
818
  RecordsCountRule(uint8_t section, uint16_t minCount, uint16_t maxCount) :
819
    d_minCount(minCount), d_maxCount(maxCount), d_section(section)
6✔
820
  {
6✔
821
  }
6✔
822
  bool matches(const DNSQuestion* dq) const override
823
  {
16✔
824
    uint16_t count = 0;
16✔
825
    switch (d_section) {
16!
826
    case 0:
×
827
      count = ntohs(dq->getHeader()->qdcount);
×
828
      break;
×
829
    case 1:
6✔
830
      count = ntohs(dq->getHeader()->ancount);
6✔
831
      break;
6✔
832
    case 2:
4✔
833
      count = ntohs(dq->getHeader()->nscount);
4✔
834
      break;
4✔
835
    case 3:
6✔
836
      count = ntohs(dq->getHeader()->arcount);
6✔
837
      break;
6✔
838
    }
16✔
839
    return count >= d_minCount && count <= d_maxCount;
16✔
840
  }
16✔
841
  string toString() const override
842
  {
×
843
    string section;
×
844
    switch (d_section) {
×
845
    case 0:
×
846
      section = "QD";
×
847
      break;
×
848
    case 1:
×
849
      section = "AN";
×
850
      break;
×
851
    case 2:
×
852
      section = "NS";
×
853
      break;
×
854
    case 3:
×
855
      section = "AR";
×
856
      break;
×
857
    }
×
858
    return std::to_string(d_minCount) + " <= records in " + section + " <= " + std::to_string(d_maxCount);
×
859
  }
×
860

861
private:
862
  uint16_t d_minCount;
863
  uint16_t d_maxCount;
864
  uint8_t d_section;
865
};
866

867
class RecordsTypeCountRule : public DNSRule
868
{
869
public:
870
  RecordsTypeCountRule(uint8_t section, uint16_t type, uint16_t minCount, uint16_t maxCount) :
871
    d_type(type), d_minCount(minCount), d_maxCount(maxCount), d_section(section)
2✔
872
  {
2✔
873
  }
2✔
874
  bool matches(const DNSQuestion* dq) const override
875
  {
6✔
876
    uint16_t count = 0;
6✔
877
    switch (d_section) {
6!
878
    case 0:
×
879
      count = ntohs(dq->getHeader()->qdcount);
×
880
      break;
×
881
    case 1:
×
882
      count = ntohs(dq->getHeader()->ancount);
×
883
      break;
×
884
    case 2:
×
885
      count = ntohs(dq->getHeader()->nscount);
×
886
      break;
×
887
    case 3:
6!
888
      count = ntohs(dq->getHeader()->arcount);
6✔
889
      break;
6✔
890
    }
6✔
891
    if (count < d_minCount) {
6!
892
      return false;
×
893
    }
×
894
    count = getRecordsOfTypeCount(reinterpret_cast<const char*>(dq->getData().data()), dq->getData().size(), d_section, d_type);
6✔
895
    return count >= d_minCount && count <= d_maxCount;
6!
896
  }
6✔
897
  string toString() const override
898
  {
×
899
    string section;
×
900
    switch (d_section) {
×
901
    case 0:
×
902
      section = "QD";
×
903
      break;
×
904
    case 1:
×
905
      section = "AN";
×
906
      break;
×
907
    case 2:
×
908
      section = "NS";
×
909
      break;
×
910
    case 3:
×
911
      section = "AR";
×
912
      break;
×
913
    }
×
914
    return std::to_string(d_minCount) + " <= " + QType(d_type).toString() + " records in " + section + " <= " + std::to_string(d_maxCount);
×
915
  }
×
916

917
private:
918
  uint16_t d_type;
919
  uint16_t d_minCount;
920
  uint16_t d_maxCount;
921
  uint8_t d_section;
922
};
923

924
class TrailingDataRule : public DNSRule
925
{
926
public:
927
  TrailingDataRule()
928
  {
2✔
929
  }
2✔
930
  bool matches(const DNSQuestion* dq) const override
931
  {
4✔
932
    uint16_t length = getDNSPacketLength(reinterpret_cast<const char*>(dq->getData().data()), dq->getData().size());
4✔
933
    return length < dq->getData().size();
4✔
934
  }
4✔
935
  string toString() const override
936
  {
×
937
    return "trailing data";
×
938
  }
×
939
};
940

941
class QNameLabelsCountRule : public DNSRule
942
{
943
public:
944
  QNameLabelsCountRule(unsigned int minLabelsCount, unsigned int maxLabelsCount) :
945
    d_min(minLabelsCount), d_max(maxLabelsCount)
2✔
946
  {
2✔
947
  }
2✔
948
  bool matches(const DNSQuestion* dq) const override
949
  {
6✔
950
    unsigned int count = dq->ids.qname.countLabels();
6✔
951
    return count < d_min || count > d_max;
6✔
952
  }
6✔
953
  string toString() const override
954
  {
×
955
    return "labels count < " + std::to_string(d_min) + " || labels count > " + std::to_string(d_max);
×
956
  }
×
957

958
private:
959
  unsigned int d_min;
960
  unsigned int d_max;
961
};
962

963
class QNameWireLengthRule : public DNSRule
964
{
965
public:
966
  QNameWireLengthRule(size_t min, size_t max) :
967
    d_min(min), d_max(max)
2✔
968
  {
2✔
969
  }
2✔
970
  bool matches(const DNSQuestion* dq) const override
971
  {
6✔
972
    size_t const wirelength = dq->ids.qname.wirelength();
6✔
973
    return wirelength < d_min || wirelength > d_max;
6✔
974
  }
6✔
975
  string toString() const override
976
  {
×
977
    return "wire length < " + std::to_string(d_min) + " || wire length > " + std::to_string(d_max);
×
978
  }
×
979

980
private:
981
  size_t d_min;
982
  size_t d_max;
983
};
984

985
class RCodeRule : public DNSRule
986
{
987
public:
988
  RCodeRule(uint8_t rcode) :
989
    d_rcode(rcode)
10✔
990
  {
10✔
991
  }
10✔
992
  bool matches(const DNSQuestion* dq) const override
993
  {
806✔
994
    return d_rcode == dq->getHeader()->rcode;
806✔
995
  }
806✔
996
  string toString() const override
997
  {
×
998
    return "rcode==" + RCode::to_s(d_rcode);
×
999
  }
×
1000

1001
private:
1002
  uint8_t d_rcode;
1003
};
1004

1005
class ERCodeRule : public DNSRule
1006
{
1007
public:
1008
  ERCodeRule(uint8_t rcode) :
1009
    d_rcode(rcode & 0xF), d_extrcode(rcode >> 4)
2✔
1010
  {
2✔
1011
  }
2✔
1012
  bool matches(const DNSQuestion* dq) const override
1013
  {
3✔
1014
    // avoid parsing EDNS OPT RR when not needed.
1015
    if (d_rcode != dq->getHeader()->rcode) {
3✔
1016
      return false;
1✔
1017
    }
1✔
1018

1019
    EDNS0Record edns0;
2✔
1020
    if (!getEDNS0Record(dq->getData(), edns0)) {
2!
1021
      return false;
×
1022
    }
×
1023

1024
    return d_extrcode == edns0.extRCode;
2✔
1025
  }
2✔
1026
  string toString() const override
1027
  {
×
1028
    return "ercode==" + ERCode::to_s(d_rcode | (d_extrcode << 4));
×
1029
  }
×
1030

1031
private:
1032
  uint8_t d_rcode; // plain DNS Rcode
1033
  uint8_t d_extrcode; // upper bits in EDNS0 record
1034
};
1035

1036
class EDNSVersionRule : public DNSRule
1037
{
1038
public:
1039
  EDNSVersionRule(uint8_t version) :
1040
    d_version(version)
2✔
1041
  {
2✔
1042
  }
2✔
1043
  bool matches(const DNSQuestion* dq) const override
1044
  {
4✔
1045
    EDNS0Record edns0;
4✔
1046
    if (!getEDNS0Record(dq->getData(), edns0)) {
4✔
1047
      return false;
2✔
1048
    }
2✔
1049

1050
    return d_version < edns0.version;
2✔
1051
  }
4✔
1052
  string toString() const override
1053
  {
×
1054
    return "ednsversion>" + std::to_string(d_version);
×
1055
  }
×
1056

1057
private:
1058
  uint8_t d_version;
1059
};
1060

1061
class EDNSOptionRule : public DNSRule
1062
{
1063
public:
1064
  EDNSOptionRule(uint16_t optcode) :
1065
    d_optcode(optcode)
2✔
1066
  {
2✔
1067
  }
2✔
1068
  bool matches(const DNSQuestion* dq) const override
1069
  {
6✔
1070
    uint16_t optStart;
6✔
1071
    size_t optLen = 0;
6✔
1072
    bool last = false;
6✔
1073
    int res = locateEDNSOptRR(dq->getData(), &optStart, &optLen, &last);
6✔
1074
    if (res != 0) {
6✔
1075
      // no EDNS OPT RR
1076
      return false;
2✔
1077
    }
2✔
1078

1079
    if (optLen < optRecordMinimumSize) {
4!
1080
      return false;
×
1081
    }
×
1082

1083
    if (optStart < dq->getData().size() && dq->getData().at(optStart) != 0) {
4!
1084
      // OPT RR Name != '.'
1085
      return false;
×
1086
    }
×
1087

1088
    return isEDNSOptionInOpt(dq->getData(), optStart, optLen, d_optcode);
4✔
1089
  }
4✔
1090
  string toString() const override
1091
  {
×
1092
    return "ednsoptcode==" + std::to_string(d_optcode);
×
1093
  }
×
1094

1095
private:
1096
  uint16_t d_optcode;
1097
};
1098

1099
class RDRule : public DNSRule
1100
{
1101
public:
1102
  RDRule()
1103
  {
12✔
1104
  }
12✔
1105
  bool matches(const DNSQuestion* dq) const override
1106
  {
65✔
1107
    return dq->getHeader()->rd == 1;
65✔
1108
  }
65✔
1109
  string toString() const override
1110
  {
×
1111
    return "rd==1";
×
1112
  }
×
1113
};
1114

1115
class ProbaRule : public DNSRule
1116
{
1117
public:
1118
  ProbaRule(double proba) :
1119
    d_proba(proba)
×
1120
  {
×
1121
  }
×
1122
  bool matches(const DNSQuestion* dnsQuestion) const override
1123
  {
×
1124
    (void)dnsQuestion;
×
1125
    if (d_proba == 1.0) {
×
1126
      return true;
×
1127
    }
×
1128
    double rnd = 1.0 * dns_random_uint32() / UINT32_MAX;
×
1129
    return rnd > (1.0 - d_proba);
×
1130
  }
×
1131
  string toString() const override
1132
  {
×
1133
    return "match with prob. " + (boost::format("%0.2f") % d_proba).str();
×
1134
  }
×
1135

1136
private:
1137
  double d_proba;
1138
};
1139

1140
class TagRule : public DNSRule
1141
{
1142
public:
1143
  TagRule(const std::string& tag, boost::optional<std::string> value, bool emptyAsWildcard) :
1144
    d_value(std::move(value)), d_tag(tag), d_emptyAsWildcard(emptyAsWildcard)
66✔
1145
  {
66✔
1146
  }
66✔
1147
  bool matches(const DNSQuestion* dq) const override
1148
  {
198✔
1149
    if (!dq->ids.qTag) {
198✔
1150
      return false;
48✔
1151
    }
48✔
1152

1153
    const auto it = dq->ids.qTag->find(d_tag);
150✔
1154
    if (it == dq->ids.qTag->cend()) {
150✔
1155
      return false;
44✔
1156
    }
44✔
1157

1158
    if (!d_value || (d_emptyAsWildcard && d_value->empty())) {
106!
1159
      return true;
4✔
1160
    }
4✔
1161

1162
    return it->second == *d_value;
102✔
1163
  }
106✔
1164

1165
  string toString() const override
1166
  {
×
1167
    return "tag '" + d_tag + "' is set" + ((d_value && (!d_value->empty() || !d_emptyAsWildcard)) ? (" to '" + *d_value + "'") : "");
×
1168
  }
×
1169

1170
private:
1171
  boost::optional<std::string> d_value;
1172
  std::string d_tag;
1173
  bool d_emptyAsWildcard;
1174
};
1175

1176
class PoolAvailableRule : public DNSRule
1177
{
1178
public:
1179
  PoolAvailableRule(const std::string& poolname) :
1180
    d_poolname(poolname)
×
1181
  {
×
1182
  }
×
1183

1184
  bool matches(const DNSQuestion* dnsQuestion) const override
1185
  {
×
1186
    (void)dnsQuestion;
×
1187
    return (getPool(d_poolname).countServers(true) > 0);
×
1188
  }
×
1189

1190
  string toString() const override
1191
  {
×
1192
    return "pool '" + d_poolname + "' is available";
×
1193
  }
×
1194

1195
private:
1196
  std::string d_poolname;
1197
};
1198

1199
class PoolOutstandingRule : public DNSRule
1200
{
1201
public:
1202
  PoolOutstandingRule(const std::string& poolname, const size_t limit) :
1203
    d_poolname(poolname), d_limit(limit)
6✔
1204
  {
6✔
1205
  }
6✔
1206

1207
  bool matches(const DNSQuestion* dnsQuestion) const override
1208
  {
6✔
1209
    (void)dnsQuestion;
6✔
1210
    return (getPool(d_poolname).poolLoad()) > d_limit;
6✔
1211
  }
6✔
1212

1213
  string toString() const override
1214
  {
×
1215
    return "pool '" + d_poolname + "' outstanding > " + std::to_string(d_limit);
×
1216
  }
×
1217

1218
private:
1219
  std::string d_poolname;
1220
  size_t d_limit;
1221
};
1222

1223
class KeyValueStoreLookupRule : public DNSRule
1224
{
1225
public:
1226
  KeyValueStoreLookupRule(const std::shared_ptr<KeyValueStore>& kvs, const std::shared_ptr<KeyValueLookupKey>& lookupKey) :
1227
    d_kvs(kvs), d_key(lookupKey)
4✔
1228
  {
4✔
1229
  }
4✔
1230

1231
  bool matches(const DNSQuestion* dq) const override
1232
  {
4✔
1233
    std::vector<std::string> keys = d_key->getKeys(*dq);
4✔
1234
    for (const auto& key : keys) {
4!
1235
      if (d_kvs->keyExists(key) == true) {
4!
1236
        return true;
4✔
1237
      }
4✔
1238
    }
4✔
1239

1240
    return false;
×
1241
  }
4✔
1242

1243
  string toString() const override
1244
  {
×
1245
    return "lookup key-value store based on '" + d_key->toString() + "'";
×
1246
  }
×
1247

1248
private:
1249
  std::shared_ptr<KeyValueStore> d_kvs;
1250
  std::shared_ptr<KeyValueLookupKey> d_key;
1251
};
1252

1253
class KeyValueStoreRangeLookupRule : public DNSRule
1254
{
1255
public:
1256
  KeyValueStoreRangeLookupRule(const std::shared_ptr<KeyValueStore>& kvs, const std::shared_ptr<KeyValueLookupKey>& lookupKey) :
1257
    d_kvs(kvs), d_key(lookupKey)
4✔
1258
  {
4✔
1259
  }
4✔
1260

1261
  bool matches(const DNSQuestion* dq) const override
1262
  {
4✔
1263
    std::vector<std::string> keys = d_key->getKeys(*dq);
4✔
1264
    for (const auto& key : keys) {
4✔
1265
      std::string value;
4✔
1266
      if (d_kvs->getRangeValue(key, value) == true) {
4✔
1267
        return true;
2✔
1268
      }
2✔
1269
    }
4✔
1270

1271
    return false;
2✔
1272
  }
4✔
1273

1274
  string toString() const override
1275
  {
×
1276
    return "range-based lookup key-value store based on '" + d_key->toString() + "'";
×
1277
  }
×
1278

1279
private:
1280
  std::shared_ptr<KeyValueStore> d_kvs;
1281
  std::shared_ptr<KeyValueLookupKey> d_key;
1282
};
1283

1284
class LuaRule : public DNSRule
1285
{
1286
public:
1287
  LuaRule(const dnsdist::selectors::LuaSelectorFunction& func) :
1288
    d_func(func)
10✔
1289
  {}
10✔
1290

1291
  bool matches(const DNSQuestion* dq) const override
1292
  {
14✔
1293
    try {
14✔
1294
      auto lock = g_lua.lock();
14✔
1295
      return d_func(dq);
14✔
1296
    }
14✔
1297
    catch (const std::exception& e) {
14✔
1298
      warnlog("LuaRule failed inside Lua: %s", e.what());
×
1299
    }
×
1300
    catch (...) {
14✔
1301
      warnlog("LuaRule failed inside Lua: [unknown exception]");
×
1302
    }
×
1303
    return false;
×
1304
  }
14✔
1305

1306
  string toString() const override
1307
  {
×
1308
    return "Lua script";
×
1309
  }
×
1310

1311
private:
1312
  dnsdist::selectors::LuaSelectorFunction d_func;
1313
};
1314

1315
class LuaFFIRule : public DNSRule
1316
{
1317
public:
1318
  LuaFFIRule(const dnsdist::selectors::LuaSelectorFFIFunction& func) :
1319
    d_func(func)
2✔
1320
  {}
2✔
1321

1322
  bool matches(const DNSQuestion* dq) const override
1323
  {
4✔
1324
    dnsdist_ffi_dnsquestion_t dqffi(const_cast<DNSQuestion*>(dq));
4✔
1325
    try {
4✔
1326
      auto lock = g_lua.lock();
4✔
1327
      return d_func(&dqffi);
4✔
1328
    }
4✔
1329
    catch (const std::exception& e) {
4✔
1330
      warnlog("LuaFFIRule failed inside Lua: %s", e.what());
×
1331
    }
×
1332
    catch (...) {
4✔
1333
      warnlog("LuaFFIRule failed inside Lua: [unknown exception]");
×
1334
    }
×
1335
    return false;
×
1336
  }
4✔
1337

1338
  string toString() const override
1339
  {
×
1340
    return "Lua FFI script";
×
1341
  }
×
1342

1343
private:
1344
  dnsdist::selectors::LuaSelectorFFIFunction d_func;
1345
};
1346

1347
class LuaFFIPerThreadRule : public DNSRule
1348
{
1349
public:
1350
  LuaFFIPerThreadRule(const std::string& code) :
1351
    d_functionCode(code), d_functionID(s_functionsCounter++)
2✔
1352
  {
2✔
1353
  }
2✔
1354

1355
  bool matches(const DNSQuestion* dq) const override
1356
  {
4✔
1357
    try {
4✔
1358
      auto& state = t_perThreadStates[d_functionID];
4✔
1359
      if (!state.d_initialized) {
4✔
1360
        setupLuaFFIPerThreadContext(state.d_luaContext);
3✔
1361
        /* mark the state as initialized first so if there is a syntax error
1362
           we only try to execute the code once */
1363
        state.d_initialized = true;
3✔
1364
        state.d_func = state.d_luaContext.executeCode<dnsdist::selectors::LuaSelectorFFIFunction>(d_functionCode);
3✔
1365
      }
3✔
1366

1367
      if (!state.d_func) {
4!
1368
        /* the function was not properly initialized */
1369
        return false;
×
1370
      }
×
1371

1372
      dnsdist_ffi_dnsquestion_t dqffi(const_cast<DNSQuestion*>(dq));
4✔
1373
      return state.d_func(&dqffi);
4✔
1374
    }
4✔
1375
    catch (const std::exception& e) {
4✔
1376
      warnlog("LuaFFIPerthreadRule failed inside Lua: %s", e.what());
×
1377
    }
×
1378
    catch (...) {
4✔
1379
      warnlog("LuaFFIPerThreadRule failed inside Lua: [unknown exception]");
×
1380
    }
×
1381
    return false;
×
1382
  }
4✔
1383

1384
  string toString() const override
1385
  {
×
1386
    return "Lua FFI per-thread script";
×
1387
  }
×
1388

1389
private:
1390
  struct PerThreadState
1391
  {
1392
    LuaContext d_luaContext;
1393
    dnsdist::selectors::LuaSelectorFFIFunction d_func;
1394
    bool d_initialized{false};
1395
  };
1396

1397
  static std::atomic<uint64_t> s_functionsCounter;
1398
  static thread_local std::map<uint64_t, PerThreadState> t_perThreadStates;
1399
  const std::string d_functionCode;
1400
  const uint64_t d_functionID;
1401
};
1402

1403
class ProxyProtocolValueRule : public DNSRule
1404
{
1405
public:
1406
  ProxyProtocolValueRule(uint8_t type, boost::optional<std::string> value) :
1407
    d_value(std::move(value)), d_type(type)
4✔
1408
  {
4✔
1409
  }
4✔
1410

1411
  bool matches(const DNSQuestion* dq) const override
1412
  {
70✔
1413
    if (!dq->proxyProtocolValues) {
70!
1414
      return false;
×
1415
    }
×
1416

1417
    for (const auto& entry : *dq->proxyProtocolValues) {
105!
1418
      if (entry.type == d_type && (!d_value || d_value->empty() || entry.content == *d_value)) {
105!
1419
        return true;
70✔
1420
      }
70✔
1421
    }
105✔
1422

1423
    return false;
×
1424
  }
70✔
1425

1426
  string toString() const override
1427
  {
×
1428
    if (d_value) {
×
1429
      return "proxy protocol value of type " + std::to_string(d_type) + " matches";
×
1430
    }
×
1431
    return "proxy protocol value of type " + std::to_string(d_type) + " is present";
×
1432
  }
×
1433

1434
private:
1435
  boost::optional<std::string> d_value;
1436
  uint8_t d_type;
1437
};
1438

1439
class PayloadSizeRule : public DNSRule
1440
{
1441
  enum class Comparisons : uint8_t
1442
  {
1443
    equal,
1444
    greater,
1445
    greaterOrEqual,
1446
    smaller,
1447
    smallerOrEqual
1448
  };
1449

1450
public:
1451
  PayloadSizeRule(const std::string& comparison, uint16_t size) :
1452
    d_size(size)
47✔
1453
  {
47✔
1454
    if (comparison == "equal") {
47✔
1455
      d_comparison = Comparisons::equal;
8✔
1456
    }
8✔
1457
    else if (comparison == "greater") {
39✔
1458
      d_comparison = Comparisons::greater;
10✔
1459
    }
10✔
1460
    else if (comparison == "greaterOrEqual") {
29✔
1461
      d_comparison = Comparisons::greaterOrEqual;
9✔
1462
    }
9✔
1463
    else if (comparison == "smaller") {
20✔
1464
      d_comparison = Comparisons::smaller;
8✔
1465
    }
8✔
1466
    else if (comparison == "smallerOrEqual") {
12✔
1467
      d_comparison = Comparisons::smallerOrEqual;
9✔
1468
    }
9✔
1469
    else {
3✔
1470
      throw std::runtime_error("Unsupported comparison '" + comparison + "'");
3✔
1471
    }
3✔
1472
  }
47✔
1473

1474
  bool matches(const DNSQuestion* dq) const override
1475
  {
44✔
1476
    const auto size = dq->getData().size();
44✔
1477

1478
    switch (d_comparison) {
44✔
1479
    case Comparisons::equal:
8✔
1480
      return size == d_size;
8✔
1481
    case Comparisons::greater:
10✔
1482
      return size > d_size;
10✔
1483
    case Comparisons::greaterOrEqual:
9✔
1484
      return size >= d_size;
9✔
1485
    case Comparisons::smaller:
8✔
1486
      return size < d_size;
8✔
1487
    case Comparisons::smallerOrEqual:
9✔
1488
      return size <= d_size;
9✔
1489
    default:
×
1490
      return false;
×
1491
    }
44✔
1492
  }
44✔
1493

1494
  string toString() const override
1495
  {
15✔
1496
    static const std::array<const std::string, 5> comparisonStr{
15✔
1497
      "equal to",
15✔
1498
      "greater than",
15✔
1499
      "equal to or greater than",
15✔
1500
      "smaller than",
15✔
1501
      "equal to or smaller than"};
15✔
1502
    return "payload size is " + comparisonStr.at(static_cast<size_t>(d_comparison)) + " " + std::to_string(d_size);
15✔
1503
  }
15✔
1504

1505
private:
1506
  uint16_t d_size;
1507
  Comparisons d_comparison;
1508
};
1509

1510
class IncomingProtocolRule : public DNSRule
1511
{
1512
public:
1513
  IncomingProtocolRule(const std::string& protocol) :
1514
    d_protocol(protocol)
24✔
1515
  {
24✔
1516
  }
24✔
1517

1518
  bool matches(const DNSQuestion* dq) const override
1519
  {
42✔
1520
    return dq->getProtocol() == d_protocol;
42✔
1521
  }
42✔
1522

1523
  string toString() const override
1524
  {
×
1525
    return "incoming protocol is " + d_protocol.toString();
×
1526
  }
×
1527

1528
private:
1529
  dnsdist::Protocol d_protocol;
1530
};
1531

1532
namespace dnsdist::selectors
1533
{
1534
std::shared_ptr<AndRule> getAndSelector(const std::vector<std::shared_ptr<DNSRule>>& rules);
1535
std::shared_ptr<OrRule> getOrSelector(const std::vector<std::shared_ptr<DNSRule>>& rules);
1536
std::shared_ptr<NotRule> getNotSelector(const std::shared_ptr<DNSRule>& rule);
1537
std::shared_ptr<LuaRule> getLuaSelector(const dnsdist::selectors::LuaSelectorFunction& func);
1538
std::shared_ptr<LuaFFIRule> getLuaFFISelector(const dnsdist::selectors::LuaSelectorFFIFunction& func);
1539
std::shared_ptr<QNameRule> getQNameSelector(const DNSName& qname);
1540
std::shared_ptr<QNameSetRule> getQNameSetSelector(const DNSNameSet& qnames);
1541
std::shared_ptr<SuffixMatchNodeRule> getQNameSuffixSelector(const SuffixMatchNode& suffixes, bool quiet);
1542
std::shared_ptr<QTypeRule> getQTypeSelector(const std::string& qtypeStr, uint16_t qtypeCode);
1543
std::shared_ptr<QClassRule> getQClassSelector(const std::string& qclassStr, uint16_t qclassCode);
1544
std::shared_ptr<NetmaskGroupRule> getNetmaskGroupSelector(const NetmaskGroup& nmg, bool source, bool quiet);
1545
std::shared_ptr<KeyValueStoreLookupRule> getKeyValueStoreLookupSelector(const std::shared_ptr<KeyValueStore>& kvs, const std::shared_ptr<KeyValueLookupKey>& lookupKey);
1546
std::shared_ptr<KeyValueStoreRangeLookupRule> getKeyValueStoreRangeLookupSelector(const std::shared_ptr<KeyValueStore>& kvs, const std::shared_ptr<KeyValueLookupKey>& lookupKey);
1547

1548
#include "dnsdist-selectors-factory-generated.hh"
1549
}
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