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

PowerDNS / pdns / 18937558685

30 Oct 2025 10:27AM UTC coverage: 73.04% (+0.03%) from 73.014%
18937558685

Pull #16395

github

web-flow
Merge 3b9be1959 into 9769584db
Pull Request #16395: dnsdist: Add option to use incoming OpenTelemetry Trace ID

38333 of 63186 branches covered (60.67%)

Branch coverage included in aggregate %.

109 of 120 new or added lines in 5 files covered. (90.83%)

64 existing lines in 15 files now uncovered.

127583 of 163972 relevant lines covered (77.81%)

5996675.21 hits per line

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

61.36
/pdns/dnsdistdist/dnsdist-actions-factory.cc
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
#include <memory>
23
#include <optional>
24
#include <unordered_map>
25
#include <utility>
26

27
#include "dnsdist-actions-factory.hh"
28

29
#include "config.h"
30
#include "dnsdist.hh"
31
#include "dnsdist-async.hh"
32
#include "dnsdist-dnsparser.hh"
33
#include "dnsdist-ecs.hh"
34
#include "dnsdist-edns.hh"
35
#include "dnsdist-lua.hh"
36
#include "dnsdist-lua-ffi.hh"
37
#include "dnsdist-mac-address.hh"
38
#include "dnsdist-protobuf.hh"
39
#include "dnsdist-proxy-protocol.hh"
40
#include "dnsdist-kvs.hh"
41
#include "dnsdist-rule-chains.hh"
42
#include "dnsdist-self-answers.hh"
43
#include "dnsdist-snmp.hh"
44

45
#include "dnstap.hh"
46
#include "dnswriter.hh"
47
#include "dolog.hh"
48
#include "ednsoptions.hh"
49
#include "fstrm_logger.hh"
50
#include "ipcipher.hh"
51
#include "dnsdist-ipcrypt2.hh"
52
#include "iputils.hh"
53
#include "protozero-trace.hh"
54
#include "qtype.hh"
55
#include "remote_logger.hh"
56
#include "svc-records.hh"
57
#include "threadname.hh"
58

59
namespace dnsdist::actions
60
{
61
class DropAction : public DNSAction
62
{
63
public:
64
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
65
  {
23✔
66
    (void)dnsquestion;
23✔
67
    (void)ruleresult;
23✔
68
    return Action::Drop;
23✔
69
  }
23✔
70
  [[nodiscard]] std::string toString() const override
71
  {
×
72
    return "drop";
×
73
  }
×
74
};
75

76
class AllowAction : public DNSAction
77
{
78
public:
79
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
80
  {
6✔
81
    (void)dnsquestion;
6✔
82
    (void)ruleresult;
6✔
83
    return Action::Allow;
6✔
84
  }
6✔
85
  [[nodiscard]] std::string toString() const override
86
  {
×
87
    return "allow";
×
88
  }
×
89
};
90

91
class NoneAction : public DNSAction
92
{
93
public:
94
  // this action does not stop the processing
95
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
96
  {
4✔
97
    (void)dnsquestion;
4✔
98
    (void)ruleresult;
4✔
99
    return Action::None;
4✔
100
  }
4✔
101
  [[nodiscard]] std::string toString() const override
102
  {
×
103
    return "no op";
×
104
  }
×
105
};
106

107
class QPSAction : public DNSAction
108
{
109
public:
110
  QPSAction(int limit) :
111
    d_qps(QPSLimiter(limit, limit))
4✔
112
  {
4✔
113
  }
4✔
114
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
115
  {
24✔
116
    (void)dnsquestion;
24✔
117
    (void)ruleresult;
24✔
118
    if (d_qps.lock()->check()) {
24✔
119
      return Action::None;
22✔
120
    }
22✔
121
    return Action::Drop;
2✔
122
  }
24✔
123
  [[nodiscard]] std::string toString() const override
124
  {
×
125
    return "qps limit to " + std::to_string(d_qps.lock()->getRate());
×
126
  }
×
127

128
private:
129
  mutable LockGuarded<QPSLimiter> d_qps;
130
};
131

132
class DelayAction : public DNSAction
133
{
134
public:
135
  DelayAction(int msec) :
136
    d_msec(msec)
2✔
137
  {
2✔
138
  }
2✔
139
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
140
  {
2✔
141
    (void)dnsquestion;
2✔
142
    *ruleresult = std::to_string(d_msec);
2✔
143
    return Action::Delay;
2✔
144
  }
2✔
145
  [[nodiscard]] std::string toString() const override
146
  {
×
147
    return "delay by " + std::to_string(d_msec) + " ms";
×
148
  }
×
149

150
private:
151
  int d_msec;
152
};
153

154
class TeeAction : public DNSAction
155
{
156
public:
157
  // this action does not stop the processing
158
  TeeAction(const ComboAddress& rca, const std::optional<ComboAddress>& lca, bool addECS = false, bool addProxyProtocol = false);
159
  TeeAction(TeeAction& other) = delete;
160
  TeeAction(TeeAction&& other) = delete;
161
  TeeAction& operator=(TeeAction& other) = delete;
162
  TeeAction& operator=(TeeAction&& other) = delete;
163
  ~TeeAction() override;
164
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override;
165
  [[nodiscard]] std::string toString() const override;
166
  std::map<std::string, double> getStats() const override;
167

168
private:
169
  void worker();
170

171
  ComboAddress d_remote;
172
  std::thread d_worker;
173
  Socket d_socket;
174
  mutable std::atomic<unsigned long> d_senderrors{0};
175
  unsigned long d_recverrors{0};
176
  mutable std::atomic<unsigned long> d_queries{0};
177
  stat_t d_responses{0};
178
  stat_t d_nxdomains{0};
179
  stat_t d_servfails{0};
180
  stat_t d_refuseds{0};
181
  stat_t d_formerrs{0};
182
  stat_t d_notimps{0};
183
  stat_t d_noerrors{0};
184
  mutable stat_t d_tcpdrops{0};
185
  stat_t d_otherrcode{0};
186
  std::atomic<bool> d_pleaseQuit{false};
187
  bool d_addECS{false};
188
  bool d_addProxyProtocol{false};
189
};
190

191
TeeAction::TeeAction(const ComboAddress& rca, const std::optional<ComboAddress>& lca, bool addECS, bool addProxyProtocol) :
192
  d_remote(rca), d_socket(d_remote.sin4.sin_family, SOCK_DGRAM, 0), d_addECS(addECS), d_addProxyProtocol(addProxyProtocol)
6✔
193
{
6✔
194
  if (lca) {
6✔
195
    d_socket.bind(*lca, false);
2✔
196
  }
2✔
197
  d_socket.connect(d_remote);
6✔
198
  d_socket.setNonBlocking();
6✔
199
  d_worker = std::thread([this]() {
6✔
200
    worker();
6✔
201
  });
6✔
202
}
6✔
203

204
TeeAction::~TeeAction()
205
{
3✔
206
  d_pleaseQuit = true;
3✔
207
  close(d_socket.releaseHandle());
3✔
208
  d_worker.join();
3✔
209
}
3✔
210

211
DNSAction::Action TeeAction::operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const
212
{
30✔
213
  (void)ruleresult;
30✔
214
  if (dnsquestion->overTCP()) {
30!
215
    d_tcpdrops++;
×
216
    return DNSAction::Action::None;
×
217
  }
×
218

219
  d_queries++;
30✔
220

221
  PacketBuffer query;
30✔
222
  if (d_addECS) {
30✔
223
    query = dnsquestion->getData();
10✔
224
    bool ednsAdded = false;
10✔
225
    bool ecsAdded = false;
10✔
226

227
    std::string newECSOption;
10✔
228
    generateECSOption(dnsquestion->ecs ? dnsquestion->ecs->getNetwork() : dnsquestion->ids.origRemote, newECSOption, dnsquestion->ecs ? dnsquestion->ecs->getBits() : dnsquestion->ecsPrefixLength);
10!
229

230
    if (!handleEDNSClientSubnet(query, dnsquestion->getMaximumSize(), dnsquestion->ids.qname.wirelength(), ednsAdded, ecsAdded, dnsquestion->ecsOverride, newECSOption)) {
10!
231
      return DNSAction::Action::None;
×
232
    }
×
233
  }
10✔
234

235
  if (d_addProxyProtocol) {
30✔
236
    auto proxyPayload = getProxyProtocolPayload(*dnsquestion);
10✔
237
    if (query.empty()) {
10!
238
      query = dnsquestion->getData();
10✔
239
    }
10✔
240
    if (!addProxyProtocol(query, proxyPayload)) {
10!
241
      return DNSAction::Action::None;
×
242
    }
×
243
  }
10✔
244

245
  {
30✔
246
    const PacketBuffer& payload = query.empty() ? dnsquestion->getData() : query;
30✔
247
    auto res = send(d_socket.getHandle(), payload.data(), payload.size(), 0);
30✔
248

249
    if (res <= 0) {
30!
250
      d_senderrors++;
×
251
    }
×
252
  }
30✔
253

254
  return DNSAction::Action::None;
30✔
255
}
30✔
256

257
std::string TeeAction::toString() const
258
{
×
259
  return "tee to " + d_remote.toStringWithPort();
×
260
}
×
261

262
std::map<std::string, double> TeeAction::getStats() const
263
{
3✔
264
  return {{"queries", d_queries},
3✔
265
          {"responses", d_responses},
3✔
266
          {"recv-errors", d_recverrors},
3✔
267
          {"send-errors", d_senderrors},
3✔
268
          {"noerrors", d_noerrors},
3✔
269
          {"nxdomains", d_nxdomains},
3✔
270
          {"refuseds", d_refuseds},
3✔
271
          {"servfails", d_servfails},
3✔
272
          {"other-rcode", d_otherrcode},
3✔
273
          {"tcp-drops", d_tcpdrops}};
3✔
274
}
3✔
275

276
void TeeAction::worker()
277
{
6✔
278
  setThreadName("dnsdist/TeeWork");
6✔
279
  std::array<char, dnsdist::configuration::s_udpIncomingBufferSize> packet{};
6✔
280
  ssize_t res = 0;
6✔
281
  const dnsheader_aligned dnsheader(packet.data());
6✔
282
  for (;;) {
48✔
283
    res = waitForData(d_socket.getHandle(), 0, 250);
48✔
284
    if (d_pleaseQuit) {
48✔
285
      break;
3✔
286
    }
3✔
287

288
    if (res < 0) {
45!
289
      usleep(250000);
×
290
      continue;
×
291
    }
×
292
    if (res == 0) {
45✔
293
      continue;
12✔
294
    }
12✔
295
    res = recv(d_socket.getHandle(), packet.data(), packet.size(), 0);
33✔
296
    if (static_cast<size_t>(res) <= sizeof(struct dnsheader)) {
33!
297
      d_recverrors++;
×
298
    }
×
299
    else {
33✔
300
      d_responses++;
33✔
301
    }
33✔
302

303
    // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions): rcode is unsigned, RCode::rcodes_ as well
304
    if (dnsheader->rcode == RCode::NoError) {
33✔
305
      d_noerrors++;
30✔
306
    }
30✔
307
    // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions): rcode is unsigned, RCode::rcodes_ as well
308
    else if (dnsheader->rcode == RCode::ServFail) {
3!
309
      d_servfails++;
×
310
    }
×
311
    // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions): rcode is unsigned, RCode::rcodes_ as well
312
    else if (dnsheader->rcode == RCode::NXDomain) {
3!
313
      d_nxdomains++;
×
314
    }
×
315
    // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions): rcode is unsigned, RCode::rcodes_ as well
316
    else if (dnsheader->rcode == RCode::Refused) {
3!
317
      d_refuseds++;
×
318
    }
×
319
    // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions): rcode is unsigned, RCode::rcodes_ as well
320
    else if (dnsheader->rcode == RCode::FormErr) {
3!
321
      d_formerrs++;
×
322
    }
×
323
    // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions): rcode is unsigned, RCode::rcodes_ as well
324
    else if (dnsheader->rcode == RCode::NotImp) {
3!
325
      d_notimps++;
×
326
    }
×
327
  }
33✔
328
}
6✔
329

330
class PoolAction : public DNSAction
331
{
332
public:
333
  PoolAction(std::string pool, bool stopProcessing) :
334
    d_pool(std::move(pool)), d_stopProcessing(stopProcessing) {}
80✔
335

336
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
337
  {
593✔
338
    if (d_stopProcessing) {
593✔
339
      /* we need to do it that way to keep compatiblity with custom Lua actions returning DNSAction.Pool, 'poolname' */
340
      *ruleresult = d_pool;
501✔
341
      return Action::Pool;
501✔
342
    }
501✔
343
    dnsquestion->ids.poolName = d_pool;
92✔
344
    return Action::None;
92✔
345
  }
593✔
346

347
  [[nodiscard]] std::string toString() const override
348
  {
160✔
349
    return "to pool " + d_pool;
160✔
350
  }
160✔
351

352
private:
353
  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
354
  const std::string d_pool;
355
  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
356
  const bool d_stopProcessing;
357
};
358

359
class QPSPoolAction : public DNSAction
360
{
361
public:
362
  QPSPoolAction(unsigned int limit, std::string pool, bool stopProcessing) :
363
    d_qps(QPSLimiter(limit, limit)), d_pool(std::move(pool)), d_stopProcessing(stopProcessing) {}
2✔
364
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
365
  {
22✔
366
    (void)ruleresult;
22✔
367
    if (d_qps.lock()->check()) {
22✔
368
      if (d_stopProcessing) {
20!
369
        /* we need to do it that way to keep compatiblity with custom Lua actions returning DNSAction.Pool, 'poolname' */
370
        *ruleresult = d_pool;
20✔
371
        return Action::Pool;
20✔
372
      }
20✔
373
      dnsquestion->ids.poolName = d_pool;
×
374
    }
×
375
    return Action::None;
2✔
376
  }
22✔
377
  [[nodiscard]] std::string toString() const override
378
  {
×
379
    return "max " + std::to_string(d_qps.lock()->getRate()) + " to pool " + d_pool;
×
380
  }
×
381

382
private:
383
  mutable LockGuarded<QPSLimiter> d_qps;
384
  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
385
  const std::string d_pool;
386
  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
387
  const bool d_stopProcessing;
388
};
389

390
class RCodeAction : public DNSAction
391
{
392
public:
393
  RCodeAction(uint8_t rcode, const dnsdist::ResponseConfig& responseConfig) :
394
    d_responseConfig(responseConfig), d_rcode(rcode) {}
110✔
395
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
396
  {
107✔
397
    (void)ruleresult;
107✔
398
    dnsdist::self_answers::removeRecordsAndSetRCode(*dnsquestion, d_rcode);
107✔
399
    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [this](dnsheader& header) {
107✔
400
      setResponseHeadersFromConfig(header, d_responseConfig);
107✔
401
      return true;
107✔
402
    });
107✔
403
    return Action::HeaderModify;
107✔
404
  }
107✔
405
  [[nodiscard]] std::string toString() const override
406
  {
362✔
407
    return "set rcode " + std::to_string(d_rcode);
362✔
408
  }
362✔
409

410
private:
411
  dnsdist::ResponseConfig d_responseConfig;
412
  uint8_t d_rcode;
413
};
414

