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

PowerDNS / pdns / 18743945403

23 Oct 2025 09:29AM UTC coverage: 65.845% (+0.02%) from 65.829%
18743945403

Pull #16356

github

web-flow
Merge 8a2027ef1 into efa3637e8
Pull Request #16356: auth 5.0: backport "pdnsutil: fix b2b-migrate to from sql to non-sql"

42073 of 92452 branches covered (45.51%)

Branch coverage included in aggregate %.

128008 of 165855 relevant lines covered (77.18%)

6379935.17 hits per line

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

56.49
/pdns/recursordist/rec-lua-conf.cc
1
#include "config.h"
2
#include "ext/luawrapper/include/LuaContext.hpp"
3

4
#include <fstream>
5
#include <thread>
6
#include "namespaces.hh"
7
#include "logger.hh"
8
#include "lua-base4.hh"
9
#include "rec-lua-conf.hh"
10
#include "sortlist.hh"
11
#include "filterpo.hh"
12
#include "syncres.hh"
13
#include "rpzloader.hh"
14
#include "base64.hh"
15
#include "remote_logger.hh"
16
#include "validate.hh"
17
#include "validate-recursor.hh"
18
#include "root-dnssec.hh"
19
#include "rec-system-resolve.hh"
20

21
GlobalStateHolder<LuaConfigItems> g_luaconfs;
22

23
/* SO HOW DOES THIS WORK! AND PLEASE PAY ATTENTION!
24
   This function can be called at any time. It is expected to overwrite all the contents
25
   of LuaConfigItems, which is held in a GlobalStateHolder for RCU properties.
26

27
   This function can be called again at a later date, so you must make sure that anything you
28
   allow to be configured from here lives in g_luaconfs AND NOWHERE ELSE.
29

30
   If someone loads an empty Lua file, the default LuaConfigItems struct MUST MAKE SENSE.
31

32
   To make this easy on you, here is a LuaConfigItems constructor where you
33
   can set sane defaults:
34
*/
35

36
LuaConfigItems::LuaConfigItems()
37
{
1,159✔
38
  DNSName root("."); // don't use g_rootdnsname here, it might not exist yet
1,159✔
39
  for (const auto& dsRecord : rootDSs) {
2,318✔
40
    auto dsRecContent = std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(dsRecord));
2,318✔
41
    dsAnchors[root].emplace(*dsRecContent);
2,318✔
42
  }
2,318✔
43
}
1,159✔
44

45
/* DID YOU READ THE STORY ABOVE? */
46

47
bool operator==(const ProtobufExportConfig& configA, const ProtobufExportConfig& configB)
48
{
110✔
49
  // clang-format off
50
  return configA.exportTypes          == configB.exportTypes       &&
110✔
51
         configA.servers              == configB.servers           &&
110!
52
         configA.maxQueuedEntries     == configB.maxQueuedEntries  &&
110!
53
         configA.timeout              == configB.timeout           &&
110!
54
         configA.reconnectWaitTime    == configB.reconnectWaitTime &&
110!
55
         configA.asyncConnect         == configB.asyncConnect      &&
110!
56
         configA.enabled              == configB.enabled           &&
110!
57
         configA.logQueries           == configB.logQueries        &&
110!
58
         configA.logResponses         == configB.logResponses      &&
110!
59
         configA.taggedOnly           == configB.taggedOnly        &&
110!
60
         configA.logMappedFrom        == configB.logMappedFrom;
110!
61
  // clang-format on
62
}
110✔
63

64
bool operator!=(const ProtobufExportConfig& configA, const ProtobufExportConfig& configB)
65
{
110✔
66
  return !(configA == configB);
110✔
67
}
110✔
68

69
bool operator==(const FrameStreamExportConfig& configA, const FrameStreamExportConfig& configB)
70
{
25✔
71
  // clang-format off
72
  return configA.enabled              == configB.enabled              &&
25!
73
         configA.logQueries           == configB.logQueries           &&
25!
74
         configA.logResponses         == configB.logResponses         &&
25!
75
         configA.logNODs              == configB.logNODs              &&
25!
76
         configA.logUDRs              == configB.logUDRs              &&
25!
77
         configA.bufferHint           == configB.bufferHint           &&
25!
78
         configA.flushTimeout         == configB.flushTimeout         &&
25!
79
         configA.inputQueueSize       == configB.inputQueueSize       &&
25!
80
         configA.outputQueueSize      == configB.outputQueueSize      &&
25!
81
         configA.queueNotifyThreshold == configB.queueNotifyThreshold &&
25!
82
         configA.reopenInterval       == configB.reopenInterval       &&
25!
83
         configA.servers              == configB.servers;
25!
84
  // clang-format on
85
}
25✔
86

87
bool operator!=(const FrameStreamExportConfig& configA, const FrameStreamExportConfig& configB)
88
{
25✔
89
  return !(configA == configB);
25✔
90
}
25✔
91

92
template <typename C>
93
typename C::value_type::second_type constGet(const C& c, const std::string& name)
94
{
95
  auto iter = c.find(name);
96
  if (iter == c.end())
97
    return 0;
98
  return iter->second;
99
}
100

101
typedef std::unordered_map<std::string, boost::variant<bool, uint32_t, std::string, std::vector<std::pair<int, std::string>>>> rpzOptions_t;
102