415
class ERCodeAction : public DNSAction
416
{
417
public:
418
  ERCodeAction(uint8_t rcode, dnsdist::ResponseConfig responseConfig) :
419
    d_responseConfig(responseConfig), d_rcode(rcode)
2✔
420
  {
2✔
421
  }
2✔
422
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
423
  {
×
424
    (void)ruleresult;
×
425
    dnsdist::self_answers::removeRecordsAndSetRCode(*dnsquestion, (d_rcode & 0xF));
×
426
    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [this](dnsheader& header) {
×
427
      setResponseHeadersFromConfig(header, d_responseConfig);
×
428
      return true;
×
429
    });
×
430
    dnsquestion->ednsRCode = ((d_rcode & 0xFFF0) >> 4);
×
431
    return Action::HeaderModify;
×
432
  }
×
433
  [[nodiscard]] std::string toString() const override
434
  {
×
435
    return "set ercode " + ERCode::to_s(d_rcode);
×
436
  }
×
437

438
private:
439
  dnsdist::ResponseConfig d_responseConfig;
440
  uint8_t d_rcode;
441
};
442

443
class SpoofSVCAction : public DNSAction
444
{
445
public:
446
  SpoofSVCAction(const std::vector<SVCRecordParameters>& parameters, const dnsdist::ResponseConfig& responseConfig) :
447
    d_responseConfig(responseConfig)
8✔
448
  {
8✔
449
    d_payloads.reserve(parameters.size());
8✔
450

451
    for (const auto& param : parameters) {
14✔
452
      std::vector<uint8_t> payload;
14✔
453
      if (!generateSVCPayload(payload, param)) {
14!
454
        throw std::runtime_error("Unable to generate a valid SVC record from the supplied parameters");
×
455
      }
×
456

457
      d_payloads.push_back(std::move(payload));
14✔
458

459
      for (const auto& hint : param.ipv4hints) {
14✔
460
        d_additionals4.insert({param.target, ComboAddress(hint)});
10✔
461
      }
10✔
462

463
      for (const auto& hint : param.ipv6hints) {
14✔
464
        d_additionals6.insert({param.target, ComboAddress(hint)});
10✔
465
      }
10✔
466
    }
14✔
467
  }
8✔
468

469
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
470
  {
8✔
471
    (void)ruleresult;
8✔
472
    if (!dnsdist::svc::generateSVCResponse(*dnsquestion, d_payloads, d_additionals4, d_additionals6, d_responseConfig)) {
8!
473
      return Action::None;
×
474
    }
×
475

476
    return Action::HeaderModify;
8✔
477
  }
8✔
478

479
  [[nodiscard]] std::string toString() const override
480
  {
×
481
    return "spoof SVC record ";
×
482
  }
×
483

484
private:
485
  dnsdist::ResponseConfig d_responseConfig;
486
  std::vector<std::vector<uint8_t>> d_payloads;
487
  std::set<std::pair<DNSName, ComboAddress>> d_additionals4;
488
  std::set<std::pair<DNSName, ComboAddress>> d_additionals6;
489
};
490

491
class TCAction : public DNSAction
492
{
493
public:
494
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
495
  {
7✔
496
    (void)dnsquestion;
7✔
497
    (void)ruleresult;
7✔
498
    return Action::Truncate;
7✔
499
  }
7✔
500
  [[nodiscard]] std::string toString() const override
501
  {
×
502
    return "tc=1 answer";
×
503
  }
×
504
};
505

506
class TCResponseAction : public DNSResponseAction
507
{
508
public:
509
  DNSResponseAction::Action operator()(DNSResponse* dnsResponse, std::string* ruleresult) const override
510
  {
4✔
511
    (void)dnsResponse;
4✔
512
    (void)ruleresult;
4✔
513
    return Action::Truncate;
4✔
514
  }
4✔
515
  [[nodiscard]] std::string toString() const override
516
  {
×
517
    return "tc=1 answer";
×
518
  }
×
519
};
520

521
class LuaAction : public DNSAction
522
{
523
public:
524
  LuaAction(LuaActionFunction func) :
525
    d_func(std::move(func))
162✔
526
  {}
162✔
527

528
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
529
  {
871✔
530
    try {
871✔
531
      DNSAction::Action result{};
871✔
532
      {
871✔
533
        auto lock = g_lua.lock();
871✔
534
        auto ret = d_func(dnsquestion);
871✔
535
        if (ruleresult != nullptr) {
871!
536
          if (boost::optional<std::string> rule = std::get<1>(ret)) {
871✔
537
            *ruleresult = *rule;
156✔
538
          }
156✔
539
          else {
715✔
540
            // default to empty string
541
            ruleresult->clear();
715✔
542
          }
715✔
543
        }
871✔
544
        result = static_cast<Action>(std::get<0>(ret));
871✔
545
      }
871✔
546
      dnsdist::handleQueuedAsynchronousEvents();
871✔
547
      return result;
871✔
548
    }
871✔
549
    catch (const std::exception& e) {
871✔
550
      warnlog("LuaAction failed inside Lua, returning ServFail: %s", e.what());
×
551
    }
×
552
    catch (...) {
871✔
553
      warnlog("LuaAction failed inside Lua, returning ServFail: [unknown exception]");
×
554
    }
×
555
    return DNSAction::Action::ServFail;
×
556
  }
871✔
557

558
  [[nodiscard]] std::string toString() const override
559
  {
10✔
560
    return "Lua script";
10✔
561
  }
10✔
562

563
private:
564
  LuaActionFunction d_func;
565
};
566

567
class LuaResponseAction : public DNSResponseAction
568
{
569
public:
570
  LuaResponseAction(LuaResponseActionFunction func) :
571
    d_func(std::move(func))
50✔
572
  {}
50✔
573
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
574
  {
545✔
575
    try {
545✔
576
      DNSResponseAction::Action result{};
545✔
577
      {
545✔
578
        auto lock = g_lua.lock();
545✔
579
        auto ret = d_func(response);
545✔
580
        if (ruleresult != nullptr) {
545!
581
          if (boost::optional<std::string> rule = std::get<1>(ret)) {
545✔
582
            *ruleresult = *rule;
17✔
583
          }
17✔
584
          else {
528✔
585
            // default to empty string
586
            ruleresult->clear();
528✔
587
          }
528✔
588
        }
545✔
589
        result = static_cast<Action>(std::get<0>(ret));
545✔
590
      }
545✔
591
      dnsdist::handleQueuedAsynchronousEvents();
545✔
592
      return result;
545✔
593
    }
545✔
594
    catch (const std::exception& e) {
545✔
595
      warnlog("LuaResponseAction failed inside Lua, returning ServFail: %s", e.what());
×
596
    }
×
597
    catch (...) {
545✔
598
      warnlog("LuaResponseAction failed inside Lua, returning ServFail: [unknown exception]");
×
599
    }
×
600
    return DNSResponseAction::Action::ServFail;
×
601
  }
545✔
602

603
  [[nodiscard]] std::string toString() const override
604
  {
×
605
    return "Lua response script";
×
606
  }
×
607

608
private:
609
  LuaResponseActionFunction d_func;
610
};
611

612
class LuaFFIAction : public DNSAction
613
{
614
public:
615
  LuaFFIAction(LuaActionFFIFunction func) :
616
    d_func(std::move(func))
36✔
617
  {
36✔
618
  }
36✔
619

620
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
621
  {
150✔
622
    dnsdist_ffi_dnsquestion_t dqffi(dnsquestion);
150✔
623
    try {
150✔
624
      DNSAction::Action result{};
150✔
625
      {
150✔
626
        auto lock = g_lua.lock();
150✔
627
        auto ret = d_func(&dqffi);
150✔
628
        if (ruleresult != nullptr) {
150!
629
          if (dqffi.result) {
150✔
630
            *ruleresult = *dqffi.result;
8✔
631
          }
8✔
632
          else {
142✔
633
            // default to empty string
634
            ruleresult->clear();
142✔
635
          }
142✔
636
        }
150✔
637
        result = static_cast<DNSAction::Action>(ret);
150✔
638
      }
150✔
639
      dnsdist::handleQueuedAsynchronousEvents();
150✔
640
      return result;
150✔
641
    }
150✔
642
    catch (const std::exception& e) {
150✔
643
      warnlog("LuaFFIAction failed inside Lua, returning ServFail: %s", e.what());
×
644
    }
×
645
    catch (...) {
150✔
646
      warnlog("LuaFFIAction failed inside Lua, returning ServFail: [unknown exception]");
×
647
    }
×
648
    return DNSAction::Action::ServFail;
×
649
  }
150✔
650

651
  [[nodiscard]] std::string toString() const override
652
  {
×
653
    return "Lua FFI script";
×
654
  }
×
655

656
private:
657
  LuaActionFFIFunction d_func;
658
};
659

660
class LuaFFIPerThreadAction : public DNSAction
661
{
662
public:
663
  LuaFFIPerThreadAction(std::string code) :
664
    d_functionCode(std::move(code)), d_functionID(s_functionsCounter++)
4✔
665
  {
4✔
666
  }
4✔
667

668
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
669
  {
8✔
670
    try {
8✔
671
      auto& state = t_perThreadStates[d_functionID];
8✔
672
      if (!state.d_initialized) {
8✔
673
        setupLuaFFIPerThreadContext(state.d_luaContext);
6✔
674
        /* mark the state as initialized first so if there is a syntax error
675
           we only try to execute the code once */
676
        state.d_initialized = true;
6✔
677
        state.d_func = state.d_luaContext.executeCode<LuaActionFFIFunction>(d_functionCode);
6✔
678
      }
6✔
679

680
      if (!state.d_func) {
8!
681
        /* the function was not properly initialized */
682
        return DNSAction::Action::None;
×
683
      }
×
684

685
      dnsdist_ffi_dnsquestion_t dqffi(dnsquestion);
8✔
686
      auto ret = state.d_func(&dqffi);
8✔
687
      if (ruleresult != nullptr) {
8!
688
        if (dqffi.result) {
8✔
689
          *ruleresult = *dqffi.result;
2✔
690
        }
2✔
691
        else {
6✔
692
          // default to empty string
693
          ruleresult->clear();
6✔
694
        }
6✔
695
      }
8✔
696
      dnsdist::handleQueuedAsynchronousEvents();
8✔
697
      return static_cast<DNSAction::Action>(ret);
8✔
698
    }
8✔
699
    catch (const std::exception& e) {
8✔
700
      warnlog("LuaFFIPerThreadAction failed inside Lua, returning ServFail: %s", e.what());
×
701
    }
×
702
    catch (...) {
8✔
703
      warnlog("LuaFFIPerthreadAction failed inside Lua, returning ServFail: [unknown exception]");
×
704
    }
×
705
    return DNSAction::Action::ServFail;
×
706
  }
8✔
707

708
  [[nodiscard]] std::string toString() const override
709
  {
×
710
    return "Lua FFI per-thread script";
×
711
  }
×
712

713
private:
714
  struct PerThreadState
715
  {
716
    LuaContext d_luaContext;
717
    LuaActionFFIFunction d_func;
718
    bool d_initialized{false};
719
  };
720
  static std::atomic<uint64_t> s_functionsCounter;
721
  static thread_local std::map<uint64_t, PerThreadState> t_perThreadStates;
722
  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
723
  const std::string d_functionCode;
724
  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
725
  const uint64_t d_functionID;
726
};
727

728
std::atomic<uint64_t> LuaFFIPerThreadAction::s_functionsCounter = 0;
729
thread_local std::map<uint64_t, LuaFFIPerThreadAction::PerThreadState> LuaFFIPerThreadAction::t_perThreadStates;
730

731
class LuaFFIResponseAction : public DNSResponseAction
732
{
733
public:
734
  LuaFFIResponseAction(LuaResponseActionFFIFunction func) :
735
    d_func(std::move(func))
18✔
736
  {
18✔
737
  }
18✔
738

739
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
740
  {
94✔
741
    dnsdist_ffi_dnsresponse_t ffiResponse(response);
94✔
742
    try {
94✔
743
      DNSResponseAction::Action result{};
94✔
744
      {
94✔
745
        auto lock = g_lua.lock();
94✔
746
        auto ret = d_func(&ffiResponse);
94✔
747
        if (ruleresult != nullptr) {
94!
748
          if (ffiResponse.result) {
94!
749
            *ruleresult = *ffiResponse.result;
×
750
          }
×
751
          else {
94✔
752
            // default to empty string
753
            ruleresult->clear();
94✔
754
          }
94✔
755
        }
94✔
756
        result = static_cast<DNSResponseAction::Action>(ret);
94✔
757
      }
94✔
758
      dnsdist::handleQueuedAsynchronousEvents();
94✔
759
      return result;
94✔
760
    }
94✔
761
    catch (const std::exception& e) {
94✔
762
      warnlog("LuaFFIResponseAction failed inside Lua, returning ServFail: %s", e.what());
×
763
    }
×
764
    catch (...) {
94✔
765
      warnlog("LuaFFIResponseAction failed inside Lua, returning ServFail: [unknown exception]");
×
766
    }
×
767
    return DNSResponseAction::Action::ServFail;
×
768
  }
94✔
769

770
  [[nodiscard]] std::string toString() const override
771
  {
×
772
    return "Lua FFI script";
×
773
  }
×
774

775
private:
776
  LuaResponseActionFFIFunction d_func;
777
};
778

779
class LuaFFIPerThreadResponseAction : public DNSResponseAction
780
{
781
public:
782
  LuaFFIPerThreadResponseAction(std::string code) :
783
    d_functionCode(std::move(code)), d_functionID(s_functionsCounter++)
×
784
  {
×
785
  }
×
786

787
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
788
  {
×
789
    try {
×
790
      auto& state = t_perThreadStates[d_functionID];
×
791
      if (!state.d_initialized) {
×
792
        setupLuaFFIPerThreadContext(state.d_luaContext);
×
793
        /* mark the state as initialized first so if there is a syntax error
794
           we only try to execute the code once */
795
        state.d_initialized = true;
×
796
        state.d_func = state.d_luaContext.executeCode<LuaResponseActionFFIFunction>(d_functionCode);
×
797
      }
×
798

799
      if (!state.d_func) {
×
800
        /* the function was not properly initialized */
801
        return DNSResponseAction::Action::None;
×
802
      }
×
803

804
      dnsdist_ffi_dnsresponse_t ffiResponse(response);
×
805
      auto ret = state.d_func(&ffiResponse);
×
806
      if (ruleresult != nullptr) {
×
807
        if (ffiResponse.result) {
×
808
          *ruleresult = *ffiResponse.result;
×
809
        }
×
810
        else {
×
811
          // default to empty string
812
          ruleresult->clear();
×
813
        }
×
814
      }
×
815
      dnsdist::handleQueuedAsynchronousEvents();
×
816
      return static_cast<DNSResponseAction::Action>(ret);
×
817
    }
×
818
    catch (const std::exception& e) {
×
819
      warnlog("LuaFFIPerThreadResponseAction failed inside Lua, returning ServFail: %s", e.what());
×
820
    }
×
821
    catch (...) {
×
822
      warnlog("LuaFFIPerthreadResponseAction failed inside Lua, returning ServFail: [unknown exception]");
×
823
    }
×
824
    return DNSResponseAction::Action::ServFail;
×
825
  }
×
826

827
  [[nodiscard]] std::string toString() const override
828
  {
×
829
    return "Lua FFI per-thread script";
×
830
  }
×
831

832
private:
833
  struct PerThreadState
834
  {
835
    LuaContext d_luaContext;
836
    LuaResponseActionFFIFunction d_func;
837
    bool d_initialized{false};
838
  };
839

840
  static std::atomic<uint64_t> s_functionsCounter;
841
  static thread_local std::map<uint64_t, PerThreadState> t_perThreadStates;
842
  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
843
  const std::string d_functionCode;
844
  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
845
  const uint64_t d_functionID;
846
};
847

848
std::atomic<uint64_t> LuaFFIPerThreadResponseAction::s_functionsCounter = 0;
849
thread_local std::map<uint64_t, LuaFFIPerThreadResponseAction::PerThreadState> LuaFFIPerThreadResponseAction::t_perThreadStates;
850

851
class SpoofAction : public DNSAction
852
{
853
public:
854
  SpoofAction(const vector<ComboAddress>& addrs, const dnsdist::ResponseConfig& responseConfig) :
855
    d_responseConfig(responseConfig), d_addrs(addrs)
177✔
856
  {
177✔
857
    for (const auto& addr : d_addrs) {
199✔
858
      if (addr.isIPv4()) {
199✔
859
        d_types.insert(QType::A);
183✔
860
      }
183✔
861
      else if (addr.isIPv6()) {
16!
862
        d_types.insert(QType::AAAA);
16✔
863
      }
16✔
864
    }
199✔
865

866
    if (!d_addrs.empty()) {
177!
867
      d_types.insert(QType::ANY);
177✔
868
    }
177✔
869
  }
177✔
870

871
  SpoofAction(DNSName cname, const dnsdist::ResponseConfig& responseConfig) :
872
    d_responseConfig(responseConfig), d_cname(std::move(cname))
26✔
873
  {
26✔
874
  }
26✔
875

876
  SpoofAction(PacketBuffer rawresponse) :
877
    d_raw(std::move(rawresponse))
2✔
878
  {
2✔
879
  }
2✔
880

881
  SpoofAction(const vector<std::string>& raws, std::optional<uint16_t> typeForAny, const dnsdist::ResponseConfig& responseConfig) :
882
    d_responseConfig(responseConfig), d_rawResponses(raws), d_rawTypeForAny(typeForAny)
18✔
883
  {
18✔
884
  }
18✔
885

886
  DNSAction::Action operator()(DNSQuestion* dnsquestion, string* ruleresult) const override;
887

888
  string toString() const override
889
  {
2✔
890
    string ret = "spoof in ";
2✔
891
    if (!d_cname.empty()) {
2!
892
      ret += d_cname.toString() + " ";
×
893
    }
×
894
    if (!d_rawResponses.empty()) {
2!
895
      ret += "raw bytes ";
×
896
    }
×
897
    else {
2✔
898
      for (const auto& addr : d_addrs) {
2✔
899
        ret += addr.toString() + " ";
2✔
900
      }
2✔
901
    }
2✔
902
    return ret;
2✔
903
  }
2✔
904

905
private:
906
  dnsdist::ResponseConfig d_responseConfig;
907
  std::vector<ComboAddress> d_addrs;
908
  std::unordered_set<uint16_t> d_types;
909
  std::vector<std::string> d_rawResponses;
910
  PacketBuffer d_raw;
911
  DNSName d_cname;
912
  std::optional<uint16_t> d_rawTypeForAny;
913
};
914

915
DNSAction::Action SpoofAction::operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const
916
{
172✔
917
  (void)ruleresult;
172✔
918
  uint16_t qtype = dnsquestion->ids.qtype;
172✔
919
  // do we even have a response?
920
  if (d_cname.empty() && d_rawResponses.empty() &&
172✔
921
      // make sure pre-forged response is greater than sizeof(dnsheader)
922
      (d_raw.size() < sizeof(dnsheader)) && d_types.count(qtype) == 0) {
172!
923
    return Action::None;
×
924
  }
×
925

926
  if (d_raw.size() >= sizeof(dnsheader)) {
172✔
927
    dnsdist::self_answers::generateAnswerFromRawPacket(*dnsquestion, d_raw);
2✔
928
    return Action::HeaderModify;
2✔
929
  }
2✔
930

931
  if (!d_cname.empty()) {
170✔
932
    if (dnsdist::self_answers::generateAnswerFromCNAME(*dnsquestion, d_cname, d_responseConfig)) {
14!
933
      return Action::HeaderModify;
14✔
934
    }
14✔
935
  }
14✔
936
  else if (!d_rawResponses.empty()) {
156✔
937
    if (dnsdist::self_answers::generateAnswerFromRDataEntries(*dnsquestion, d_rawResponses, d_rawTypeForAny, d_responseConfig)) {
16!
938
      return Action::HeaderModify;
16✔
939
    }
16✔
940
  }
16✔
941
  else {
140✔
942
    if (dnsdist::self_answers::generateAnswerFromIPAddresses(*dnsquestion, d_addrs, d_responseConfig)) {
140!
943
      return Action::HeaderModify;
140✔
944
    }
140✔
945
  }
140✔
946

947
  return Action::None;
×
948
}
170✔
949

950
class SetMacAddrAction : public DNSAction
951
{
952
public:
953
  // this action does not stop the processing
954
  SetMacAddrAction(uint16_t code) :
955
    d_code(code)
×
956
  {
×
957
  }
×
958

959
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
960
  {
×
961
    (void)ruleresult;
×
962
    dnsdist::MacAddress mac{};
×
963
    int res = dnsdist::MacAddressesCache::get(dnsquestion->ids.origRemote, mac.data(), mac.size());
×
964
    if (res != 0) {
×
965
      return Action::None;
×
966
    }
×
967

968
    std::string optRData;
×
969
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
970
    generateEDNSOption(d_code, reinterpret_cast<const char*>(mac.data()), optRData);
×
971

972
    if (dnsquestion->getHeader()->arcount > 0) {
×
973
      bool ednsAdded = false;
×
974
      bool optionAdded = false;
×
975
      PacketBuffer newContent;
×
976
      newContent.reserve(dnsquestion->getData().size());
×
977

978
      if (!slowRewriteEDNSOptionInQueryWithRecords(dnsquestion->getData(), newContent, ednsAdded, d_code, optionAdded, true, optRData)) {
×
979
        return Action::None;
×
980
      }
×
981

982
      if (newContent.size() > dnsquestion->getMaximumSize()) {
×
983
        return Action::None;
×
984
      }
×
985

986
      dnsquestion->getMutableData() = std::move(newContent);
×
987
      if (!dnsquestion->ids.ednsAdded && ednsAdded) {
×
988
        dnsquestion->ids.ednsAdded = true;
×
989
      }
×
990

991
      return Action::None;
×
992
    }
×
993

994
    auto& data = dnsquestion->getMutableData();
×
995
    if (generateOptRR(optRData, data, dnsquestion->getMaximumSize(), dnsdist::configuration::s_EdnsUDPPayloadSize, 0, false)) {
×
996
      dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [](dnsheader& header) {
×
997
        header.arcount = htons(1);
×
998
        return true;
×
999
      });
×
1000
      // make sure that any EDNS sent by the backend is removed before forwarding the response to the client
1001
      dnsquestion->ids.ednsAdded = true;
×
1002
    }
×
1003

1004
    return Action::None;
×
1005
  }
×
1006
  [[nodiscard]] std::string toString() const override
1007
  {
×
1008
    return "add EDNS MAC (code=" + std::to_string(d_code) + ")";
×
1009
  }
×
1010

1011
private:
1012
  uint16_t d_code{3};
1013
};
1014

1015
class SetEDNSOptionAction : public DNSAction
1016
{
1017
public:
1018
  // this action does not stop the processing
1019
  SetEDNSOptionAction(uint16_t code, std::string data) :
1020
    d_code(code), d_data(std::move(data))
2✔
1021
  {
2✔
1022
  }
2✔
1023

1024
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1025
  {
6✔
1026
    (void)ruleresult;
6✔
1027
    setEDNSOption(*dnsquestion, d_code, d_data, true);
6✔
1028
    return Action::None;
6✔
1029
  }
6✔
1030

1031
  [[nodiscard]] std::string toString() const override
1032
  {
×
1033
    return "add EDNS Option (code=" + std::to_string(d_code) + ")";
×
1034
  }
×
1035

1036
private:
1037
  uint16_t d_code;
1038
  std::string d_data;
1039
};
1040

1041
class SetEDNSOptionResponseAction : public DNSResponseAction
1042
{
1043
public:
1044
  // this action does not stop the processing
1045
  SetEDNSOptionResponseAction(uint16_t code, std::string data) :
1046
    d_code(code), d_data(std::move(data))
2✔
1047
  {
2✔
1048
  }
2✔
1049

1050
  DNSResponseAction::Action operator()(DNSResponse* response, [[maybe_unused]] std::string* ruleresult) const override
1051
  {
6✔
1052
    setEDNSOption(*response, d_code, d_data, false);
6✔
1053
    return Action::None;
6✔
1054
  }
6✔
1055

1056
  [[nodiscard]] std::string toString() const override
1057
  {
×
1058
    return "add EDNS Option to response (code=" + std::to_string(d_code) + ")";
×
1059
  }
×
1060

1061
private:
1062
  uint16_t d_code;
1063
  std::string d_data;
1064
};
1065

1066
class SetNoRecurseAction : public DNSAction
1067
{
1068
public:
1069
  // this action does not stop the processing
1070
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1071
  {
18✔
1072
    (void)ruleresult;
18✔
1073
    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [](dnsheader& header) {
18✔
1074
      header.rd = false;
18✔
1075
      return true;
18✔
1076
    });
18✔
1077
    return Action::None;
18✔
1078
  }
18✔
1079
  [[nodiscard]] std::string toString() const override
1080
  {
×
1081
    return "set rd=0";
×
1082
  }
×
1083
};
1084