103
static void parseRPZParameters(const rpzOptions_t& have, RPZTrackerParams& params)
104
{
17✔
105
  if (have.count("policyName") != 0) {
17✔
106
    params.polName = boost::get<std::string>(have.at("policyName"));
15✔
107
  }
15✔
108
  if (have.count("defpol") != 0) {
17✔
109
    params.defpol = DNSFilterEngine::Policy();
2✔
110
    params.defpol->d_kind = (DNSFilterEngine::PolicyKind)boost::get<uint32_t>(have.at("defpol"));
2✔
111
    params.defpol->setName(params.polName);
2✔
112
    if (params.defpol->d_kind == DNSFilterEngine::PolicyKind::Custom) {
2!
113
      params.defcontent = boost::get<string>(have.at("defcontent"));
×
114
      if (!params.defpol->d_custom) {
×
115
        params.defpol->d_custom = make_unique<DNSFilterEngine::Policy::CustomData>();
×
116
      }
×
117
      params.defpol->d_custom->push_back(DNSRecordContent::make(QType::CNAME, QClass::IN,
×
118
                                                                params.defcontent));
×
119

120
      if (have.count("defttl") != 0) {
×
121
        params.defpol->d_ttl = static_cast<int32_t>(boost::get<uint32_t>(have.at("defttl")));
×
122
      }
×
123
      else {
×
124
        params.defpol->d_ttl = -1; // get it from the zone
×
125
      }
×
126
    }
×
127

128
    if (have.count("defpolOverrideLocalData") != 0) {
2✔
129
      params.defpolOverrideLocal = boost::get<bool>(have.at("defpolOverrideLocalData"));
1✔
130
    }
1✔
131
  }
2✔
132
  if (have.count("maxTTL") != 0) {
17!
133
    params.maxTTL = boost::get<uint32_t>(have.at("maxTTL"));
×
134
  }
×
135
  if (have.count("zoneSizeHint") != 0) {
17!
136
    params.zoneXFRParams.zoneSizeHint = static_cast<size_t>(boost::get<uint32_t>(have.at("zoneSizeHint")));
×
137
  }
×
138
  if (have.count("tags") != 0) {
17✔
139
    const auto& tagsTable = boost::get<std::vector<std::pair<int, std::string>>>(have.at("tags"));
1✔
140
    std::unordered_set<std::string> tags;
1✔
141
    for (const auto& tag : tagsTable) {
2✔
142
      tags.insert(tag.second);
2✔
143
      params.tags.insert(tag.second);
2✔
144
    }
2✔
145
  }
1✔
146
  if (have.count("overridesGettag") != 0) {
17!
147
    params.defpolOverrideLocal = boost::get<bool>(have.at("overridesGettag"));
×
148
  }
×
149
  if (have.count("extendedErrorCode") != 0) {
17✔
150
    auto code = boost::get<uint32_t>(have.at("extendedErrorCode"));
1✔
151
    if (code > std::numeric_limits<uint16_t>::max()) {
1!
152
      throw std::runtime_error("Invalid extendedErrorCode value " + std::to_string(code) + " in RPZ configuration");
×
153
    }
×
154
    params.extendedErrorCode = code;
1✔
155
    if (have.count("extendedErrorExtra") != 0) {
1!
156
      params.extendedErrorExtra = boost::get<std::string>(have.at("extendedErrorExtra"));
1✔
157
    }
1✔
158
  }
1✔
159
  if (have.count("includeSOA") != 0) {
17✔
160
    params.includeSOA = boost::get<bool>(have.at("includeSOA"));
2✔
161
  }
2✔
162
  if (have.count("ignoreDuplicates") != 0) {
17!
163
    params.ignoreDuplicates = boost::get<bool>(have.at("ignoreDuplicates"));
×
164
  }
×
165
}
17✔
166

167
typedef std::unordered_map<std::string, boost::variant<bool, uint64_t, std::string, std::vector<std::pair<int, std::string>>>> protobufOptions_t;
168

169
static void parseProtobufOptions(const boost::optional<protobufOptions_t>& vars, ProtobufExportConfig& config)
170
{
22✔
171
  if (!vars) {
22✔
172
    return;
7✔
173
  }
7✔
174
  const auto& have = *vars;
15✔
175

176
  if (have.count("timeout") != 0) {
15!
177
    config.timeout = boost::get<uint64_t>(have.at("timeout"));
×
178
  }
×
179

180
  if (have.count("maxQueuedEntries") != 0) {
15!
181
    config.maxQueuedEntries = boost::get<uint64_t>(have.at("maxQueuedEntries"));
×
182
  }
×
183

184
  if (have.count("reconnectWaitTime") != 0) {
15!
185
    config.reconnectWaitTime = boost::get<uint64_t>(have.at("reconnectWaitTime"));
×
186
  }
×
187

188
  if (have.count("asyncConnect") != 0) {
15!
189
    config.asyncConnect = boost::get<bool>(have.at("asyncConnect"));
×
190
  }
×
191

192
  if (have.count("taggedOnly") != 0) {
15✔
193
    config.taggedOnly = boost::get<bool>(have.at("taggedOnly"));
1✔
194
  }
1✔
195

196
  if (have.count("logQueries") != 0) {
15✔
197
    config.logQueries = boost::get<bool>(have.at("logQueries"));
12✔
198
  }
12✔
199

200
  if (have.count("logResponses") != 0) {
15✔
201
    config.logResponses = boost::get<bool>(have.at("logResponses"));
12✔
202
  }
12✔
203

204
  if (have.count("logMappedFrom") != 0) {
15✔
205
    config.logMappedFrom = boost::get<bool>(have.at("logMappedFrom"));
2✔
206
  }
2✔
207

208
  if (have.count("exportTypes") != 0) {
15✔
209
    config.exportTypes.clear();
1✔
210

211
    auto types = boost::get<std::vector<std::pair<int, std::string>>>(have.at("exportTypes"));
1✔
212
    for (const auto& pair : types) {
5✔
213
      const auto& type = pair.second;
5✔
214

215
      QType qtype;
5✔
216
      try {
5✔
217
        qtype = std::stoul(type);
5✔
218
      }
5✔
219
      catch (const std::exception& ex) {
5✔
220
        qtype = QType::chartocode(type.c_str());
5✔
221
        if (qtype == 0) {
5!
222
          throw std::runtime_error("Unknown QType '" + type + "' in protobuf's export types");
×
223
        }
×
224
      }
5✔
225
      config.exportTypes.insert(qtype);
5✔
226
    }
5✔
227
  }
1✔
228
}
15✔
229

230
#ifdef HAVE_FSTRM
231
typedef std::unordered_map<std::string, boost::variant<bool, uint64_t, std::string, std::vector<std::pair<int, std::string>>>> frameStreamOptions_t;
232

233
static void parseFrameStreamOptions(const boost::optional<frameStreamOptions_t>& vars, FrameStreamExportConfig& config)
234
{
5✔
235
  if (!vars) {
5✔
236
    return;
2✔
237
  }
2✔
238
  const auto& have = *vars;
3✔
239

240
  if (have.count("logQueries") != 0) {
3✔
241
    config.logQueries = boost::get<bool>(have.at("logQueries"));
1✔
242
  }
1✔
243
  if (have.count("logResponses") != 0) {
3!
244
    config.logResponses = boost::get<bool>(have.at("logResponses"));
×
245
  }
×
246
  if (have.count("logNODs") != 0) {
3✔
247
    config.logNODs = boost::get<bool>(have.at("logNODs"));
2✔
248
  }
2✔
249
  if (have.count("logUDRs") != 0) {
3✔
250
    config.logUDRs = boost::get<bool>(have.at("logUDRs"));
2✔
251
  }
2✔
252

253
  if (have.count("bufferHint") != 0) {
3!
254
    config.bufferHint = boost::get<uint64_t>(have.at("bufferHint"));
×
255
  }
×
256
  if (have.count("flushTimeout") != 0) {
3!
257
    config.flushTimeout = boost::get<uint64_t>(have.at("flushTimeout"));
×
258
  }
×
259
  if (have.count("inputQueueSize") != 0) {
3!
260
    config.inputQueueSize = boost::get<uint64_t>(have.at("inputQueueSize"));
×
261
  }
×
262
  if (have.count("outputQueueSize") != 0) {
3!
263
    config.outputQueueSize = boost::get<uint64_t>(have.at("outputQueueSize"));
×
264
  }
×
265
  if (have.count("queueNotifyThreshold") != 0) {
3!
266
    config.queueNotifyThreshold = boost::get<uint64_t>(have.at("queueNotifyThreshold"));
×
267
  }
×
268
  if (have.count("reopenInterval") != 0) {
3!
269
    config.reopenInterval = boost::get<uint64_t>(have.at("reopenInterval"));
×
270
  }
×
271
}
3✔
272
#endif /* HAVE_FSTRM */
273

274
static void rpzPrimary(LuaConfigItems& lci, const boost::variant<string, std::vector<std::pair<int, string>>>& primaries_, const string& zoneName, const boost::optional<rpzOptions_t>& options)
275
{
2✔
276
  RPZTrackerParams params;
2✔
277
  params.zoneXFRParams.name = zoneName;
2✔
278
  params.polName = zoneName;
2✔
279

280
  std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
2✔
281
  if (primaries_.type() == typeid(string)) {
2!
282
    params.zoneXFRParams.primaries.emplace_back(boost::get<std::string>(primaries_));
×
283
  }
×
284
  else {
2✔
285
    for (const auto& primary : boost::get<std::vector<std::pair<int, std::string>>>(primaries_)) {
4✔
286
      params.zoneXFRParams.primaries.emplace_back(primary.second);
4✔
287
    }
4✔
288
  }
2✔
289

290
  try {
2✔
291
    if (options) {
2!
292
      auto& have = *options;
2✔
293
      parseRPZParameters(have, params);
2✔
294

295
      if (have.count("tsigname") != 0) {
2!
296
        params.zoneXFRParams.tsigtriplet.name = DNSName(toLower(boost::get<string>(have.at("tsigname"))));
×
297
        params.zoneXFRParams.tsigtriplet.algo = DNSName(toLower(boost::get<string>(have.at("tsigalgo"))));
×
298
        if (B64Decode(boost::get<string>(have.at("tsigsecret")), params.zoneXFRParams.tsigtriplet.secret) != 0) {
×
299
          throw std::runtime_error("TSIG secret is not valid Base-64 encoded");
×
300
        }
×
301
      }
×
302
      if (have.count("refresh") != 0) {
2!
303
        params.zoneXFRParams.refreshFromConf = boost::get<uint32_t>(have.at("refresh"));
2✔
304
        if (params.zoneXFRParams.refreshFromConf == 0) {
2!
305
          SLOG(g_log << Logger::Warning << "rpzPrimary refresh value of 0 ignored" << endl,
×
306
               lci.d_slog->info(Logr::Warning, "rpzPrimary refresh value of 0 ignored"));
×
307
        }
×
308
      }
2✔
309

310
      if (have.count("maxReceivedMBytes") != 0) {
2!
311
        params.zoneXFRParams.maxReceivedMBytes = static_cast<size_t>(boost::get<uint32_t>(have.at("maxReceivedMBytes")));
×
312
      }
×
313

314
      if (have.count("localAddress") != 0) {
2!
315
        params.zoneXFRParams.localAddress = ComboAddress(boost::get<string>(have.at("localAddress")));
×
316
      }
×
317

318
      if (have.count("axfrTimeout") != 0) {
2!
319
        params.zoneXFRParams.xfrTimeout = static_cast<uint16_t>(boost::get<uint32_t>(have.at("axfrTimeout")));
×
320
      }
×
321

322
      if (have.count("seedFile") != 0) {
2!
323
        params.seedFileName = boost::get<std::string>(have.at("seedFile"));
×
324
      }
×
325

326
      if (have.count("dumpFile") != 0) {
2✔
327
        params.dumpZoneFileName = boost::get<std::string>(have.at("dumpFile"));
1✔
328
      }
1✔
329
    }
2✔
330

331
    if (params.zoneXFRParams.localAddress != ComboAddress()) {
2!
332
      // We were passed a localAddress, check if its AF matches the primaries'
333
      for (const auto& nameOrIP : params.zoneXFRParams.primaries) {
×
334
        auto primary = pdns::fromNameOrIP(nameOrIP, 53, lci.d_slog);
×
335
        if (params.zoneXFRParams.localAddress.sin4.sin_family != primary.sin4.sin_family) {
×
336
          throw PDNSException("Primary address(" + primary.toString() + ") is not of the same Address Family as the local address (" + params.zoneXFRParams.localAddress.toString() + ").");
×
337
        }
×
338
      }
×
339
    }
×
340
    lci.rpzs.emplace_back(params);
2✔
341
  }
2✔
342
  catch (const std::exception& e) {
2✔
343
    SLOG(g_log << Logger::Error << "Problem configuring 'rpzPrimary': " << e.what() << endl,
×
344
         lci.d_slog->error(Logr::Error, e.what(), "Exception configuring 'rpzPrimary'", "exception", Logging::Loggable("std::exception")));
×
345
  }
×
346
  catch (const PDNSException& e) {
2✔
347
    SLOG(g_log << Logger::Error << "Problem configuring 'rpzPrimary': " << e.reason << endl,
×
348
         lci.d_slog->error(Logr::Error, e.reason, "Exception configuring 'rpzPrimary'", Logging::Loggable("PDNSException")));
×
349
  }
×
350
}
2✔
351