1085
class LogAction : public DNSAction, public boost::noncopyable
1086
{
1087
public:
1088
  // this action does not stop the processing
1089
  LogAction() = default;
1090

1091
  LogAction(const std::string& str, bool binary = true, bool append = false, bool buffered = true, bool verboseOnly = true, bool includeTimestamp = false) :
1092
    d_fname(str), d_binary(binary), d_verboseOnly(verboseOnly), d_includeTimestamp(includeTimestamp), d_append(append), d_buffered(buffered)
2✔
1093
  {
2✔
1094
    if (str.empty()) {
2!
1095
      return;
×
1096
    }
×
1097

1098
    if (!reopenLogFile()) {
2!
1099
      throw std::runtime_error("Unable to open file '" + str + "' for logging: " + stringerror());
×
1100
    }
×
1101
  }
2✔
1102

1103
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1104
  {
50✔
1105
    (void)ruleresult;
50✔
1106
    auto filepointer = std::atomic_load_explicit(&d_fp, std::memory_order_acquire);
50✔
1107
    if (!filepointer) {
50!
1108
      if (!d_verboseOnly || dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose) {
×
1109
        if (d_includeTimestamp) {
×
1110
          infolog("[%u.%u] Packet from %s for %s %s with id %d", static_cast<unsigned long long>(dnsquestion->getQueryRealTime().tv_sec), static_cast<unsigned long>(dnsquestion->getQueryRealTime().tv_nsec), dnsquestion->ids.origRemote.toStringWithPort(), dnsquestion->ids.qname.toString(), QType(dnsquestion->ids.qtype).toString(), dnsquestion->getHeader()->id);
×
1111
        }
×
1112
        else {
×
1113
          infolog("Packet from %s for %s %s with id %d", dnsquestion->ids.origRemote.toStringWithPort(), dnsquestion->ids.qname.toString(), QType(dnsquestion->ids.qtype).toString(), dnsquestion->getHeader()->id);
×
1114
        }
×
1115
      }
×
1116
    }
×
1117
    else {
50✔
1118
      if (d_binary) {
50!
1119
        const auto& out = dnsquestion->ids.qname.getStorage();
×
1120
        if (d_includeTimestamp) {
×
1121
          auto tv_sec = static_cast<uint64_t>(dnsquestion->getQueryRealTime().tv_sec);
×
1122
          auto tv_nsec = static_cast<uint32_t>(dnsquestion->getQueryRealTime().tv_nsec);
×
1123
          fwrite(&tv_sec, sizeof(tv_sec), 1, filepointer.get());
×
1124
          fwrite(&tv_nsec, sizeof(tv_nsec), 1, filepointer.get());
×
1125
        }
×
1126
        uint16_t queryId = dnsquestion->getHeader()->id;
×
1127
        fwrite(&queryId, sizeof(queryId), 1, filepointer.get());
×
1128
        fwrite(out.c_str(), 1, out.size(), filepointer.get());
×
1129
        fwrite(&dnsquestion->ids.qtype, sizeof(dnsquestion->ids.qtype), 1, filepointer.get());
×
1130
        fwrite(&dnsquestion->ids.origRemote.sin4.sin_family, sizeof(dnsquestion->ids.origRemote.sin4.sin_family), 1, filepointer.get());
×
1131
        if (dnsquestion->ids.origRemote.sin4.sin_family == AF_INET) {
×
1132
          fwrite(&dnsquestion->ids.origRemote.sin4.sin_addr.s_addr, sizeof(dnsquestion->ids.origRemote.sin4.sin_addr.s_addr), 1, filepointer.get());
×
1133
        }
×
1134
        else if (dnsquestion->ids.origRemote.sin4.sin_family == AF_INET6) {
×
1135
          fwrite(&dnsquestion->ids.origRemote.sin6.sin6_addr.s6_addr, sizeof(dnsquestion->ids.origRemote.sin6.sin6_addr.s6_addr), 1, filepointer.get());
×
1136
        }
×
1137
        fwrite(&dnsquestion->ids.origRemote.sin4.sin_port, sizeof(dnsquestion->ids.origRemote.sin4.sin_port), 1, filepointer.get());
×
1138
      }
×
1139
      else {
50✔
1140
        if (d_includeTimestamp) {
50!
1141
          fprintf(filepointer.get(), "[%llu.%lu] Packet from %s for %s %s with id %u\n", static_cast<unsigned long long>(dnsquestion->getQueryRealTime().tv_sec), static_cast<unsigned long>(dnsquestion->getQueryRealTime().tv_nsec), dnsquestion->ids.origRemote.toStringWithPort().c_str(), dnsquestion->ids.qname.toString().c_str(), QType(dnsquestion->ids.qtype).toString().c_str(), dnsquestion->getHeader()->id);
×
1142
        }
×
1143
        else {
50✔
1144
          fprintf(filepointer.get(), "Packet from %s for %s %s with id %u\n", dnsquestion->ids.origRemote.toStringWithPort().c_str(), dnsquestion->ids.qname.toString().c_str(), QType(dnsquestion->ids.qtype).toString().c_str(), dnsquestion->getHeader()->id);
50✔
1145
        }
50✔
1146
      }
50✔
1147
    }
50✔
1148
    return Action::None;
50✔
1149
  }
50✔
1150

1151
  [[nodiscard]] std::string toString() const override
1152
  {
×
1153
    if (!d_fname.empty()) {
×
1154
      return "log to " + d_fname;
×
1155
    }
×
1156
    return "log";
×
1157
  }
×
1158

1159
  void reload() override
1160
  {
×
1161
    if (!reopenLogFile()) {
×
1162
      warnlog("Unable to open file '%s' for logging: %s", d_fname, stringerror());
×
1163
    }
×
1164
  }
×
1165

1166
private:
1167
  bool reopenLogFile()
1168
  {
2✔
1169
    // we are using a naked pointer here because we don't want fclose to be called
1170
    // with a nullptr, which would happen if we constructor a shared_ptr with fclose
1171
    // as a custom deleter and nullptr as a FILE*
1172
    // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
1173
    auto* nfp = fopen(d_fname.c_str(), d_append ? "a+" : "w");
2!
1174
    if (nfp == nullptr) {
2!
1175
      /* don't fall on our sword when reopening */
1176
      return false;
×
1177
    }
×
1178

1179
    auto filepointer = std::shared_ptr<FILE>(nfp, fclose);
2✔
1180
    nfp = nullptr;
2✔
1181

1182
    if (!d_buffered) {
2!
1183
      setbuf(filepointer.get(), nullptr);
2✔
1184
    }
2✔
1185

1186
    std::atomic_store_explicit(&d_fp, std::move(filepointer), std::memory_order_release);
2✔
1187
    return true;
2✔
1188
  }
2✔
1189

1190
  std::string d_fname;
1191
  std::shared_ptr<FILE> d_fp{nullptr};
1192
  bool d_binary{true};
1193
  bool d_verboseOnly{true};
1194
  bool d_includeTimestamp{false};
1195
  bool d_append{false};
1196
  bool d_buffered{true};
1197
};
1198

1199
class LogResponseAction : public DNSResponseAction, public boost::noncopyable
1200
{
1201
public:
1202
  LogResponseAction() = default;
1203

1204
  LogResponseAction(const std::string& str, bool append = false, bool buffered = true, bool verboseOnly = true, bool includeTimestamp = false) :
1205
    d_fname(str), d_verboseOnly(verboseOnly), d_includeTimestamp(includeTimestamp), d_append(append), d_buffered(buffered)
×
1206
  {
×
1207
    if (str.empty()) {
×
1208
      return;
×
1209
    }
×
1210

1211
    if (!reopenLogFile()) {
×
1212
      throw std::runtime_error("Unable to open file '" + str + "' for logging: " + stringerror());
×
1213
    }
×
1214
  }
×
1215

1216
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
1217
  {
×
1218
    (void)ruleresult;
×
1219
    auto filepointer = std::atomic_load_explicit(&d_fp, std::memory_order_acquire);
×
1220
    if (!filepointer) {
×
1221
      if (!d_verboseOnly || dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose) {
×
1222
        if (d_includeTimestamp) {
×
1223
          infolog("[%u.%u] Answer to %s for %s %s (%s) with id %u", static_cast<unsigned long long>(response->getQueryRealTime().tv_sec), static_cast<unsigned long>(response->getQueryRealTime().tv_nsec), response->ids.origRemote.toStringWithPort(), response->ids.qname.toString(), QType(response->ids.qtype).toString(), RCode::to_s(response->getHeader()->rcode), response->getHeader()->id);
×
1224
        }
×
1225
        else {
×
1226
          infolog("Answer to %s for %s %s (%s) with id %u", response->ids.origRemote.toStringWithPort(), response->ids.qname.toString(), QType(response->ids.qtype).toString(), RCode::to_s(response->getHeader()->rcode), response->getHeader()->id);
×
1227
        }
×
1228
      }
×
1229
    }
×
1230
    else {
×
1231
      if (d_includeTimestamp) {
×
1232
        fprintf(filepointer.get(), "[%llu.%lu] Answer to %s for %s %s (%s) with id %u\n", static_cast<unsigned long long>(response->getQueryRealTime().tv_sec), static_cast<unsigned long>(response->getQueryRealTime().tv_nsec), response->ids.origRemote.toStringWithPort().c_str(), response->ids.qname.toString().c_str(), QType(response->ids.qtype).toString().c_str(), RCode::to_s(response->getHeader()->rcode).c_str(), response->getHeader()->id);
×
1233
      }
×
1234
      else {
×
1235
        fprintf(filepointer.get(), "Answer to %s for %s %s (%s) with id %u\n", response->ids.origRemote.toStringWithPort().c_str(), response->ids.qname.toString().c_str(), QType(response->ids.qtype).toString().c_str(), RCode::to_s(response->getHeader()->rcode).c_str(), response->getHeader()->id);
×
1236
      }
×
1237
    }
×
1238
    return Action::None;
×
1239
  }
×
1240

1241
  [[nodiscard]] std::string toString() const override
1242
  {
×
1243
    if (!d_fname.empty()) {
×
1244
      return "log to " + d_fname;
×
1245
    }
×
1246
    return "log";
×
1247
  }
×
1248

1249
  void reload() override
1250
  {
×
1251
    if (!reopenLogFile()) {
×
1252
      warnlog("Unable to open file '%s' for logging: %s", d_fname, stringerror());
×
1253
    }
×
1254
  }
×
1255

1256
private:
1257
  bool reopenLogFile()
1258
  {
×
1259
    // we are using a naked pointer here because we don't want fclose to be called
1260
    // with a nullptr, which would happen if we constructor a shared_ptr with fclose
1261
    // as a custom deleter and nullptr as a FILE*
1262
    // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
1263
    auto* nfp = fopen(d_fname.c_str(), d_append ? "a+" : "w");
×
1264
    if (nfp == nullptr) {
×
1265
      /* don't fall on our sword when reopening */
1266
      return false;
×
1267
    }
×
1268

1269
    auto filepointer = std::shared_ptr<FILE>(nfp, fclose);
×
1270
    nfp = nullptr;
×
1271

1272
    if (!d_buffered) {
×
1273
      setbuf(filepointer.get(), nullptr);
×
1274
    }
×
1275

1276
    std::atomic_store_explicit(&d_fp, std::move(filepointer), std::memory_order_release);
×
1277
    return true;
×
1278
  }
×
1279

1280
  std::string d_fname;
1281
  std::shared_ptr<FILE> d_fp{nullptr};
1282
  bool d_verboseOnly{true};
1283
  bool d_includeTimestamp{false};
1284
  bool d_append{false};
1285
  bool d_buffered{true};
1286
};
1287

1288
class SetDisableValidationAction : public DNSAction
1289
{
1290
public:
1291
  // this action does not stop the processing
1292
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1293
  {
8✔
1294
    (void)ruleresult;
8✔
1295
    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [](dnsheader& header) {
8✔
1296
      header.cd = true;
8✔
1297
      return true;
8✔
1298
    });
8✔
1299
    return Action::None;
8✔
1300
  }
8✔
1301
  [[nodiscard]] std::string toString() const override
1302
  {
×
1303
    return "set cd=1";
×
1304
  }
×
1305
};
1306

1307
class SetSkipCacheAction : public DNSAction
1308
{
1309
public:
1310
  // this action does not stop the processing
1311
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1312
  {
220✔
1313
    (void)ruleresult;
220✔
1314
    dnsquestion->ids.skipCache = true;
220✔
1315
    return Action::None;
220✔
1316
  }
220✔
1317
  [[nodiscard]] std::string toString() const override
1318
  {
×
1319
    return "skip cache";
×
1320
  }
×
1321
};
1322

1323
class SetSkipCacheResponseAction : public DNSResponseAction
1324
{
1325
public:
1326
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
1327
  {
20✔
1328
    (void)ruleresult;
20✔
1329
    response->ids.skipCache = true;
20✔
1330
    return Action::None;
20✔
1331
  }
20✔
1332
  [[nodiscard]] std::string toString() const override
1333
  {
×
1334
    return "skip cache";
×
1335
  }
×
1336
};
1337

1338
class SetTempFailureCacheTTLAction : public DNSAction
1339
{
1340
public:
1341
  // this action does not stop the processing
1342
  SetTempFailureCacheTTLAction(uint32_t ttl) :
1343
    d_ttl(ttl)
2✔
1344
  {
2✔
1345
  }
2✔
1346
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1347
  {
3✔
1348
    (void)ruleresult;
3✔
1349
    dnsquestion->ids.tempFailureTTL = d_ttl;
3✔
1350
    return Action::None;
3✔
1351
  }
3✔
1352
  [[nodiscard]] std::string toString() const override
1353
  {
×
1354
    return "set tempfailure cache ttl to " + std::to_string(d_ttl);
×
1355
  }
×
1356

1357
private:
1358
  uint32_t d_ttl;
1359
};
1360

1361
class SetECSPrefixLengthAction : public DNSAction
1362
{
1363
public:
1364
  // this action does not stop the processing
1365
  SetECSPrefixLengthAction(uint16_t v4Length, uint16_t v6Length) :
1366
    d_v4PrefixLength(v4Length), d_v6PrefixLength(v6Length)
2✔
1367
  {
2✔
1368
  }
2✔
1369
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1370
  {
2✔
1371
    (void)ruleresult;
2✔
1372
    dnsquestion->ecsPrefixLength = dnsquestion->ids.origRemote.sin4.sin_family == AF_INET ? d_v4PrefixLength : d_v6PrefixLength;
2!
1373
    return Action::None;
2✔
1374
  }
2✔
1375
  [[nodiscard]] std::string toString() const override
1376
  {
×
1377
    return "set ECS prefix length to " + std::to_string(d_v4PrefixLength) + "/" + std::to_string(d_v6PrefixLength);
×
1378
  }
×
1379

1380
private:
1381
  uint16_t d_v4PrefixLength;
1382
  uint16_t d_v6PrefixLength;
1383
};
1384

1385
class SetECSOverrideAction : public DNSAction
1386
{
1387
public:
1388
  // this action does not stop the processing
1389
  SetECSOverrideAction(bool ecsOverride) :
1390
    d_ecsOverride(ecsOverride)
2✔
1391
  {
2✔
1392
  }
2✔
1393
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1394
  {
2✔
1395
    (void)ruleresult;
2✔
1396
    dnsquestion->ecsOverride = d_ecsOverride;
2✔
1397
    return Action::None;
2✔
1398
  }
2✔
1399
  [[nodiscard]] std::string toString() const override
1400
  {
×
1401
    return "set ECS override to " + std::to_string(static_cast<int>(d_ecsOverride));
×
1402
  }
×
1403

1404
private:
1405
  bool d_ecsOverride;
1406
};
1407

1408
class SetDisableECSAction : public DNSAction
1409
{
1410
public:
1411
  // this action does not stop the processing
1412
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1413
  {
2✔
1414
    (void)ruleresult;
2✔
1415
    dnsquestion->useECS = false;
2✔
1416
    return Action::None;
2✔
1417
  }
2✔
1418
  [[nodiscard]] std::string toString() const override
1419
  {
×
1420
    return "disable ECS";
×
1421
  }
×
1422
};
1423