352
// A wrapper class that loads the standard Lua defintions into the context, so that we can use things like pdns.A
353
class RecLuaConfigContext : public BaseLua4
354
{
355
public:
356
  RecLuaConfigContext() :
357
    BaseLua4("")
358
  {
154✔
359
    prepareContext();
154✔
360
  }
154✔
361
  void postPrepareContext() override
362
  {
154✔
363
    // clang-format off
364
    d_pd.push_back({"AdditionalMode", in_t{
154✔
365
          {"Ignore", static_cast<int>(AdditionalMode::Ignore)},
154✔
366
          {"CacheOnly", static_cast<int>(AdditionalMode::CacheOnly)},
154✔
367
          {"CacheOnlyRequireAuth", static_cast<int>(AdditionalMode::CacheOnlyRequireAuth)},
154✔
368
          {"ResolveImmediately", static_cast<int>(AdditionalMode::ResolveImmediately)},
154✔
369
          {"ResolveDeferred", static_cast<int>(AdditionalMode::ResolveDeferred)}
154✔
370
        }});
154✔
371
  }
154✔
372
  void postLoad() override
373
  {
×
374
  }
×
375
  LuaContext* operator->()
376
  {
3,388✔
377
    return d_lw.get();
3,388✔
378
  }
3,388✔
379
};
380