1424
class SetECSAction : public DNSAction
1425
{
1426
public:
1427
  // this action does not stop the processing
1428
  SetECSAction(const Netmask& v4Netmask) :
1429
    d_v4(v4Netmask), d_hasV6(false)
6✔
1430
  {
6✔
1431
  }
6✔
1432

1433
  SetECSAction(const Netmask& v4Netmask, const Netmask& v6Netmask) :
1434
    d_v4(v4Netmask), d_v6(v6Netmask), d_hasV6(true)
×
1435
  {
×
1436
  }
×
1437

1438
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1439
  {
10✔
1440
    (void)ruleresult;
10✔
1441
    if (d_hasV6) {
10!
1442
      dnsquestion->ecs = std::make_unique<Netmask>(dnsquestion->ids.origRemote.isIPv4() ? d_v4 : d_v6);
×
1443
    }
×
1444
    else {
10✔
1445
      dnsquestion->ecs = std::make_unique<Netmask>(d_v4);
10✔
1446
    }
10✔
1447

1448
    return Action::None;
10✔
1449
  }
10✔
1450

1451
  [[nodiscard]] std::string toString() const override
1452
  {
×
1453
    std::string result = "set ECS to " + d_v4.toString();
×
1454
    if (d_hasV6) {
×
1455
      result += " / " + d_v6.toString();
×
1456
    }
×
1457
    return result;
×
1458
  }
×
1459

1460
private:
1461
  Netmask d_v4;
1462
  Netmask d_v6;
1463
  bool d_hasV6;
1464
};
1465

1466
#ifndef DISABLE_PROTOBUF
1467
static std::tuple<DnstapMessage::ProtocolType, boost::optional<DnstapMessage::HttpProtocolType>> ProtocolToDNSTap(dnsdist::Protocol protocol)
1468
{
30✔
1469
  if (protocol == dnsdist::Protocol::DoUDP) {
30✔
1470
    return {DnstapMessage::ProtocolType::DoUDP, boost::none};
20✔
1471
  }
20✔
1472
  if (protocol == dnsdist::Protocol::DoTCP) {
10✔
1473
    return {DnstapMessage::ProtocolType::DoTCP, boost::none};
8✔
1474
  }
8✔
1475
  if (protocol == dnsdist::Protocol::DoT) {
2!
1476
    return {DnstapMessage::ProtocolType::DoT, boost::none};
×
1477
  }
×
1478
  if (protocol == dnsdist::Protocol::DoH) {
2✔
1479
    return {DnstapMessage::ProtocolType::DoH, DnstapMessage::HttpProtocolType::HTTP2};
1✔
1480
  }
1✔
1481
  if (protocol == dnsdist::Protocol::DoH3) {
1!
1482
    return {DnstapMessage::ProtocolType::DoH, DnstapMessage::HttpProtocolType::HTTP3};
1✔
1483
  }
1✔
1484
  if (protocol == dnsdist::Protocol::DNSCryptUDP) {
×
1485
    return {DnstapMessage::ProtocolType::DNSCryptUDP, boost::none};
×
1486
  }
×
1487
  if (protocol == dnsdist::Protocol::DNSCryptTCP) {
×
1488
    return {DnstapMessage::ProtocolType::DNSCryptTCP, boost::none};
×
1489
  }
×
1490
  if (protocol == dnsdist::Protocol::DoQ) {
×
1491
    return {DnstapMessage::ProtocolType::DoQ, boost::none};
×
1492
  }
×
1493
  throw std::runtime_error("Unhandled protocol for dnstap: " + protocol.toPrettyString());
×
1494
}
×
1495

1496
static void remoteLoggerQueueData(RemoteLoggerInterface& remoteLogger, const std::string& data)
1497
{
50✔
1498
  auto ret = remoteLogger.queueData(data);
50✔
1499

1500
  switch (ret) {
50!
1501
  case RemoteLoggerInterface::Result::Queued:
50!
1502
    break;
50✔
1503
  case RemoteLoggerInterface::Result::PipeFull: {
×
1504
    vinfolog("%s: %s", remoteLogger.name(), RemoteLoggerInterface::toErrorString(ret));
×
1505
    break;
×
1506
  }
×
1507
  case RemoteLoggerInterface::Result::TooLarge: {
×
1508
    warnlog("%s: %s", remoteLogger.name(), RemoteLoggerInterface::toErrorString(ret));
×
1509
    break;
×
1510
  }
×
1511
  case RemoteLoggerInterface::Result::OtherError:
×
1512
    warnlog("%s: %s", remoteLogger.name(), RemoteLoggerInterface::toErrorString(ret));
×
1513
  }
50✔
1514
}
50✔
1515

1516
class DnstapLogAction : public DNSAction, public boost::noncopyable
1517
{
1518
public:
1519
  // this action does not stop the processing
1520
  DnstapLogAction(std::string identity, std::shared_ptr<RemoteLoggerInterface>& logger, std::optional<std::function<void(DNSQuestion*, DnstapMessage*)>> alterFunc) :
1521
    d_identity(std::move(identity)), d_logger(logger), d_alterFunc(std::move(alterFunc))
12✔
1522
  {
12✔
1523
  }
12✔
1524
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1525
  {
22✔
1526
    (void)ruleresult;
22✔
1527
    static thread_local std::string data;
22✔
1528
    data.clear();
22✔
1529

1530
    auto [protocol, httpProtocol] = ProtocolToDNSTap(dnsquestion->getProtocol());
22✔
1531
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1532
    DnstapMessage message(std::move(data), !dnsquestion->getHeader()->qr ? DnstapMessage::MessageType::client_query : DnstapMessage::MessageType::client_response, d_identity, &dnsquestion->ids.origRemote, &dnsquestion->ids.origDest, protocol, reinterpret_cast<const char*>(dnsquestion->getData().data()), dnsquestion->getData().size(), &dnsquestion->getQueryRealTime(), nullptr, boost::none, httpProtocol);
22!
1533
    {
22✔
1534
      if (d_alterFunc) {
22✔
1535
        auto lock = g_lua.lock();
8✔
1536
        (*d_alterFunc)(dnsquestion, &message);
8✔
1537
      }
8✔
1538
    }
22✔
1539

1540
    data = message.getBuffer();
22✔
1541
    remoteLoggerQueueData(*d_logger, data);
22✔
1542

1543
    return Action::None;
22✔
1544
  }
22✔
1545
  [[nodiscard]] std::string toString() const override
1546
  {
×
1547
    return "remote log as dnstap to " + (d_logger ? d_logger->toString() : "");
×
1548
  }
×
1549

1550
private:
1551
  std::string d_identity;
1552
  std::shared_ptr<RemoteLoggerInterface> d_logger;
1553
  std::optional<std::function<void(DNSQuestion*, DnstapMessage*)>> d_alterFunc;
1554
};
1555

1556
namespace
1557
{
1558
  void addMetaDataToProtobuf(DNSDistProtoBufMessage& message, const DNSQuestion& dnsquestion, const std::vector<std::pair<std::string, ProtoBufMetaKey>>& metas)
1559
  {
62✔
1560
    for (const auto& [name, meta] : metas) {
62✔
1561
      message.addMeta(name, meta.getValues(dnsquestion), {});
55✔
1562
    }
55✔
1563
  }
62✔
1564

1565
  void addTagsToProtobuf(DNSDistProtoBufMessage& message, const DNSQuestion& dnsquestion, const std::unordered_set<std::string>& allowed)
1566
  {
4✔
1567
    if (!dnsquestion.ids.qTag) {
4✔
1568
      return;
2✔
1569
    }
2✔
1570

1571
    for (const auto& [key, value] : *dnsquestion.ids.qTag) {
5✔
1572
      if (!allowed.empty() && allowed.count(key) == 0) {
5✔
1573
        continue;
1✔
1574
      }
1✔
1575

1576
      if (value.empty()) {
4✔
1577
        message.addTag(key);
2✔
1578
      }
2✔
1579
      else {
2✔
1580
        auto tag = key;
2✔
1581
        tag.append(":");
2✔
1582
        tag.append(value);
2✔
1583
        message.addTag(tag);
2✔
1584
      }
2✔
1585
    }
4✔
1586
  }
2✔
1587

1588
  void addExtendedDNSErrorToProtobuf(DNSDistProtoBufMessage& message, const DNSResponse& response, const std::string& metaKey)
1589
  {
1✔
1590
    auto [infoCode, extraText] = dnsdist::edns::getExtendedDNSError(response.getData());
1✔
1591
    if (!infoCode) {
1!
1592
      return;
×
1593
    }
×
1594

1595
    if (extraText) {
1!
1596
      message.addMeta(metaKey, {*extraText}, {*infoCode});
1✔
1597
    }
1✔
1598
    else {
×
1599
      message.addMeta(metaKey, {}, {*infoCode});
×
1600
    }
×
1601
  }
1✔
1602
}
1603

1604
class RemoteLogAction : public DNSAction, public boost::noncopyable
1605
{
1606
public:
1607
  // this action does not stop the processing
1608
  RemoteLogAction(RemoteLogActionConfiguration& config) :
1609
    d_tagsToExport(std::move(config.tagsToExport)), d_metas(std::move(config.metas)), d_logger(config.logger), d_alterFunc(std::move(config.alterQueryFunc)), d_serverID(config.serverID), d_ipEncryptKey(config.ipEncryptKey), d_ipEncryptMethod(config.ipEncryptMethod)
18✔
1610
  {
18✔
1611
    if (!d_ipEncryptKey.empty() && d_ipEncryptMethod == "ipcrypt-pfx") {
18✔
1612
      d_ipcrypt2 = pdns::ipcrypt2::IPCrypt2(pdns::ipcrypt2::IPCryptMethod::pfx, d_ipEncryptKey);
2✔
1613
    }
2✔
1614
  }
18✔
1615

1616
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1617
  {
20✔
1618
    (void)ruleresult;
20✔
1619
    if (!dnsquestion->ids.d_protoBufData) {
20!
1620
      dnsquestion->ids.d_protoBufData = std::make_unique<InternalQueryState::ProtoBufData>();
20✔
1621
    }
20✔
1622
    if (!dnsquestion->ids.d_protoBufData->uniqueId) {
20!
1623
      dnsquestion->ids.d_protoBufData->uniqueId = getUniqueID();
20✔
1624
    }
20✔
1625

1626
    DNSDistProtoBufMessage message(*dnsquestion);
20✔
1627
    if (!d_serverID.empty()) {
20!
1628
      message.setServerIdentity(d_serverID);
20✔
1629
    }
20✔
1630

1631
#ifdef HAVE_IPCIPHER
20✔
1632
    if (!d_ipEncryptKey.empty() && d_ipEncryptMethod == "legacy") {
20✔
1633
      message.setRequestor(encryptCA(dnsquestion->ids.origRemote, d_ipEncryptKey));
2✔
1634
    }
2✔
1635
#endif /* HAVE_IPCIPHER */
20✔
1636
    if (d_ipcrypt2) {
20✔
1637
      auto encryptedAddress = d_ipcrypt2->encrypt(dnsquestion->ids.origRemote);
2✔
1638
      encryptedAddress.setPort(dnsquestion->ids.origRemote.getPort());
2✔
1639
      message.setRequestor(encryptedAddress);
2✔
1640
    }
2✔
1641

1642
    if (d_tagsToExport) {
20✔
1643
      addTagsToProtobuf(message, *dnsquestion, *d_tagsToExport);
3✔
1644
    }
3✔
1645

1646
    addMetaDataToProtobuf(message, *dnsquestion, d_metas);
20✔
1647

1648
    if (d_alterFunc) {
20✔
1649
      auto lock = g_lua.lock();
4✔
1650
      (*d_alterFunc)(dnsquestion, &message);
4✔
1651
    }
4✔
1652

1653
    static thread_local std::string data;
20✔
1654
    data.clear();
20✔
1655
    message.serialize(data);
20✔
1656
    if (!dnsquestion->ids.d_rawProtobufContent.empty()) {
20✔
1657
      data.insert(data.end(), dnsquestion->ids.d_rawProtobufContent.begin(), dnsquestion->ids.d_rawProtobufContent.end());
1✔
1658
    }
1✔
1659
    remoteLoggerQueueData(*d_logger, data);
20✔
1660

1661
    return Action::None;
20✔
1662
  }
20✔
1663
  [[nodiscard]] std::string toString() const override
1664
  {
×
1665
    return "remote log to " + (d_logger ? d_logger->toString() : "");
×
1666
  }
×
1667

1668
private:
1669
  std::optional<std::unordered_set<std::string>> d_tagsToExport;
1670
  std::vector<std::pair<std::string, ProtoBufMetaKey>> d_metas;
1671
  std::shared_ptr<RemoteLoggerInterface> d_logger;
1672
  std::optional<std::function<void(DNSQuestion*, DNSDistProtoBufMessage*)>> d_alterFunc;
1673
  std::string d_serverID;
1674
  std::string d_ipEncryptKey;
1675
  std::string d_ipEncryptMethod;
1676
  std::optional<pdns::ipcrypt2::IPCrypt2> d_ipcrypt2{std::nullopt};
1677
};
1678
#endif /* DISABLE_PROTOBUF */
1679