381
void loadRecursorLuaConfig(const std::string& fname, ProxyMapping& proxyMapping, LuaConfigItems& newLuaConfig) // NOLINT(readability-function-cognitive-complexity)
382
{
154✔
383
  LuaConfigItems lci;
154✔
384
  if (g_slog) {
154!
385
    lci.d_slog = g_slog->withName("luaconfig");
154✔
386
  }
154✔
387

388
  RecLuaConfigContext Lua;
154✔
389

390
  if (fname.empty())
154!
391
    return;
×
392
  ifstream ifs(fname);
154✔
393
  if (!ifs)
154!
394
    throw PDNSException("Cannot open file '" + fname + "': " + stringerror());
×
395

396
  auto luaconfsLocal = g_luaconfs.getLocal();
154✔
397
  lci.generation = luaconfsLocal->generation + 1;
154✔
398

399
  Lua->writeFunction("clearSortlist", [&lci]() { lci.sortlist.clear(); });
154✔
400

401
  /* we can get: "1.2.3.4"
402
                 {"1.2.3.4", "4.5.6.7"}
403
                 {"1.2.3.4", {"4.5.6.7", "8.9.10.11"}}
404
  */
405

406
  map<string, DNSFilterEngine::PolicyKind> pmap{
154✔
407
    {"NoAction", DNSFilterEngine::PolicyKind::NoAction},
154✔
408
    {"Drop", DNSFilterEngine::PolicyKind::Drop},
154✔
409
    {"NXDOMAIN", DNSFilterEngine::PolicyKind::NXDOMAIN},
154✔
410
    {"NODATA", DNSFilterEngine::PolicyKind::NODATA},
154✔
411
    {"Truncate", DNSFilterEngine::PolicyKind::Truncate},
154✔
412
    {"Custom", DNSFilterEngine::PolicyKind::Custom}};
154✔
413
  Lua->writeVariable("Policy", pmap);
154✔
414

415
  Lua->writeFunction("rpzFile", [&lci](const string& filename, boost::optional<rpzOptions_t> options) {
154✔
416
    RPZTrackerParams params;
15✔
417
    params.zoneXFRParams.name = filename;
15✔
418
    params.polName = "rpzFile";
15✔
419
    if (options) {
15!
420
      parseRPZParameters(*options, params);
15✔
421
    }
15✔
422
    lci.rpzs.emplace_back(params);
15✔
423
  });
15✔
424

425
  Lua->writeFunction("rpzMaster", [&lci](const boost::variant<string, std::vector<std::pair<int, string>>>& primaries_, const string& zoneName, const boost::optional<rpzOptions_t>& options) {
154✔
426
    SLOG(g_log << Logger::Warning << "'rpzMaster' is deprecated and will be removed in a future release, use 'rpzPrimary' instead" << endl,
2✔
427
         lci.d_slog->info(Logr::Warning, "'rpzMaster' is deprecated and will be removed in a future release, use 'rpzPrimary' instead"));
2✔
428
    rpzPrimary(lci, primaries_, zoneName, options);
2✔
429
  });
2✔
430
  Lua->writeFunction("rpzPrimary", [&lci](const boost::variant<string, std::vector<std::pair<int, string>>>& primaries_, const string& zoneName, const boost::optional<rpzOptions_t>& options) {
154✔
431
    rpzPrimary(lci, primaries_, zoneName, options);
×
432
  });
×
433

434
  typedef std::unordered_map<std::string, boost::variant<uint32_t, std::string>> zoneToCacheOptions_t;
154✔
435

436
  Lua->writeFunction("zoneToCache", [&lci](const string& zoneName, const string& method, const boost::variant<string, std::vector<std::pair<int, string>>>& srcs, boost::optional<zoneToCacheOptions_t> options) {
154✔
437
    try {
×
438
      RecZoneToCache::Config conf;
×
439
      DNSName validZoneName(zoneName);
×
440
      conf.d_zone = zoneName;
×
441
      const set<string> methods = {"axfr", "url", "file"};
×
442
      if (methods.count(method) == 0) {
×
443
        throw std::runtime_error("unknwon method '" + method + "'");
×
444
      }
×
445
      conf.d_method = method;
×
446
      if (srcs.type() == typeid(std::string)) {
×
447
        conf.d_sources.push_back(boost::get<std::string>(srcs));
×
448
      }
×
449
      else {
×
450
        for (const auto& src : boost::get<std::vector<std::pair<int, std::string>>>(srcs)) {
×
451
          conf.d_sources.push_back(src.second);
×
452
        }
×
453
      }
×
454
      if (conf.d_sources.size() == 0) {
×
455
        throw std::runtime_error("at least one source required");
×
456
      }
×
457
      if (options) {
×
458
        auto& have = *options;
×
459
        if (have.count("timeout")) {
×
460
          conf.d_timeout = boost::get<uint32_t>(have.at("timeout"));
×
461
        }
×
462
        if (have.count("tsigname")) {
×
463
          conf.d_tt.name = DNSName(toLower(boost::get<string>(have.at("tsigname"))));
×
464
          conf.d_tt.algo = DNSName(toLower(boost::get<string>(have.at("tsigalgo"))));
×
465
          if (B64Decode(boost::get<string>(have.at("tsigsecret")), conf.d_tt.secret)) {
×
466
            throw std::runtime_error("TSIG secret is not valid Base-64 encoded");
×
467
          }
×
468
        }
×
469
        if (have.count("maxReceivedMBytes")) {
×
470
          conf.d_maxReceivedBytes = static_cast<size_t>(boost::get<uint32_t>(have.at("maxReceivedMBytes")));
×
471
          conf.d_maxReceivedBytes *= 1024 * 1024;
×
472
        }
×
473
        if (have.count("localAddress")) {
×
474
          conf.d_local = ComboAddress(boost::get<string>(have.at("localAddress")));
×
475
        }
×
476
        if (have.count("refreshPeriod")) {
×
477
          conf.d_refreshPeriod = boost::get<uint32_t>(have.at("refreshPeriod"));
×
478
        }
×
479
        if (have.count("retryOnErrorPeriod")) {
×
480
          conf.d_retryOnError = boost::get<uint32_t>(have.at("retryOnErrorPeriod"));
×
481
        }
×
482
        const map<string, pdns::ZoneMD::Config> nameToVal = {
×
483
          {"ignore", pdns::ZoneMD::Config::Ignore},
×
484
          {"validate", pdns::ZoneMD::Config::Validate},
×
485
          {"require", pdns::ZoneMD::Config::Require},
×
486
        };
×
487
        if (have.count("zonemd")) {
×
488
          string zonemdValidation = boost::get<string>(have.at("zonemd"));
×
489
          auto it = nameToVal.find(zonemdValidation);
×
490
          if (it == nameToVal.end()) {
×
491
            throw std::runtime_error(zonemdValidation + " is not a valid value for `zonemd`");
×
492
          }
×
493
          else {
×
494
            conf.d_zonemd = it->second;
×
495
          }
×
496
        }
×
497
        if (have.count("dnssec")) {
×
498
          string dnssec = boost::get<string>(have.at("dnssec"));
×
499
          auto it = nameToVal.find(dnssec);
×
500
          if (it == nameToVal.end()) {
×
501
            throw std::runtime_error(dnssec + " is not a valid value for `dnssec`");
×
502
          }
×
503
          else {
×
504
            conf.d_dnssec = it->second;
×
505
          }
×
506
        }
×
507
      }
×
508

509
      lci.ztcConfigs[validZoneName] = std::move(conf);
×
510
    }
×
511
    catch (const std::exception& e) {
×
512
      SLOG(g_log << Logger::Error << "Problem configuring zoneToCache for zone '" << zoneName << "': " << e.what() << endl,
×
513
           lci.d_slog->error(Logr::Error, e.what(), "Problem configuring zoneToCache", "zone", Logging::Loggable(zoneName),
×
514
                             "exception", Logging::Loggable("std::exception")));
×
515
    }
×
516
  });
×
517

518
  typedef vector<pair<int, boost::variant<string, vector<pair<int, string>>>>> argvec_t;
154✔
519
  Lua->writeFunction("addSortList",
154✔
520
                     [&lci](const std::string& formask_,
154✔
521
                            const boost::variant<string, argvec_t>& masks,
154✔
522
                            boost::optional<int> order_) {
154✔
523
                       try {
1✔
524
                         Netmask formask(formask_);
1✔
525
                         int order = order_ ? (*order_) : lci.sortlist.getMaxOrder(formask) + 1;
1!
526
                         if (auto str = boost::get<string>(&masks))
1!
527
                           lci.sortlist.addEntry(formask, Netmask(*str), order);
×
528
                         else {
1✔
529

530
                           auto vec = boost::get<argvec_t>(&masks);
1✔
531
                           for (const auto& e : *vec) {
5✔
532
                             if (auto s = boost::get<string>(&e.second)) {
5✔
533
                               lci.sortlist.addEntry(formask, Netmask(*s), order);
4✔
534
                             }
4✔
535
                             else {
1✔
536
                               const auto& v = boost::get<vector<pair<int, string>>>(e.second);
1✔
537
                               for (const auto& entry : v)
1✔
538
                                 lci.sortlist.addEntry(formask, Netmask(entry.second), order);
2✔
539
                             }
1✔
540
                             ++order;
5✔
541
                           }
5✔
542
                         }
1✔
543
                       }
1✔
544
                       catch (std::exception& e) {
1✔
545
                         SLOG(g_log << Logger::Error << "Error in addSortList: " << e.what() << endl,
×
546
                              lci.d_slog->error(Logr::Error, e.what(), "Error in addSortList", "exception",  Logging::Loggable("std::exception")));
×
547
                       }
×
548
                     });
1✔
549

550
  Lua->writeFunction("addTA", [&lci](const std::string& who, const std::string& what) {
154✔
551
    DNSName zone(who);
154✔
552
    auto ds = std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(what));
154✔
553
    lci.dsAnchors[zone].insert(*ds);
154✔
554
  });