1680
class SetTraceAction : public DNSAction
1681
{
1682
public:
1683
  SetTraceAction(bool value, std::optional<bool> useIncomingTraceID, std::optional<short unsigned int> incomingTraceIDOptionCode) :
1684
    d_value{value}, d_useIncomingTraceID(useIncomingTraceID), d_incomingTraceIDOptionCode(incomingTraceIDOptionCode) {};
20✔
1685

1686
  DNSAction::Action operator()([[maybe_unused]] DNSQuestion* dnsquestion, [[maybe_unused]] std::string* ruleresult) const override
1687
  {
18✔
1688
#ifndef DISABLE_PROTOBUF
18✔
1689
    auto tracer = dnsquestion->ids.getTracer();
18✔
1690
    if (tracer == nullptr) {
18!
1691
      vinfolog("SetTraceAction called, but OpenTelemetry tracing is globally disabled. Did you forget to call setOpenTelemetryTracing?");
×
1692
      return Action::None;
×
1693
    }
×
1694
    dnsquestion->ids.tracingEnabled = d_value;
18✔
1695
    if (d_value) {
18✔
1696
      tracer->activate();
16✔
1697
      tracer->setRootSpanAttribute("query.qname", AnyValue{dnsquestion->ids.qname.toStringNoDot()});
16✔
1698
      tracer->setRootSpanAttribute("query.qtype", AnyValue{QType(dnsquestion->ids.qtype).toString()});
16✔
1699
      tracer->setRootSpanAttribute("query.remote.address", AnyValue{dnsquestion->ids.origRemote.toString()});
16✔
1700
      tracer->setRootSpanAttribute("query.remote.port", AnyValue{dnsquestion->ids.origRemote.getPort()});
16✔
1701
      if (d_useIncomingTraceID.value_or(false)) {
16✔
1702
        if (dnsquestion->ednsOptions == nullptr && !parseEDNSOptions(*dnsquestion)) {
6!
1703
          // Maybe parsed, but no EDNS found
1704
          return Action::None;
2✔
1705
        }
2✔
1706
        if (dnsquestion->ednsOptions == nullptr) {
4!
1707
          // Parsing failed, log a warning and return
NEW
1708
          vinfolog("parsing EDNS options failed while looking for OpenTelemetry Trace ID");
×
NEW
1709
          return Action::None;
×
NEW
1710
        }
×
1711
        pdns::trace::TraceID traceID;
4✔
1712
        pdns::trace::SpanID spanID;
4✔
1713
        if (pdns::trace::extractOTraceIDs(*(dnsquestion->ednsOptions), EDNSOptionCode::EDNSOptionCodeEnum(d_incomingTraceIDOptionCode.value_or(EDNSOptionCode::OTTRACEIDS)), traceID, spanID)) {
4!
1714
          tracer->setTraceID(traceID);
4✔
1715
          tracer->setRootSpanID(spanID);
4✔
1716
        }
4✔
1717
      }
4✔
1718
    }
16✔
1719
    else {
2✔
1720
      tracer->deactivate();
2✔
1721
    }
2✔
1722
#endif
16✔
1723
    return Action::None;
16✔
1724
  }
18✔
1725

1726
  [[nodiscard]] std::string toString() const override
1727
  {
×
1728
    return string((d_value ? "en" : "dis")) + string("able OpenTelemetry Tracing");
×
1729
  }
×
1730

1731
private:
1732
  bool d_value;
1733
  std::optional<bool> d_useIncomingTraceID;
1734
  std::optional<short unsigned int> d_incomingTraceIDOptionCode;
1735
};
1736

1737
class SNMPTrapAction : public DNSAction
1738
{
1739
public:
1740
  // this action does not stop the processing
1741
  SNMPTrapAction(std::string reason) :
1742
    d_reason(std::move(reason))
×
1743
  {
×
1744
  }
×
1745
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1746
  {
×
1747
    (void)ruleresult;
×
1748
    if (g_snmpAgent != nullptr && dnsdist::configuration::getImmutableConfiguration().d_snmpTrapsEnabled) {
×
1749
      g_snmpAgent->sendDNSTrap(*dnsquestion, d_reason);
×
1750
    }
×
1751

1752
    return Action::None;
×
1753
  }
×
1754
  [[nodiscard]] std::string toString() const override
1755
  {
×
1756
    return "send SNMP trap";
×
1757
  }
×
1758

1759
private:
1760
  std::string d_reason;
1761
};
1762

1763
class SetTagAction : public DNSAction
1764
{
1765
public:
1766
  // this action does not stop the processing
1767
  SetTagAction(std::string tag, std::string value) :
1768
    d_tag(std::move(tag)), d_value(std::move(value))
20✔
1769
  {
20✔
1770
  }
20✔
1771
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
1772
  {
22✔
1773
    (void)ruleresult;
22✔
1774
    dnsquestion->setTag(d_tag, d_value);
22✔
1775

1776
    return Action::None;
22✔
1777
  }
22✔
1778
  [[nodiscard]] std::string toString() const override
1779
  {
×
1780
    return "set tag '" + d_tag + "' to value '" + d_value + "'";
×
1781
  }
×
1782

1783
private:
1784
  std::string d_tag;
1785
  std::string d_value;
1786
};
1787

1788
#ifndef DISABLE_PROTOBUF
1789
class DnstapLogResponseAction : public DNSResponseAction, public boost::noncopyable
1790
{
1791
public:
1792
  // this action does not stop the processing
1793
  DnstapLogResponseAction(std::string identity, std::shared_ptr<RemoteLoggerInterface>& logger, std::optional<std::function<void(DNSResponse*, DnstapMessage*)>> alterFunc) :
1794
    d_identity(std::move(identity)), d_logger(logger), d_alterFunc(std::move(alterFunc))
4✔
1795
  {
4✔
1796
  }
4✔
1797
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
1798
  {
8✔
1799
    (void)ruleresult;
8✔
1800
    static thread_local std::string data;
8✔
1801
    struct timespec now = {};
8✔
1802
    gettime(&now, true);
8✔
1803
    data.clear();
8✔
1804

1805
    auto [protocol, httpProtocol] = ProtocolToDNSTap(response->getProtocol());
8✔
1806
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1807
    DnstapMessage message(std::move(data), DnstapMessage::MessageType::client_response, d_identity, &response->ids.origRemote, &response->ids.origDest, protocol, reinterpret_cast<const char*>(response->getData().data()), response->getData().size(), &response->getQueryRealTime(), &now, boost::none, httpProtocol);
8✔
1808
    {
8✔
1809
      if (d_alterFunc) {
8!
1810
        auto lock = g_lua.lock();
8✔
1811
        (*d_alterFunc)(response, &message);
8✔
1812
      }
8✔
1813
    }
8✔
1814

1815
    data = message.getBuffer();
8✔
1816
    remoteLoggerQueueData(*d_logger, data);
8✔
1817

1818
    return Action::None;
8✔
1819
  }
8✔
1820
  [[nodiscard]] std::string toString() const override
1821
  {
×
1822
    return "log response as dnstap to " + (d_logger ? d_logger->toString() : "");
×
1823
  }
×
1824

1825
private:
1826
  std::string d_identity;
1827
  std::shared_ptr<RemoteLoggerInterface> d_logger;
1828
  std::optional<std::function<void(DNSResponse*, DnstapMessage*)>> d_alterFunc;
1829
};
1830

1831
class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable
1832
{
1833
public:
1834
  // this action does not stop the processing
1835
  RemoteLogResponseAction(RemoteLogActionConfiguration& config) :
1836
    d_tagsToExport(std::move(config.tagsToExport)), d_metas(std::move(config.metas)), d_logger(config.logger), d_alterFunc(std::move(config.alterResponseFunc)), d_serverID(config.serverID), d_ipEncryptKey(config.ipEncryptKey), d_ipEncryptMethod(config.ipEncryptMethod), d_exportExtendedErrorsToMeta(std::move(config.exportExtendedErrorsToMeta)), d_includeCNAME(config.includeCNAME), d_delay(config.delay)
40✔
1837
  {
40✔
1838
    if (!d_ipEncryptKey.empty() && d_ipEncryptMethod == "ipcrypt-pfx") {
40✔
1839
      d_ipcrypt2 = pdns::ipcrypt2::IPCrypt2(pdns::ipcrypt2::IPCryptMethod::pfx, d_ipEncryptKey);
2✔
1840
    }
2✔
1841
  }
40✔
1842
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
1843
  {
42✔
1844
    (void)ruleresult;
42✔
1845
    if (!response->ids.d_protoBufData) {
42✔
1846
      response->ids.d_protoBufData = std::make_unique<InternalQueryState::ProtoBufData>();
27✔
1847
    }
27✔
1848
    if (!response->ids.d_protoBufData->uniqueId) {
42✔
1849
      response->ids.d_protoBufData->uniqueId = getUniqueID();
27✔
1850
    }
27✔
1851

1852
    DNSDistProtoBufMessage message(*response, d_includeCNAME);
42✔
1853
    if (!d_serverID.empty()) {
42✔
1854
      message.setServerIdentity(d_serverID);
24✔
1855
    }
24✔
1856

1857
#ifdef HAVE_IPCIPHER
42✔
1858
    if (!d_ipEncryptKey.empty() && d_ipEncryptMethod == "legacy") {
42✔
1859
      message.setRequestor(encryptCA(response->ids.origRemote, d_ipEncryptKey));
2✔
1860
    }
2✔
1861
#endif /* HAVE_IPCIPHER */
42✔
1862
    if (d_ipcrypt2) {
42✔
1863
      auto encryptedAddress = d_ipcrypt2->encrypt(response->ids.origRemote);
2✔
1864
      encryptedAddress.setPort(response->ids.origRemote.getPort());
2✔
1865
      message.setRequestor(encryptedAddress);
2✔
1866
    }
2✔
1867

1868
    if (d_tagsToExport) {
42✔
1869
      addTagsToProtobuf(message, *response, *d_tagsToExport);
1✔
1870
    }
1✔
1871

1872
    addMetaDataToProtobuf(message, *response, d_metas);
42✔
1873

1874
    if (d_exportExtendedErrorsToMeta) {
42✔
1875
      addExtendedDNSErrorToProtobuf(message, *response, *d_exportExtendedErrorsToMeta);
1✔
1876
    }
1✔
1877

1878
    if (d_alterFunc) {
42✔
1879
      auto lock = g_lua.lock();
4✔
1880
      (*d_alterFunc)(response, &message);
4✔
1881
    }
4✔
1882

1883
    static thread_local std::string data;
42✔
1884
    data.clear();
42✔
1885
    message.serialize(data, !d_delay);
42✔
1886

1887
    if (!response->ids.d_rawProtobufContent.empty()) {
42✔
1888
      data.insert(data.end(), response->ids.d_rawProtobufContent.begin(), response->ids.d_rawProtobufContent.end());
1✔
1889
    }
1✔
1890

1891
    if (d_delay) {
42✔
1892
      response->ids.delayedResponseMsgs.emplace_back(data, std::shared_ptr<RemoteLoggerInterface>(d_logger));
4✔
1893
    }
4✔
1894
    else {
38✔
1895
      d_logger->queueData(data);
38✔
1896
    }
38✔
1897

1898
    return Action::None;
42✔
1899
  }
42✔
1900
  [[nodiscard]] std::string toString() const override
1901
  {
×
1902
    return "remote log response to " + (d_logger ? d_logger->toString() : "");
×
1903
  }
×
1904

1905
private:
1906
  std::optional<std::unordered_set<std::string>> d_tagsToExport;
1907
  std::vector<std::pair<std::string, ProtoBufMetaKey>> d_metas;
1908
  std::shared_ptr<RemoteLoggerInterface> d_logger;
1909
  std::optional<std::function<void(DNSResponse*, DNSDistProtoBufMessage*)>> d_alterFunc;
1910
  std::string d_serverID;
1911
  std::string d_ipEncryptKey;
1912
  std::string d_ipEncryptMethod;
1913
  std::optional<pdns::ipcrypt2::IPCrypt2> d_ipcrypt2{std::nullopt};
1914
  std::optional<std::string> d_exportExtendedErrorsToMeta{std::nullopt};
1915
  bool d_includeCNAME;
1916
  bool d_delay{false};
1917
};
1918

1919
#endif /* DISABLE_PROTOBUF */
1920

1921
class DropResponseAction : public DNSResponseAction
1922
{
1923
public:
1924
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
1925
  {
9✔
1926
    (void)response;
9✔
1927
    (void)ruleresult;
9✔
1928
    return Action::Drop;
9✔
1929
  }
9✔
1930
  [[nodiscard]] std::string toString() const override
1931
  {
×
1932
    return "drop";
×
1933
  }
×
1934
};
1935

1936
class AllowResponseAction : public DNSResponseAction
1937
{
1938
public:
1939
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
1940
  {
2✔
1941
    (void)response;
2✔
1942
    (void)ruleresult;
2✔
1943
    return Action::Allow;
2✔
1944
  }
2✔
1945
  [[nodiscard]] std::string toString() const override
1946
  {
2✔
1947
    return "allow";
2✔
1948
  }
2✔
1949
};
1950

1951
class DelayResponseAction : public DNSResponseAction
1952
{
1953
public:
1954
  DelayResponseAction(int msec) :
1955
    d_msec(msec)
4✔
1956
  {
4✔
1957
  }
4✔
1958
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
1959
  {
3✔
1960
    (void)response;
3✔
1961
    *ruleresult = std::to_string(d_msec);
3✔
1962
    return Action::Delay;
3✔
1963
  }
3✔
1964
  [[nodiscard]] std::string toString() const override
1965
  {
×
1966
    return "delay by " + std::to_string(d_msec) + " ms";
×
1967
  }
×
1968

1969
private:
1970
  int d_msec;
1971
};
1972

1973
class SNMPTrapResponseAction : public DNSResponseAction
1974
{
1975
public:
1976
  // this action does not stop the processing
1977
  SNMPTrapResponseAction(std::string reason) :
1978
    d_reason(std::move(reason))
×
1979
  {
×
1980
  }
×
1981
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
1982
  {
×
1983
    (void)ruleresult;
×
1984
    if (g_snmpAgent != nullptr && dnsdist::configuration::getImmutableConfiguration().d_snmpTrapsEnabled) {
×
1985
      g_snmpAgent->sendDNSTrap(*response, d_reason);
×
1986
    }
×
1987

1988
    return Action::None;
×
1989
  }
×
1990
  [[nodiscard]] std::string toString() const override
1991
  {
×
1992
    return "send SNMP trap";
×
1993
  }
×
1994

1995
private:
1996
  std::string d_reason;
1997
};
1998

1999
class SetTagResponseAction : public DNSResponseAction
2000
{
2001
public:
2002
  // this action does not stop the processing
2003
  SetTagResponseAction(std::string tag, std::string value) :
2004
    d_tag(std::move(tag)), d_value(std::move(value))
4✔
2005
  {
4✔
2006
  }
4✔
2007
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
2008
  {
3✔
2009
    (void)ruleresult;
3✔
2010
    response->setTag(d_tag, d_value);
3✔
2011

2012
    return Action::None;
3✔
2013
  }
3✔
2014
  [[nodiscard]] std::string toString() const override
2015
  {
×
2016
    return "set tag '" + d_tag + "' to value '" + d_value + "'";
×
2017
  }
×
2018

2019
private:
2020
  std::string d_tag;
2021
  std::string d_value;
2022
};
2023