154✔
555

556
  Lua->writeFunction("clearTA", [&lci](boost::optional<string> who) {
154✔
557
    if (who)
1!
558
      lci.dsAnchors.erase(DNSName(*who));
×
559
    else
1✔
560
      lci.dsAnchors.clear();
1✔
561
  });
1✔
562

563
  /* Remove in 4.3 */
564
  Lua->writeFunction("addDS", [&lci](const std::string& who, const std::string& what) {
154✔
565
    SLOG(g_log << Logger::Warning << "addDS is deprecated and will be removed in the future, switch to addTA" << endl,
1✔
566
         lci.d_slog->info(Logr::Warning, "addDS is deprecated and will be removed in the future, switch to addTA"));
1✔
567
    DNSName zone(who);
1✔
568
    auto ds = std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(what));
1✔
569
    lci.dsAnchors[zone].insert(*ds);
1✔
570
  });
1✔
571

572
  /* Remove in 4.3 */
573
  Lua->writeFunction("clearDS", [&lci](boost::optional<string> who) {
154✔
574
    SLOG(g_log << Logger::Warning << "clearDS is deprecated and will be removed in the future, switch to clearTA" << endl,
1✔
575
         lci.d_slog->info(Logr::Warning, "clearDS is deprecated and will be removed in the future, switch to clearTA"));
1✔
576
    if (who)
1!
577
      lci.dsAnchors.erase(DNSName(*who));
1✔
578
    else
×
579
      lci.dsAnchors.clear();
×
580
  });
1✔
581

582
  Lua->writeFunction("addNTA", [&lci](const std::string& who, const boost::optional<std::string> why) {
154✔
583
    if (why)
4✔
584
      lci.negAnchors[DNSName(who)] = static_cast<string>(*why);
2✔
585
    else
2✔
586
      lci.negAnchors[DNSName(who)] = "";
2✔
587
  });
4✔
588

589
  Lua->writeFunction("clearNTA", [&lci](boost::optional<string> who) {
154✔
590
    if (who)
×
591
      lci.negAnchors.erase(DNSName(*who));
×
592
    else
×
593
      lci.negAnchors.clear();
×
594
  });
×
595

596
  Lua->writeFunction("readTrustAnchorsFromFile", [&lci](const std::string& fnamearg, const boost::optional<uint32_t> interval) {
154✔
597
    uint32_t realInterval = 24;
1✔
598
    if (interval) {
1!
599
      realInterval = static_cast<uint32_t>(*interval);
×
600
    }
×
601
    lci.trustAnchorFileInfo.fname = fnamearg;
1✔
602
    lci.trustAnchorFileInfo.interval = realInterval;
1✔
603
  });
1✔
604

605
  Lua->writeFunction("setProtobufMasks", [&lci](const uint8_t maskV4, uint8_t maskV6) {
154✔
606
    lci.protobufMaskV4 = maskV4;
1✔
607
    lci.protobufMaskV6 = maskV6;
1✔
608
  });
1✔
609

610
  Lua->writeFunction("protobufServer", [&lci](boost::variant<const std::string, const std::unordered_map<int, std::string>> servers, boost::optional<protobufOptions_t> vars) {
154✔
611
    if (!lci.protobufExportConfig.enabled) {
19!
612

613
      lci.protobufExportConfig.enabled = true;
19✔
614

615
      try {
19✔
616
        if (servers.type() == typeid(std::string)) {
19!
617
          auto server = boost::get<const std::string>(servers);
×
618

619
          lci.protobufExportConfig.servers.emplace_back(server);
×
620
        }
×
621
        else {
19✔
622
          auto serversMap = boost::get<const std::unordered_map<int, std::string>>(servers);
19✔
623
          for (const auto& serverPair : serversMap) {
38✔
624
            lci.protobufExportConfig.servers.emplace_back(serverPair.second);
38✔
625
          }
38✔
626
        }
19✔
627

628
        parseProtobufOptions(vars, lci.protobufExportConfig);
19✔
629
      }
19✔
630
      catch (std::exception& e) {
19✔
631
        SLOG(g_log << Logger::Error << "Error while adding protobuf logger: " << e.what() << endl,
×
632
             lci.d_slog->error(Logr::Error, e.what(), "Exception while adding protobuf logger", "exception", Logging::Loggable("std::exception")));
×
633
      }
×
634
      catch (PDNSException& e) {
19✔
635
        SLOG(g_log << Logger::Error << "Error while adding protobuf logger: " << e.reason << endl,
×
636
             lci.d_slog->error(Logr::Error, e.reason, "Exception while adding protobuf logger", "exception", Logging::Loggable("PDNSException")));
×
637
      }
×
638
    }
19✔
639
    else {
×
640
      SLOG(g_log << Logger::Error << "Only one protobufServer() directive can be configured, we already have " << lci.protobufExportConfig.servers.at(0).toString() << endl,
×
641
           lci.d_slog->info(Logr::Error, "Only one protobufServer() directive can be configured", "existing", Logging::Loggable(lci.protobufExportConfig.servers.at(0).toString())));
×
642
    }
×
643
  });
19✔
644