2024
class ClearRecordTypesResponseAction : public DNSResponseAction, public boost::noncopyable
2025
{
2026
public:
2027
  ClearRecordTypesResponseAction(std::unordered_set<QType> qtypes) :
2028
    d_qtypes(std::move(qtypes))
4✔
2029
  {
4✔
2030
  }
4✔
2031

2032
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
2033
  {
3✔
2034
    (void)ruleresult;
3✔
2035
    if (!d_qtypes.empty()) {
3!
2036
      clearDNSPacketRecordTypes(response->getMutableData(), d_qtypes);
3✔
2037
    }
3✔
2038
    return DNSResponseAction::Action::None;
3✔
2039
  }
3✔
2040

2041
  [[nodiscard]] std::string toString() const override
2042
  {
×
2043
    return "clear record types";
×
2044
  }
×
2045

2046
private:
2047
  std::unordered_set<QType> d_qtypes;
2048
};
2049

2050
class ContinueAction : public DNSAction
2051
{
2052
public:
2053
  // this action does not stop the processing
2054
  ContinueAction(std::shared_ptr<DNSAction>& action) :
2055
    d_action(action)
2✔
2056
  {
2✔
2057
  }
2✔
2058

2059
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
2060
  {
2✔
2061
    (void)ruleresult;
2✔
2062
    if (d_action) {
2!
2063
      /* call the action */
2064
      auto action = (*d_action)(dnsquestion, ruleresult);
2✔
2065
      bool drop = false;
2✔
2066
      /* apply the changes if needed (pool selection, flags, etc */
2067
      processRulesResult(action, *dnsquestion, *ruleresult, drop);
2✔
2068
    }
2✔
2069

2070
    /* but ignore the resulting action no matter what */
2071
    return Action::None;
2✔
2072
  }
2✔
2073

2074
  [[nodiscard]] std::string toString() const override
2075
  {
×
2076
    if (d_action) {
×
2077
      return "continue after: " + (d_action ? d_action->toString() : "");
×
2078
    }
×
2079
    return "no op";
×
2080
  }
×
2081

2082
private:
2083
  std::shared_ptr<DNSAction> d_action;
2084
};
2085

2086
#if defined(HAVE_DNS_OVER_HTTPS) || defined(HAVE_DNS_OVER_HTTP3)
2087
class HTTPStatusAction : public DNSAction
2088
{
2089
public:
2090
  HTTPStatusAction(uint16_t code, PacketBuffer body, std::string contentType, const dnsdist::ResponseConfig& responseConfig) :
2091
    d_responseConfig(responseConfig), d_body(std::move(body)), d_contentType(std::move(contentType)), d_code(code)
20✔
2092
  {
20✔
2093
  }
20✔
2094

2095
  DNSAction::Action operator()([[maybe_unused]] DNSQuestion* dnsquestion, std::string* ruleresult) const override
2096
  {
8✔
2097
    (void)ruleresult;
8✔
2098
#if defined(HAVE_DNS_OVER_HTTPS)
8✔
2099
    if (dnsquestion->ids.du) {
8✔
2100
      dnsquestion->ids.du->setHTTPResponse(d_code, PacketBuffer(d_body), d_contentType);
6✔
2101
      dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [this](dnsheader& header) {
6✔
2102
        header.qr = true; // for good measure
6✔
2103
        setResponseHeadersFromConfig(header, d_responseConfig);
6✔
2104
        return true;
6✔
2105
      });
6✔
2106
      return Action::HeaderModify;
6✔
2107
    }
6✔
2108
#endif /* HAVE_DNS_OVER_HTTPS */
2✔
2109
#if defined(HAVE_DNS_OVER_HTTP3)
2✔
2110
    if (dnsquestion->ids.doh3u) {
2!
2111
      dnsquestion->ids.doh3u->setHTTPResponse(d_code, PacketBuffer(d_body), d_contentType);
2✔
2112
      dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [this](dnsheader& header) {
2✔
2113
        header.qr = true; // for good measure
2✔
2114
        setResponseHeadersFromConfig(header, d_responseConfig);
2✔
2115
        return true;
2✔
2116
      });
2✔
2117
      return Action::HeaderModify;
2✔
2118
    }
2✔
2119
#endif /* HAVE_DNS_OVER_HTTP3 */
×
2120
    return Action::None;
×
2121
  }
2✔
2122

2123
  [[nodiscard]] std::string toString() const override
2124
  {
×
2125
    return "return an HTTP status of " + std::to_string(d_code);
×
2126
  }
×
2127

2128
private:
2129
  dnsdist::ResponseConfig d_responseConfig;
2130
  PacketBuffer d_body;
2131
  std::string d_contentType;
2132
  int d_code;
2133
};
2134
#endif /* HAVE_DNS_OVER_HTTPS || HAVE_DNS_OVER_HTTP3 */
2135

2136
#if defined(HAVE_LMDB) || defined(HAVE_CDB)
2137
class KeyValueStoreLookupAction : public DNSAction
2138
{
2139
public:
2140
  // this action does not stop the processing
2141
  KeyValueStoreLookupAction(std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey, std::string destinationTag) :
2142
    d_kvs(kvs), d_key(lookupKey), d_tag(std::move(destinationTag))
44✔
2143
  {
44✔
2144
  }
44✔
2145

2146
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
2147
  {
114✔
2148
    (void)ruleresult;
114✔
2149
    std::vector<std::string> keys = d_key->getKeys(*dnsquestion);
114✔
2150
    std::string result;
114✔
2151
    for (const auto& key : keys) {
166✔
2152
      if (d_kvs->getValue(key, result)) {
166✔
2153
        break;
72✔
2154
      }
72✔
2155
    }
166✔
2156

2157
    dnsquestion->setTag(d_tag, std::move(result));
114✔
2158

2159
    return Action::None;
114✔
2160
  }
114✔
2161

2162
  [[nodiscard]] std::string toString() const override
2163
  {
×
2164
    return "lookup key-value store based on '" + d_key->toString() + "' and set the result in tag '" + d_tag + "'";
×
2165
  }
×
2166

2167
private:
2168
  std::shared_ptr<KeyValueStore> d_kvs;
2169
  std::shared_ptr<KeyValueLookupKey> d_key;
2170
  std::string d_tag;
2171
};
2172

2173
class KeyValueStoreRangeLookupAction : public DNSAction
2174
{
2175
public:
2176
  // this action does not stop the processing
2177
  KeyValueStoreRangeLookupAction(std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey, std::string destinationTag) :
2178
    d_kvs(kvs), d_key(lookupKey), d_tag(std::move(destinationTag))
×
2179
  {
×
2180
  }
×
2181

2182
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
2183
  {
×
2184
    (void)ruleresult;
×
2185
    std::vector<std::string> keys = d_key->getKeys(*dnsquestion);
×
2186
    std::string result;
×
2187
    for (const auto& key : keys) {
×
2188
      if (d_kvs->getRangeValue(key, result)) {
×
2189
        break;
×
2190
      }
×
2191
    }
×
2192

2193
    dnsquestion->setTag(d_tag, std::move(result));
×
2194

2195
    return Action::None;
×
2196
  }
×
2197

2198
  [[nodiscard]] std::string toString() const override
2199
  {
×
2200
    return "do a range-based lookup in key-value store based on '" + d_key->toString() + "' and set the result in tag '" + d_tag + "'";
×
2201
  }
×
2202

2203
private:
2204
  std::shared_ptr<KeyValueStore> d_kvs;
2205
  std::shared_ptr<KeyValueLookupKey> d_key;
2206
  std::string d_tag;
2207
};
2208
#endif /* defined(HAVE_LMDB) || defined(HAVE_CDB) */
2209

2210
class SetMaxReturnedTTLAction : public DNSAction
2211
{
2212
public:
2213
  SetMaxReturnedTTLAction(uint32_t cap) :
2214
    d_cap(cap)
×
2215
  {
×
2216
  }
×
2217

2218
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
2219
  {
×
2220
    (void)ruleresult;
×
2221
    dnsquestion->ids.ttlCap = d_cap;
×
2222
    return DNSAction::Action::None;
×
2223
  }
×
2224

2225
  [[nodiscard]] std::string toString() const override
2226
  {
×
2227
    return "cap the TTL of the returned response to " + std::to_string(d_cap);
×
2228
  }
×
2229

2230
private:
2231
  uint32_t d_cap;
2232
};
2233

2234
class SetMaxReturnedTTLResponseAction : public DNSResponseAction
2235
{
2236
public:
2237
  SetMaxReturnedTTLResponseAction(uint32_t cap) :
2238
    d_cap(cap)
×
2239
  {
×
2240
  }
×
2241

2242
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
2243
  {
×
2244
    (void)ruleresult;
×
2245
    response->ids.ttlCap = d_cap;
×
2246
    return DNSResponseAction::Action::None;
×
2247
  }
×
2248

2249
  [[nodiscard]] std::string toString() const override
2250
  {
×
2251
    return "cap the TTL of the returned response to " + std::to_string(d_cap);
×
2252
  }
×
2253

2254
private:
2255
  uint32_t d_cap;
2256
};
2257

2258
class NegativeAndSOAAction : public DNSAction
2259
{
2260
public:
2261
  NegativeAndSOAAction(bool nxd, DNSName zone, uint32_t ttl, DNSName mname, DNSName rname, dnsdist::actions::SOAParams params, bool soaInAuthoritySection, dnsdist::ResponseConfig responseConfig) :
2262
    d_responseConfig(responseConfig), d_zone(std::move(zone)), d_mname(std::move(mname)), d_rname(std::move(rname)), d_ttl(ttl), d_params(params), d_nxd(nxd), d_soaInAuthoritySection(soaInAuthoritySection)
8✔
2263
  {
8✔
2264
  }
8✔
2265

2266
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
2267
  {
16✔
2268
    (void)ruleresult;
16✔
2269
    if (!setNegativeAndAdditionalSOA(*dnsquestion, d_nxd, d_zone, d_ttl, d_mname, d_rname, d_params.serial, d_params.refresh, d_params.retry, d_params.expire, d_params.minimum, d_soaInAuthoritySection)) {
16!
2270
      return Action::None;
×
2271
    }
×
2272

2273
    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [this](dnsheader& header) {
16✔
2274
      setResponseHeadersFromConfig(header, d_responseConfig);
16✔
2275
      return true;
16✔
2276
    });
16✔
2277

2278
    return Action::Allow;
16✔
2279
  }
16✔
2280

2281
  [[nodiscard]] std::string toString() const override
2282
  {
×
2283
    return std::string(d_nxd ? "NXD" : "NODATA") + " with SOA";
×
2284
  }
×
2285

2286
private:
2287
  dnsdist::ResponseConfig d_responseConfig;
2288
  DNSName d_zone;
2289
  DNSName d_mname;
2290
  DNSName d_rname;
2291
  uint32_t d_ttl;
2292
  dnsdist::actions::SOAParams d_params;
2293
  bool d_nxd;
2294
  bool d_soaInAuthoritySection;
2295
};
2296

2297
class SetProxyProtocolValuesAction : public DNSAction
2298
{
2299
public:
2300
  // this action does not stop the processing
2301
  SetProxyProtocolValuesAction(const std::vector<std::pair<uint8_t, std::string>>& values)
2302
  {
4✔
2303
    d_values.reserve(values.size());
4✔
2304
    for (const auto& value : values) {
6✔
2305
      d_values.push_back({value.second, value.first});
6✔
2306
    }
6✔
2307
  }
4✔
2308

2309
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
2310
  {
3✔
2311
    (void)ruleresult;
3✔
2312
    if (!dnsquestion->proxyProtocolValues) {
3✔
2313
      dnsquestion->proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>();
2✔
2314
    }
2✔
2315

2316
    *(dnsquestion->proxyProtocolValues) = d_values;
3✔
2317

2318
    return Action::None;
3✔
2319
  }
3✔
2320

2321
  [[nodiscard]] std::string toString() const override
2322
  {
5✔
2323
    return "set Proxy-Protocol values";
5✔
2324
  }
5✔
2325

2326
private:
2327
  std::vector<ProxyProtocolValue> d_values;
2328
};
2329

2330
class SetAdditionalProxyProtocolValueAction : public DNSAction
2331
{
2332
public:
2333
  // this action does not stop the processing
2334
  SetAdditionalProxyProtocolValueAction(uint8_t type, std::string value) :
2335
    d_value(std::move(value)), d_type(type)
4✔
2336
  {
4✔
2337
  }
4✔
2338

2339
  DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
2340
  {
66✔
2341
    (void)ruleresult;
66✔
2342
    if (!dnsquestion->proxyProtocolValues) {
66!
2343
      dnsquestion->proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>();
×
2344
    }
×
2345

2346
    dnsquestion->proxyProtocolValues->push_back({d_value, d_type});
66✔
2347

2348
    return Action::None;
66✔
2349
  }
66✔
2350

2351
  [[nodiscard]] std::string toString() const override
2352
  {
×
2353
    return "add a Proxy-Protocol value of type " + std::to_string(d_type);
×
2354
  }
×
2355

2356
private:
2357
  std::string d_value;
2358
  uint8_t d_type;
2359
};
2360

2361
class SetReducedTTLResponseAction : public DNSResponseAction, public boost::noncopyable
2362
{
2363
public:
2364
  // this action does not stop the processing
2365
  SetReducedTTLResponseAction(uint8_t percentage) :
2366
    d_ratio(percentage / 100.0)
2✔
2367
  {
2✔
2368
  }
2✔
2369

2370
  DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
2371
  {
2✔
2372
    (void)ruleresult;
2✔
2373
    // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
2374
    auto visitor = [&](uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl) {
2✔
2375
      (void)section;
2✔
2376
      (void)qclass;
2✔
2377
      (void)qtype;
2✔
2378
      return ttl * d_ratio;
2✔
2379
    };
2✔
2380
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2381
    editDNSPacketTTL(reinterpret_cast<char*>(response->getMutableData().data()), response->getData().size(), visitor);
2✔
2382
    return DNSResponseAction::Action::None;
2✔
2383
  }
2✔
2384

2385
  [[nodiscard]] std::string toString() const override
2386
  {
×
2387
    return "reduce ttl to " + std::to_string(d_ratio * 100) + " percent of its value";
×
2388
  }
×
2389

2390
private:
2391
  double d_ratio{1.0};
2392
};
2393