645
  Lua->writeFunction("outgoingProtobufServer", [&lci](boost::variant<const std::string, const std::unordered_map<int, std::string>> servers, boost::optional<protobufOptions_t> vars) {
154✔
646
    if (!lci.outgoingProtobufExportConfig.enabled) {
3!
647

648
      lci.outgoingProtobufExportConfig.enabled = true;
3✔
649

650
      try {
3✔
651
        if (servers.type() == typeid(std::string)) {
3!
652
          auto server = boost::get<const std::string>(servers);
×
653

654
          lci.outgoingProtobufExportConfig.servers.emplace_back(server);
×
655
        }
×
656
        else {
3✔
657
          auto serversMap = boost::get<const std::unordered_map<int, std::string>>(servers);
3✔
658
          for (const auto& serverPair : serversMap) {
6✔
659
            lci.outgoingProtobufExportConfig.servers.emplace_back(serverPair.second);
6✔
660
          }
6✔
661
        }
3✔
662

663
        parseProtobufOptions(vars, lci.outgoingProtobufExportConfig);
3✔
664
      }
3✔
665
      catch (std::exception& e) {
3✔
666
        SLOG(g_log << Logger::Error << "Error while starting outgoing protobuf logger: " << e.what() << endl,
×
667
             lci.d_slog->error(Logr::Error, "Exception while starting outgoing protobuf logger", "exception", Logging::Loggable("std::exception")));
×
668
      }
×
669
      catch (PDNSException& e) {
3✔
670
        SLOG(g_log << Logger::Error << "Error while starting outgoing protobuf logger: " << e.reason << endl,
×
671
             lci.d_slog->error(Logr::Error, "Exception while starting outgoing protobuf logger", "exception", Logging::Loggable("PDNSException")));
×
672
      }
×
673
    }
3✔
674
    else {
×
675
      SLOG(g_log << Logger::Error << "Only one outgoingProtobufServer() directive can be configured, we already have " << lci.outgoingProtobufExportConfig.servers.at(0).toString() << endl,
×
676
           lci.d_slog->info(Logr::Error, "Only one outgoingProtobufServer() directive can be configured", "existing", Logging::Loggable(lci.outgoingProtobufExportConfig.servers.at(0).toString())));
×
677
    }
×
678
  });
3✔
679

680
#ifdef HAVE_FSTRM
154✔
681
  Lua->writeFunction("dnstapFrameStreamServer", [&lci](boost::variant<const std::string, const std::unordered_map<int, std::string>> servers, boost::optional<frameStreamOptions_t> vars) {
154✔
682
    if (!lci.frameStreamExportConfig.enabled) {
2!
683

684
      lci.frameStreamExportConfig.enabled = true;
2✔
685

686
      try {
2✔
687
        if (servers.type() == typeid(std::string)) {
2!
688
          auto server = boost::get<const std::string>(servers);
×
689
          if (!boost::starts_with(server, "/")) {
×
690
            ComboAddress parsecheck(server);
×
691
          }
×
692
          lci.frameStreamExportConfig.servers.emplace_back(server);
×
693
        }
×
694
        else {
2✔
695
          auto serversMap = boost::get<const std::unordered_map<int, std::string>>(servers);
2✔
696
          for (const auto& serverPair : serversMap) {
2✔
697
            lci.frameStreamExportConfig.servers.emplace_back(serverPair.second);
2✔
698
          }
2✔
699
        }
2✔
700

701
        parseFrameStreamOptions(vars, lci.frameStreamExportConfig);
2✔
702
      }
2✔
703
      catch (std::exception& e) {
2✔
704
        SLOG(g_log << Logger::Error << "Error reading config for dnstap framestream logger: " << e.what() << endl,
×
705
              lci.d_slog->error(Logr::Error, "Exception reading config for dnstap framestream logger", "exception", Logging::Loggable("std::exception")));
×
706
      }
×
707
      catch (PDNSException& e) {
2✔
708
        SLOG(g_log << Logger::Error << "Error reading config for dnstap framestream logger: " << e.reason << endl,
×
709
             lci.d_slog->error(Logr::Error, "Exception reading config for dnstap framestream logger", "exception", Logging::Loggable("PDNSException")));
×
710
      }
×
711
    }
2✔
712
    else {
×
713
      SLOG(g_log << Logger::Error << "Only one dnstapFrameStreamServer() directive can be configured, we already have " << lci.frameStreamExportConfig.servers.at(0) << endl,
×
714
           lci.d_slog->info(Logr::Error,  "Only one dnstapFrameStreamServer() directive can be configured",  "existing", Logging::Loggable(lci.frameStreamExportConfig.servers.at(0))));
×
715
    }
×
716
  });
2✔
717
  Lua->writeFunction("dnstapNODFrameStreamServer", [&lci](boost::variant<const std::string, const std::unordered_map<int, std::string>> servers, boost::optional<frameStreamOptions_t> vars) {
154✔
718
    if (!lci.nodFrameStreamExportConfig.enabled) {
3!
719
      lci.nodFrameStreamExportConfig.enabled = true;
3✔
720

721
      try {
3✔
722
        if (servers.type() == typeid(std::string)) {
3!
723
          auto server = boost::get<const std::string>(servers);
×
724
          if (!boost::starts_with(server, "/")) {
×
725
            ComboAddress parsecheck(server);
×
726
          }
×
727
          lci.nodFrameStreamExportConfig.servers.emplace_back(server);
×
728
        }
×
729
        else {
3✔
730
          auto serversMap = boost::get<const std::unordered_map<int, std::string>>(servers);
3✔
731
          for (const auto& serverPair : serversMap) {
3✔
732
            lci.nodFrameStreamExportConfig.servers.emplace_back(serverPair.second);
3✔
733
          }
3✔
734
        }
3✔
735

736
        parseFrameStreamOptions(vars, lci.nodFrameStreamExportConfig);
3✔
737
      }
3✔
738
      catch (std::exception& e) {
3✔
739
        SLOG(g_log << Logger::Error << "Error reading config for dnstap NOD framestream logger: " << e.what() << endl,
×
740
              lci.d_slog->error(Logr::Error, "Exception reading config for dnstap NOD framestream logger", "exception", Logging::Loggable("std::exception")));
×
741
      }
×
742
      catch (PDNSException& e) {
3✔
743
        SLOG(g_log << Logger::Error << "Error reading config for dnstap NOD framestream logger: " << e.reason << endl,
×
744
             lci.d_slog->error(Logr::Error, "Exception reading config for dnstap NOD framestream logger", "exception", Logging::Loggable("PDNSException")));
×
745
      }
×
746
    }
3✔
747
    else {
×
748
      SLOG(g_log << Logger::Error << "Only one dnstapNODFrameStreamServer() directive can be configured, we already have " << lci.nodFrameStreamExportConfig.servers.at(0) << endl,
×
749
           lci.d_slog->info(Logr::Error,  "Only one dnstapNODFrameStreamServer() directive can be configured",  "existing", Logging::Loggable(lci.nodFrameStreamExportConfig.servers.at(0))));
×
750
    }
×
751
  });
3✔
752
#endif /* HAVE_FSTRM */
154✔
753

754
  Lua->writeFunction("addAllowedAdditionalQType", [&lci](int qtype, std::unordered_map<int, int> targetqtypes, boost::optional<std::map<std::string, int>> options) {
154✔
755
    switch (qtype) {
5✔
756
    case QType::MX:
3✔
757
    case QType::SRV:
4✔
758
    case QType::SVCB:
4!
759
    case QType::HTTPS:
4!
760
    case QType::NAPTR:
5✔
761
      break;
5✔
762
    default:
×
763
      SLOG(g_log << Logger::Error << "addAllowedAdditionalQType does not support " << QType(qtype).toString() << endl,
×
764
           lci.d_slog->info(Logr::Error, "addAllowedAdditionalQType does not support this qtype", "qtype", Logging::Loggable(QType(qtype).toString())));
×
765
      return;
×
766
    }
5✔
767

768
    std::set<QType> targets;
5✔
769
    for (const auto& t : targetqtypes) {
11✔
770
      targets.emplace(QType(t.second));
11✔
771
    }
11✔
772

773
    AdditionalMode mode = AdditionalMode::CacheOnlyRequireAuth; // Always cheap and should be safe
5✔
774

775
    if (options) {
5✔
776
      if (const auto it = options->find("mode"); it != options->end()) {
4!
777
        mode = static_cast<AdditionalMode>(it->second);
4✔
778
        if (mode > AdditionalMode::ResolveDeferred) {
4!
779
          SLOG(g_log << Logger::Error << "addAllowedAdditionalQType: unknown mode " << it->second << endl,
×
780
               lci.d_slog->info(Logr::Error, "addAllowedAdditionalQType: unknown mode", "mode", Logging::Loggable( it->second)));
×
781
        }
×
782
      }
4✔
783
    }
4✔
784
    lci.allowAdditionalQTypes.insert_or_assign(qtype, pair(targets, mode));
5✔
785
  });
5✔
786

787
  Lua->writeFunction("addProxyMapping", [&proxyMapping,&lci](const string& netmaskArg, const string& addressArg, boost::optional<std::vector<pair<int,std::string>>> smnStrings) {
154✔
788
    try {
6✔
789
      Netmask netmask(netmaskArg);
6✔
790
      ComboAddress address(addressArg);
6✔
791
      boost::optional<SuffixMatchNode> smn;
6✔
792
      if (smnStrings) {
6✔
793
        smn = boost::make_optional(SuffixMatchNode{});
1✔
794
        for (const auto& el : *smnStrings) {
1✔
795
          smn->add(el.second);
1✔
796
        }
1✔
797
      }
1✔
798
      proxyMapping.insert_or_assign(netmask, {address, smn});
6✔
799
    }
6✔
800
    catch (std::exception& e) {
6✔
801
      SLOG(g_log << Logger::Error << "Error processing addProxyMapping: " << e.what() << endl,
×
802
           lci.d_slog->error(Logr::Error, e.what(), "Exception processing addProxyMapping", "exception", Logging::Loggable("std::exception")));
×
803
    }
×
804
    catch (PDNSException& e) {
6✔
805
      SLOG(g_log << Logger::Error << "Error processing addProxyMapping: " << e.reason << endl,
×
806
           lci.d_slog->error(Logr::Error, e.reason, "Exception processing addProxyMapping", "exception", Logging::Loggable("PDNSException")));
×
807
    }
×
808
  });
6✔
809

810
  try {
154✔
811
    Lua->executeCode(ifs);
154✔
812
    newLuaConfig = std::move(lci);
154✔
813
  }
154✔
814
  catch (const LuaContext::ExecutionErrorException& e) {
154✔
815
    SLOG(g_log << Logger::Error << "Unable to load Lua script from '" + fname + "': ",
×
816
         lci.d_slog->error(Logr::Error, e.what(),  "Unable to load Lua script", "file", Logging::Loggable(fname)));
×
817
    try {
×
818
      std::rethrow_if_nested(e);
×
819
    }
×
820
    catch (const std::exception& exp) {
×
821
      // exp is the exception that was thrown from inside the lambda
822
      SLOG(g_log << exp.what() << std::endl,
×
823
           lci.d_slog->error(Logr::Error, exp.what(), "Exception loading Lua", "exception", Logging::Loggable("std::exception")));
×
824
    }
×
825
    catch (const PDNSException& exp) {
×
826
      // exp is the exception that was thrown from inside the lambda
827
      SLOG(g_log << exp.reason << std::endl,
×
828
           lci.d_slog->error(Logr::Error, exp.reason, "Exception loading Lua", "exception", Logging::Loggable("PDNSException")));
×
829
    }
×
830
    throw;
×
831
  }
×
832
  catch (std::exception& err) {
154✔
833
    SLOG(g_log << Logger::Error << "Unable to load Lua script from '" + fname + "': " << err.what() << endl,
×
834
         lci.d_slog->error(Logr::Error, err.what(),  "Unable to load Lua script", "file", Logging::Loggable(fname), "exception", Logging::Loggable("std::exception")));
×
835
    throw;
×
836
  }
×
837
}
154✔
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