2394
class SetExtendedDNSErrorAction : public DNSAction
2395
{
2396
public:
2397
  // this action does not stop the processing
2398
  SetExtendedDNSErrorAction(uint16_t infoCode, const std::string& extraText)
2399
  {
2✔
2400
    d_ede.infoCode = infoCode;
2✔
2401
    d_ede.extraText = extraText;
2✔
2402
  }
2✔
2403

2404
  DNSAction::Action operator()(DNSQuestion* dnsQuestion, std::string* ruleresult) const override
2405
  {
10✔
2406
    (void)ruleresult;
10✔
2407
    dnsQuestion->ids.d_extendedError = std::make_unique<EDNSExtendedError>(d_ede);
10✔
2408

2409
    return DNSAction::Action::None;
10✔
2410
  }
10✔
2411

2412
  [[nodiscard]] std::string toString() const override
2413
  {
×
2414
    return "set EDNS Extended DNS Error to " + std::to_string(d_ede.infoCode) + (d_ede.extraText.empty() ? std::string() : std::string(": \"") + d_ede.extraText + std::string("\""));
×
2415
  }
×
2416

2417
private:
2418
  EDNSExtendedError d_ede;
2419
};
2420

2421
class SetExtendedDNSErrorResponseAction : public DNSResponseAction
2422
{
2423
public:
2424
  // this action does not stop the processing
2425
  SetExtendedDNSErrorResponseAction(uint16_t infoCode, const std::string& extraText)
2426
  {
5✔
2427
    d_ede.infoCode = infoCode;
5✔
2428
    d_ede.extraText = extraText;
5✔
2429
  }
5✔
2430

2431
  DNSResponseAction::Action operator()(DNSResponse* dnsResponse, std::string* ruleresult) const override
2432
  {
5✔
2433
    (void)ruleresult;
5✔
2434
    dnsResponse->ids.d_extendedError = std::make_unique<EDNSExtendedError>(d_ede);
5✔
2435

2436
    return DNSResponseAction::Action::None;
5✔
2437
  }
5✔
2438

2439
  [[nodiscard]] std::string toString() const override
2440
  {
×
2441
    return "set EDNS Extended DNS Error to " + std::to_string(d_ede.infoCode) + (d_ede.extraText.empty() ? std::string() : std::string(": \"") + d_ede.extraText + std::string("\""));
×
2442
  }
×
2443

2444
private:
2445
  EDNSExtendedError d_ede;
2446
};
2447

2448
class LimitTTLResponseAction : public DNSResponseAction, public boost::noncopyable
2449
{
2450
public:
2451
  LimitTTLResponseAction(uint32_t min, uint32_t max = std::numeric_limits<uint32_t>::max(), std::unordered_set<QType> types = {}) :
2452
    d_types(std::move(types)), d_min(min), d_max(max)
6✔
2453
  {
6✔
2454
  }
6✔
2455

2456
  DNSResponseAction::Action operator()(DNSResponse* dnsResponse, std::string* ruleresult) const override
2457
  {
11✔
2458
    (void)ruleresult;
11✔
2459
    dnsdist::PacketMangling::restrictDNSPacketTTLs(dnsResponse->getMutableData(), d_min, d_max, d_types);
11✔
2460
    return DNSResponseAction::Action::None;
11✔
2461
  }
11✔
2462

2463
  std::string toString() const override
2464
  {
×
2465
    std::string result = "limit ttl (" + std::to_string(d_min) + " <= ttl <= " + std::to_string(d_max);
×
2466
    if (!d_types.empty()) {
×
2467
      bool first = true;
×
2468
      result += ", types in [";
×
2469
      for (const auto& type : d_types) {
×
2470
        if (first) {
×
2471
          first = false;
×
2472
        }
×
2473
        else {
×
2474
          result += " ";
×
2475
        }
×
2476
        result += type.toString();
×
2477
      }
×
2478
      result += "]";
×
2479
    }
×
2480
    result += +")";
×
2481
    return result;
×
2482
  }
×
2483

2484
private:
2485
  std::unordered_set<QType> d_types;
2486
  uint32_t d_min{0};
2487
  uint32_t d_max{std::numeric_limits<uint32_t>::max()};
2488
};
2489

2490
std::shared_ptr<DNSAction> getLuaAction(dnsdist::actions::LuaActionFunction function)
2491
{
162✔
2492
  return std::shared_ptr<DNSAction>(new LuaAction(std::move(function)));
162✔
2493
}
162✔
2494

2495
std::shared_ptr<DNSAction> getLuaFFIAction(dnsdist::actions::LuaActionFFIFunction function)
2496
{
36✔
2497
  return std::shared_ptr<DNSAction>(new LuaFFIAction(std::move(function)));
36✔
2498
}
36✔
2499

2500
std::shared_ptr<DNSResponseAction> getLuaResponseAction(dnsdist::actions::LuaResponseActionFunction function)
2501
{
50✔
2502
  return std::shared_ptr<DNSResponseAction>(new LuaResponseAction(std::move(function)));
50✔
2503
}
50✔
2504

2505
std::shared_ptr<DNSResponseAction> getLuaFFIResponseAction(dnsdist::actions::LuaResponseActionFFIFunction function)
2506
{
18✔
2507
  return std::shared_ptr<DNSResponseAction>(new LuaFFIResponseAction(std::move(function)));
18✔
2508
}
18✔
2509

2510
#ifndef DISABLE_PROTOBUF
2511
std::shared_ptr<DNSAction> getRemoteLogAction(RemoteLogActionConfiguration& config)
2512
{
18✔
2513
  return std::shared_ptr<DNSAction>(new RemoteLogAction(config));
18✔
2514
}
18✔
2515

2516
std::shared_ptr<DNSResponseAction> getRemoteLogResponseAction(RemoteLogActionConfiguration& config)
2517
{
40✔
2518
  return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(config));
40✔
2519
}
40✔
2520

2521
std::shared_ptr<DNSAction> getDnstapLogAction(const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, std::optional<DnstapAlterFunction> alterFunc)
2522
{
12✔
2523
  return std::shared_ptr<DNSAction>(new DnstapLogAction(identity, logger, std::move(alterFunc)));
12✔
2524
}
12✔
2525

2526
std::shared_ptr<DNSResponseAction> getDnstapLogResponseAction(const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, std::optional<DnstapAlterResponseFunction> alterFunc)
2527
{
4✔
2528
  return std::shared_ptr<DNSResponseAction>(new DnstapLogResponseAction(identity, logger, std::move(alterFunc)));
4✔
2529
}
4✔
2530
#endif /* DISABLE_PROTOBUF */
2531

2532
#if defined(HAVE_LMDB) || defined(HAVE_CDB)
2533
std::shared_ptr<DNSAction> getKeyValueStoreLookupAction(std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey, const std::string& destinationTag)
2534
{
44✔
2535
  return std::shared_ptr<DNSAction>(new KeyValueStoreLookupAction(kvs, lookupKey, destinationTag));
44✔
2536
}
44✔
2537

2538
std::shared_ptr<DNSAction> getKeyValueStoreRangeLookupAction(std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey, const std::string& destinationTag)
2539
{
×
2540
  return std::shared_ptr<DNSAction>(new KeyValueStoreRangeLookupAction(kvs, lookupKey, destinationTag));
×
2541
}
×
2542
#endif /* defined(HAVE_LMDB) || defined(HAVE_CDB) */
2543

2544
std::shared_ptr<DNSAction> getHTTPStatusAction([[maybe_unused]] uint16_t status, [[maybe_unused]] PacketBuffer&& body, [[maybe_unused]] const std::string& contentType, [[maybe_unused]] const dnsdist::ResponseConfig& responseConfig)
2545
{
20✔
2546
#if defined(HAVE_DNS_OVER_HTTPS)
20✔
2547
  return std::shared_ptr<DNSAction>(new HTTPStatusAction(status, std::move(body), contentType, responseConfig));
20✔
2548
#else
2549
  throw std::runtime_error("Unsupported HTTPStatus action");
2550
#endif
2551
}
20✔
2552

2553
std::shared_ptr<DNSResponseAction> getLimitTTLResponseAction(uint32_t min, uint32_t max, std::unordered_set<QType> types)
2554
{
6✔
2555
  return std::shared_ptr<DNSResponseAction>(new LimitTTLResponseAction(min, max, std::move(types)));
6✔
2556
}
6✔
2557

2558
std::shared_ptr<DNSResponseAction> getMinTTLResponseAction(uint32_t min)
2559
{
×
2560
  return std::shared_ptr<DNSResponseAction>(new LimitTTLResponseAction(min));
×
2561
}
×
2562

2563
std::shared_ptr<DNSResponseAction> getClearRecordTypesResponseAction(std::unordered_set<QType> types)
2564
{
4✔
2565
  return std::shared_ptr<DNSResponseAction>(new ClearRecordTypesResponseAction(std::move(types)));
4✔
2566
}
4✔
2567

2568
std::shared_ptr<DNSAction> getContinueAction(std::shared_ptr<DNSAction> action)
2569
{
2✔
2570
  return std::shared_ptr<DNSAction>(new ContinueAction(action));
2✔
2571
}
2✔
2572

2573
std::shared_ptr<DNSAction> getNegativeAndSOAAction(bool nxd, const DNSName& zone, uint32_t ttl, const DNSName& mname, const DNSName& rname, const SOAParams& params, bool soaInAuthority, dnsdist::ResponseConfig responseConfig)
2574
{
8✔
2575
  return std::shared_ptr<DNSAction>(new NegativeAndSOAAction(nxd, zone, ttl, mname, rname, params, soaInAuthority, responseConfig));
8✔
2576
}
8✔
2577

2578
std::shared_ptr<DNSAction> getRCodeAction(uint8_t rcode, const dnsdist::ResponseConfig& responseConfig)
2579
{
110✔
2580
  return std::shared_ptr<DNSAction>(new RCodeAction(rcode, responseConfig));
110✔
2581
}
110✔
2582

2583
std::shared_ptr<DNSAction> getERCodeAction(uint8_t rcode, const dnsdist::ResponseConfig& responseConfig)
2584
{
2✔
2585
  return std::shared_ptr<DNSAction>(new ERCodeAction(rcode, responseConfig));
2✔
2586
}
2✔
2587

2588
std::shared_ptr<DNSAction> getSetECSAction(const std::string& ipv4)
2589
{
6✔
2590
  return std::shared_ptr<DNSAction>(new SetECSAction(Netmask(ipv4)));
6✔
2591
}
6✔
2592

2593
std::shared_ptr<DNSAction> getSetECSAction(const std::string& ipv4, const std::string& ipv6)
2594
{
×
2595
  return std::shared_ptr<DNSAction>(new SetECSAction(Netmask(ipv4), Netmask(ipv6)));
×
2596
}
×
2597

2598
std::shared_ptr<DNSAction> getSpoofAction(const std::vector<ComboAddress>& addresses, const dnsdist::ResponseConfig& config)
2599
{
177✔
2600
  return std::shared_ptr<DNSAction>(new SpoofAction(addresses, config));
177✔
2601
}
177✔
2602

2603
std::shared_ptr<DNSAction> getSpoofAction(const std::vector<std::string>& rawRDatas, std::optional<uint16_t> qtypeForAny, const dnsdist::ResponseConfig& config)
2604
{
18✔
2605
  return std::shared_ptr<DNSAction>(new SpoofAction(rawRDatas, qtypeForAny, config));
18✔
2606
}
18✔
2607

2608
std::shared_ptr<DNSAction> getSpoofAction(const DNSName& cname, const dnsdist::ResponseConfig& config)
2609
{
26✔
2610
  return std::shared_ptr<DNSAction>(new SpoofAction(cname, config));
26✔
2611
}
26✔
2612

2613
std::shared_ptr<DNSAction> getSpoofAction(const PacketBuffer& packet)
2614
{
2✔
2615
  return std::shared_ptr<DNSAction>(new SpoofAction(packet));
2✔
2616
}
2✔
2617

2618
std::shared_ptr<DNSAction> getSpoofSVCAction(const std::vector<SVCRecordParameters>& parameters, const dnsdist::ResponseConfig& responseConfig)
2619
{
8✔
2620
  return std::shared_ptr<DNSAction>(new SpoofSVCAction(parameters, responseConfig));
8✔
2621
}
8✔
2622

2623
std::shared_ptr<DNSAction> getSetMaxReturnedTTLAction(uint32_t max)
2624
{
×
2625
  return std::shared_ptr<DNSAction>(new SetMaxReturnedTTLAction(max));
×
2626
}
×
2627

2628
std::shared_ptr<DNSResponseAction> getSetMaxReturnedTTLResponseAction(uint32_t max)
2629
{
×
2630
  return std::shared_ptr<DNSResponseAction>(new SetMaxReturnedTTLResponseAction(max));
×
2631
}
×
2632

2633
std::shared_ptr<DNSResponseAction> getSetMaxTTLResponseAction(uint32_t max)
2634
{
×
2635
  return std::shared_ptr<DNSResponseAction>(new LimitTTLResponseAction(0, max));
×
2636
}
×
2637

2638
std::shared_ptr<DNSAction> getSetProxyProtocolValuesAction(const std::vector<std::pair<uint8_t, std::string>>& values)
2639
{
4✔
2640
  return std::shared_ptr<DNSAction>(new SetProxyProtocolValuesAction(values));
4✔
2641
}
4✔
2642

2643
std::shared_ptr<DNSAction> getTeeAction(const ComboAddress& rca, std::optional<ComboAddress> lca, bool addECS, bool addProxyProtocol)
2644
{
6✔
2645
  return std::shared_ptr<DNSAction>(new TeeAction(rca, lca, addECS, addProxyProtocol));
6✔
2646
}
6✔
2647

2648
#include "dnsdist-actions-factory-generated-body.hh"
2649
#include "dnsdist-response-actions-factory-generated-body.hh"
2650
}
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