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

PowerDNS / pdns / 18748609987

23 Oct 2025 12:38PM UTC coverage: 73.046% (+4.3%) from 68.781%
18748609987

Pull #16362

github

web-flow
Merge fb6974bbe into dec9583d8
Pull Request #16362: rec: implements new feature to only generate OpenTelemtry Traces on certain conditions

38418 of 63316 branches covered (60.68%)

Branch coverage included in aggregate %.

122 of 136 new or added lines in 11 files covered. (89.71%)

53 existing lines in 11 files now uncovered.

127614 of 163983 relevant lines covered (77.82%)

5916487.93 hits per line

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

77.33
/pdns/recursordist/rec-rust-lib/cxxsupport.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

23
#include <fstream>
24
#include <regex>
25
#include <unistd.h>
26
#include <cstdio>
27
#include <libgen.h>
28

29
#include "namespaces.hh"
30
#include "arguments.hh"
31
#include "misc.hh"
32
#include "cxxsettings.hh"
33
#include "cxxsettings-private.hh"
34
#include "logger.hh"
35
#include "logging.hh"
36
#include "rec-lua-conf.hh"
37
#include "root-dnssec.hh"
38
#include "dnsrecords.hh"
39
#include "base64.hh"
40
#include "validate-recursor.hh"
41
#include "threadname.hh"
42
#include "iputils.hh"
43
#include "bridge.hh"
44
#include "rec-rust-lib/rust/web.rs.h"
45
#include "rec-rust-lib/rust/misc.rs.h"
46
#include "uuid-utils.hh"
47

48
::rust::Vec<::rust::String> pdns::settings::rec::getStrings(const std::string& name)
49
{
×
50
  ::rust::Vec<::rust::String> vec;
×
51
  to_yaml(vec, arg()[name]);
×
52
  return vec;
×
53
}
×
54

55
::rust::Vec<ForwardZone> pdns::settings::rec::getForwardZones(const string& name)
56
{
×
57
  ::rust::Vec<ForwardZone> vec;
×
58
  const auto recurse = name == "forward-zones-recurse";
×
59
  to_yaml(vec, arg()[name], recurse);
×
60
  return vec;
×
61
}
×
62

63
::rust::Vec<AuthZone> pdns::settings::rec::getAuthZones(const string& name)
64
{
×
65
  ::rust::Vec<AuthZone> vec;
×
66
  to_yaml(vec, arg()[name]);
×
67
  return vec;
×
68
}
×
69

70
void pdns::settings::rec::oldStyleForwardsFileToBridgeStruct(const std::string& file, ::rust::Vec<ForwardZone>& vec)
71
{
2✔
72
  auto filePtr = pdns::UniqueFilePtr(fopen(file.c_str(), "r"));
2✔
73
  if (!filePtr) {
2!
74
    throw PDNSException("Error opening forward-zones-file '" + file + "': " + stringerror());
×
75
  }
×
76

77
  string line;
2✔
78
  int linenum = 0;
2✔
79
  while (linenum++, stringfgets(filePtr.get(), line)) {
10✔
80
    boost::trim(line);
8✔
81
    if (line.length() == 0 || line.at(0) == '#') { // Comment line, skip to the next line
8✔
82
      continue;
4✔
83
    }
4✔
84
    auto [domain, instructions] = splitField(line, '=');
4✔
85
    instructions = splitField(instructions, '#').first; // Remove EOL comments
4✔
86
    boost::trim(domain);
4✔
87
    boost::trim(instructions);
4✔
88
    if (domain.empty() || instructions.empty()) {
4!
89
      throw PDNSException("Error parsing line " + std::to_string(linenum) + " of " + file);
×
90
    }
×
91
    bool allowNotify = false;
4✔
92
    bool recurse = false;
4✔
93
    for (; !domain.empty(); domain.erase(0, 1)) {
8!
94
      switch (domain[0]) {
8✔
95
      case '+':
2✔
96
        recurse = true;
2✔
97
        continue;
2✔
98
      case '^':
2✔
99
        allowNotify = true;
2✔
100
        continue;
2✔
101
      }
8✔
102
      break;
4✔
103
    }
8✔
104
    if (domain.empty()) {
4!
105
      throw PDNSException("Error parsing line " + std::to_string(linenum) + " of " + file);
×
106
    }
×
107
    ::rust::Vec<::rust::String> addresses;
4✔
108
    stringtok(addresses, instructions, ",; ");
4✔
109
    ForwardZone forwardzone{domain, std::move(addresses), recurse, allowNotify};
4✔
110
    vec.push_back(std::move(forwardzone));
4✔
111
  }
4✔
112
}
2✔
113

114
void pdns::settings::rec::oldStyleAllowFileToBridgeStruct(const std::string& file, ::rust::Vec<::rust::String>& vec)
115
{
×
116
  string line;
×
117
  ifstream ifs(file);
×
118
  if (!ifs) {
×
119
    int err = errno;
×
120
    throw runtime_error("Could not open '" + file + "': " + stringerror(err));
×
121
  }
×
122

123
  while (getline(ifs, line)) {
×
124
    auto pos = line.find('#');
×
125
    if (pos != string::npos) {
×
126
      line.resize(pos);
×
127
    }
×
128
    boost::trim(line);
×
129
    if (line.empty()) {
×
130
      continue;
×
131
    }
×
132
    vec.emplace_back(line);
×
133
  }
×
134
}
×
135

136
static void mergeYamlSubFile(const std::string& configname, Recursorsettings& settings, bool allowabsent, Logr::log_t log)
137
{
238✔
138
  auto file = ifstream(configname);
238✔
139
  if (!file.is_open()) {
238✔
140
    if (allowabsent) {
4!
141
      return;
4✔
142
    }
4✔
143
    throw runtime_error("Cannot open " + configname);
×
144
  }
4✔
145
  log->info(Logr::Notice, "Processing YAML settings", "path", Logging::Loggable(configname));
234✔
146
  auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
234✔
147
  pdns::rust::settings::rec::merge(settings, data);
234✔
148
}
234✔
149

150
static void possiblyConvertACLFile(const string& includeDir, const string& apiDir, const std::string& filename, Logr::log_t log)
151
{
6✔
152
  auto path = includeDir;
6✔
153
  path.append("/").append(filename).append(".conf");
6✔
154
  auto file = ifstream(path);
6✔
155
  if (!file.is_open()) {
6✔
156
    // Not an error, file just is not there
157
    return;
4✔
158
  }
4✔
159
  rust::vec<rust::string> result;
2✔
160
  std::string line;
2✔
161
  while (getline(file, line)) {
10✔
162
    auto pos = line.find('#');
8✔
163
    if (pos != string::npos) {
8✔
164
      line.resize(pos);
2✔
165
    }
2✔
166
    boost::trim(line);
8✔
167
    if (line.empty()) {
8✔
168
      continue;
2✔
169
    }
2✔
170
    auto plusis = line.find("+=");
6✔
171
    if (plusis != string::npos) {
6✔
172
      auto val = line.substr(plusis + 2);
2✔
173
      boost::trim(val);
2✔
174
      vector<string> acls;
2✔
175
      stringtok(acls, val, " ,\t");
2✔
176
      for (const auto& acl : acls) {
3✔
177
        result.emplace_back(acl);
3✔
178
      }
3✔
179
    }
2✔
180
  }
6✔
181

182
  rust::string key = "allow_from";
2✔
183
  rust::string filekey = "allow_from_file";
2✔
184
  if (filename == "allow-notify-from") {
2✔
185
    key = "allow_notify_from";
1✔
186
    filekey = "allow_notify_from_file";
1✔
187
  }
1✔
188
  const auto yaml = pdns::rust::settings::rec::allow_from_to_yaml_string_incoming(key, filekey, result);
2✔
189

190
  string yamlfilename = apiDir;
2✔
191
  yamlfilename.append("/").append(filename).append(".yml");
2✔
192
  string tmpfilename = yamlfilename + ".tmp";
2✔
193
  ofstream ofconf(tmpfilename);
2✔
194
  if (!ofconf) {
2!
195
    int err = errno;
×
196
    log->error(Logr::Error, err, "Cannot open for file for writing YAML", "to", Logging::Loggable(tmpfilename));
×
197
    throw runtime_error("YAML Conversion");
×
198
  }
×
199
  ofconf << "# Generated by pdns-recursor REST API, DO NOT EDIT" << endl;
2✔
200
  ofconf << yaml << endl;
2✔
201
  ofconf.close();
2✔
202
  if (ofconf.bad()) {
2!
203
    log->error(Logr::Error, "Error writing YAML", "to", Logging::Loggable(tmpfilename));
×
204
    unlink(tmpfilename.c_str());
×
205
    throw runtime_error("YAML Conversion");
×
206
  }
×
207
  if (rename(path.c_str(), (path + ".converted").c_str()) != 0) {
2!
208
    int err = errno;
×
209
    log->error(Logr::Error, err, "Rename failed", "file", Logging::Loggable(path), "to", Logging::Loggable(path + ".converted"));
×
210
    unlink(tmpfilename.c_str());
×
211
    throw runtime_error("YAML Conversion");
×
212
  }
×
213

214
  if (rename(tmpfilename.c_str(), yamlfilename.c_str()) != 0) {
2!
215
    int err = errno;
×
216
    log->error(Logr::Error, err, "Rename failed", "file", Logging::Loggable(tmpfilename), "to", Logging::Loggable(yamlfilename));
×
217
    if (rename((path + ".converted").c_str(), path.c_str()) != 0) {
×
218
      err = errno;
×
219
      log->error(Logr::Error, err, "Rename failed", "file", Logging::Loggable(path + ".converted"), "to", Logging::Loggable(path));
×
220
    }
×
221
    throw runtime_error("YAML Conversion");
×
222
  }
×
223
  log->info(Logr::Notice, "Converted to YAML", "file", Logging::Loggable(path), "to", Logging::Loggable(yamlfilename));
2✔
224
}
2✔
225

226
static void fileCopy(const string& src, const string& dst, Logr::log_t log)
227
{
×
228
  ifstream ifconf(src);
×
229
  if (!ifconf) {
×
230
    log->info(Logr::Error, "Cannot open for file for reading", "file", Logging::Loggable(src));
×
231
    throw runtime_error("YAML Conversion");
×
232
  }
×
233
  ofstream ofconf(dst);
×
234
  if (!ofconf) {
×
235
    log->info(Logr::Error, "Cannot open for file for writing YAML", "to", Logging::Loggable(dst));
×
236
    throw runtime_error("YAML Conversion");
×
237
  }
×
238
  for (;;) {
×
239
    auto character = ifconf.get();
×
240
    if (ifconf.eof()) {
×
241
      break;
×
242
    }
×
243
    if (ifconf.bad()) {
×
244
      int err = errno;
×
245
      log->error(Logr::Error, err, "Error reading", "to", Logging::Loggable(src));
×
246
      throw runtime_error("YAML Conversion");
×
247
    }
×
248
    ofconf.put(static_cast<char>(character));
×
249
    if (ofconf.bad()) {
×
250
      int err = errno;
×
251
      log->error(Logr::Error, err, "Error writing YAML", "to", Logging::Loggable(dst));
×
252
      throw runtime_error("YAML Conversion");
×
253
    }
×
254
  }
×
255
  ifconf.close();
×
256
  ofconf.close();
×
257
  if (ofconf.bad()) {
×
258
    log->error(Logr::Error, "Error writing YAML", "to", Logging::Loggable(dst));
×
259
    throw runtime_error("YAML Conversion");
×
260
  }
×
261
}
×
262

263
static void possiblyConvertForwardsandAuths(const std::string& includeDir, const std::string& apiDir, Logr::log_t log)
264
{
3✔
265
  std::vector<std::string> forwAndAuthFiles{};
3✔
266
  ::arg().gatherIncludes(includeDir, "..conf", forwAndAuthFiles);
3✔
267
  pdns::rust::settings::rec::Recursorsettings settings{};
3✔
268
  for (const auto& file : forwAndAuthFiles) {
1,359✔
269
    auto yaml = pdns::settings::rec::oldStyleSettingsFileToYaml(file, false);
1,359✔
270
    pdns::rust::settings::rec::merge(settings, yaml);
1,359✔
271
  }
1,359✔
272
  const string yamlAPiZonesFile = apiDir + "/apizones";
3✔
273

274
  for (auto& zone : settings.recursor.auth_zones) {
3!
275
    const std::string origName(zone.file);
×
276
    std::string newName(zone.file);
×
277
    newName.replace(0, includeDir.length(), apiDir);
×
278
    log->info(Logr::Notice, "Copying auth zone file", "file", Logging::Loggable(origName), "to", Logging::Loggable(newName));
×
279
    fileCopy(origName, newName, log);
×
280
    zone.file = ::rust::String(newName);
×
281
    api_add_auth_zone(yamlAPiZonesFile, zone);
×
282
  }
×
283
  api_add_forward_zones(yamlAPiZonesFile, settings.recursor.forward_zones);
3✔
284
  api_add_forward_zones(yamlAPiZonesFile, settings.recursor.forward_zones_recurse);
3✔
285
  for (const auto& file : forwAndAuthFiles) {
1,359✔
286
    if (rename(file.c_str(), (file + ".converted").c_str()) != 0) {
1,359!
287
      int err = errno;
×
288
      log->error(Logr::Error, err, "Rename failed", "file", Logging::Loggable(file), "to", Logging::Loggable(file + ".converted"));
×
289
    }
×
290
  }
1,359✔
291
}
3✔
292

293
void pdns::settings::rec::processAPIDir(const string& includeDirOnCommandLine, pdns::rust::settings::rec::Recursorsettings& settings, Logr::log_t log)
294
{
37✔
295
  auto apiDir = std::string(settings.webservice.api_dir);
37✔
296
  if (apiDir.empty()) {
37✔
297
    return;
34✔
298
  }
34✔
299
  auto includeDir = std::string(settings.recursor.include_dir);
3✔
300
  if (!includeDirOnCommandLine.empty()) {
3✔
301
    includeDir = includeDirOnCommandLine;
1✔
302
  }
1✔
303
  if (includeDir == apiDir) {
3!
304
    throw runtime_error("Active YAML settings do not allow include_dir to be equal to api_dir");
×
305
  }
×
306
  const std::array<std::string, 2> aclFiles = {
3✔
307
    "allow-from",
3✔
308
    "allow-notify-from"};
3✔
309
  for (const auto& file : aclFiles) {
6✔
310
    possiblyConvertACLFile(includeDir, apiDir, file, log);
6✔
311
    auto path = apiDir;
6✔
312
    path.append("/").append(file).append(".yml");
6✔
313
    mergeYamlSubFile(path, settings, true, log);
6✔
314
  }
6✔
315
  possiblyConvertForwardsandAuths(includeDir, apiDir, log);
3✔
316
}
3✔
317

318
template <typename T>
319
static void addToAllowNotifyFor(Recursorsettings& settings, const rust::Vec<T>& vec)
320
{
354✔
321
  for (const auto& item : vec) {
354✔
322
    if (item.notify_allowed) {
53!
323
      settings.incoming.allow_notify_for.emplace_back(item.zone);
13✔
324
    }
13✔
325
  }
53✔
326
}
354✔
327

328
pdns::settings::rec::YamlSettingsStatus pdns::settings::rec::readYamlSettings(const std::string& configname, const std::string& includeDirOnCommandLine, Recursorsettings& settings, std::string& msg, Logr::log_t log)
329
{
1,746✔
330
  auto file = ifstream(configname);
1,746✔
331
  if (!file.is_open()) {
1,746✔
332
    msg = stringerror(errno);
906✔
333
    return YamlSettingsStatus::CannotOpen;
906✔
334
  }
906✔
335
  log->info(Logr::Notice, "Processing main YAML settings", "path", Logging::Loggable(configname));
840✔
336
  try {
840✔
337
    auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
840✔
338
    auto yamlstruct = pdns::rust::settings::rec::parse_yaml_string(data);
840✔
339
    std::vector<std::string> yamlFiles;
840✔
340
    ::arg().gatherIncludes(!includeDirOnCommandLine.empty() ? includeDirOnCommandLine : string(yamlstruct.recursor.include_dir),
840✔
341
                           ".yml", yamlFiles);
840✔
342
    for (const auto& yamlfile : yamlFiles) {
840✔
343
      mergeYamlSubFile(yamlfile, yamlstruct, false, log);
232✔
344
    }
232✔
345
    // Add the zones with notify_allowed to allow_notify_for. For a forward_zones_file that will be
346
    // taken care of elsewhere.  One drawback: the zones will be shown in allow_notify_for if you
347
    // run --config, while they aren't actually there in any config file.
348
    addToAllowNotifyFor(yamlstruct, yamlstruct.recursor.forward_zones);
840✔
349
    addToAllowNotifyFor(yamlstruct, yamlstruct.recursor.forward_zones_recurse);
840✔
350
    addToAllowNotifyFor(yamlstruct, yamlstruct.recursor.forwarding_catalog_zones);
840✔
351
    yamlstruct.validate();
840✔
352
    settings = std::move(yamlstruct);
840✔
353
    return YamlSettingsStatus::OK;
840✔
354
  }
840✔
355
  catch (const ::rust::Error& ex) {
840✔
356
    msg = ex.what();
722✔
357
    return YamlSettingsStatus::PresentButFailed;
722✔
358
  }
722✔
359
  catch (const std::exception& ex) {
840✔
360
    msg = ex.what();
×
361
    return YamlSettingsStatus::PresentButFailed;
×
362
  }
×
363
  catch (...) {
840✔
364
    msg = "Unexpected exception processing YAML";
×
365
    return YamlSettingsStatus::PresentButFailed;
×
366
  }
×
367
}
840✔
368

369
void pdns::settings::rec::readYamlAllowFromFile(const std::string& filename, ::rust::Vec<::rust::String>& vec, Logr::log_t log)
370
{
×
371
  log->info(Logr::Notice, "Processing allow YAML settings", "path", Logging::Loggable(filename));
×
372
  auto file = ifstream(filename);
×
373
  if (!file.is_open()) {
×
374
    throw runtime_error(stringerror(errno));
×
375
  }
×
376
  auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
×
377
  auto yamlvec = pdns::rust::settings::rec::parse_yaml_string_to_allow_from(data);
×
378
  pdns::rust::settings::rec::validate_allow_from(filename, yamlvec);
×
379
  vec = std::move(yamlvec);
×
380
}
×
381

382
void pdns::settings::rec::readYamlForwardZonesFile(const std::string& filename, ::rust::Vec<pdns::rust::settings::rec::ForwardZone>& vec, Logr::log_t log)
383
{
×
384
  log->info(Logr::Notice, "Processing forwarding YAML settings", "path", Logging::Loggable(filename));
×
385
  auto file = ifstream(filename);
×
386
  if (!file.is_open()) {
×
387
    throw runtime_error(stringerror(errno));
×
388
  }
×
389
  auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
×
390
  auto yamlvec = pdns::rust::settings::rec::parse_yaml_string_to_forward_zones(data);
×
391
  pdns::rust::settings::rec::validate_forward_zones("forward_zones", yamlvec);
×
392
  vec = std::move(yamlvec);
×
393
}
×
394

395
void pdns::settings::rec::readYamlAllowNotifyForFile(const std::string& filename, ::rust::Vec<::rust::String>& vec, Logr::log_t log)
396
{
×
397
  log->info(Logr::Notice, "Processing allow-notify-for YAML settings", "path", Logging::Loggable(filename));
×
398
  auto file = ifstream(filename);
×
399
  if (!file.is_open()) {
×
400
    throw runtime_error(stringerror(errno));
×
401
  }
×
402
  auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
×
403
  auto yamlvec = pdns::rust::settings::rec::parse_yaml_string_to_allow_notify_for(data);
×
404
  pdns::rust::settings::rec::validate_allow_notify_for("allow-notify-for", yamlvec);
×
405
  vec = std::move(yamlvec);
×
406
}
×
407

408
std::string pdns::settings::rec::to_arg(const AuthZone& authzone)
409
{
34✔
410
  std::ostringstream str;
34✔
411
  str << to_arg(authzone.zone) << '=' << to_arg(authzone.file);
34✔
412
  return str.str();
34✔
413
}
34✔
414

415
std::string pdns::settings::rec::to_arg(const ForwardZone& forwardzone)
416
{
35✔
417
  std::ostringstream str;
35✔
418
  str << to_arg(forwardzone.zone) << '=';
35✔
419
  const auto& vec = forwardzone.forwarders;
35✔
420
  for (auto iter = vec.begin(); iter != vec.end(); ++iter) {
101✔
421
    if (iter != vec.begin()) {
66✔
422
      str << ';';
31✔
423
    }
31✔
424
    str << to_arg(*iter);
66✔
425
  }
66✔
426
  return str.str();
35✔
427
}
35✔
428

429
void pdns::settings::rec::setArgsForZoneRelatedSettings(Recursorsettings& settings)
430
{
10✔
431
  ::arg().set("forward-zones") = to_arg(settings.recursor.forward_zones);
10✔
432
  ::arg().set("forward-zones-file") = to_arg(settings.recursor.forward_zones_file);
10✔
433
  ::arg().set("forward-zones-recurse") = to_arg(settings.recursor.forward_zones_recurse);
10✔
434
  ::arg().set("auth-zones") = to_arg(settings.recursor.auth_zones);
10✔
435
  ::arg().set("allow-notify-for") = to_arg(settings.incoming.allow_notify_for);
10✔
436
  ::arg().set("allow-notify-for-file") = to_arg(settings.incoming.allow_notify_for_file);
10✔
437
  ::arg().set("export-etc-hosts") = to_arg(settings.recursor.export_etc_hosts);
10✔
438
  ::arg().set("serve-rfc1918") = to_arg(settings.recursor.serve_rfc1918);
10✔
439
}
10✔
440

441
void pdns::settings::rec::setArgsForACLRelatedSettings(Recursorsettings& settings)
442
{
×
443
  ::arg().set("allow-from") = to_arg(settings.incoming.allow_from);
×
444
  ::arg().set("allow-from-file") = to_arg(settings.incoming.allow_from_file);
×
445
  ::arg().set("allow-notify-from") = to_arg(settings.incoming.allow_notify_from);
×
446
  ::arg().set("allow-notify-from-file") = to_arg(settings.incoming.allow_notify_from_file);
×
447
  ::arg().set("proxy-protocol-from") = to_arg(settings.incoming.proxy_protocol_from);
×
448
  ::arg().set("proxy-protocol-exceptions") = to_arg(settings.incoming.proxy_protocol_exceptions);
×
449
}
×
450

451
void pdns::settings::rec::to_yaml(uint64_t& field, const std::string& val)
452
{
178✔
453
  if (val.empty()) {
178!
454
    field = 0;
×
455
    return;
×
456
  }
×
457

458
  checked_stoi_into(field, val, nullptr, 0);
178✔
459
}
178✔
460

461
void pdns::settings::rec::to_yaml(double& field, const std::string& val)
462
{
2✔
463
  if (val.empty()) {
2!
464
    field = 0.0;
×
465
    return;
×
466
  }
×
467

468
  const auto* cptr_orig = val.c_str();
2✔
469
  char* cptr_ret = nullptr;
2✔
470
  auto retval = strtod(cptr_orig, &cptr_ret);
2✔
471

472
  field = retval;
2✔
473
}
2✔
474

475
void pdns::settings::rec::to_yaml(::rust::Vec<AuthZone>& field, const std::string& val)
476
{
2✔
477
  vector<string> zones;
2✔
478
  stringtok(zones, val, " ,\t\n\r");
2✔
479
  for (const auto& zone : zones) {
2!
480
    auto headers = splitField(zone, '=');
×
481
    boost::trim(headers.first);
×
482
    boost::trim(headers.second);
×
483
    AuthZone authzone{headers.first, headers.second};
×
484
    field.push_back(std::move(authzone));
×
485
  }
×
486
}
2✔
487

488
void pdns::settings::rec::to_yaml(::rust::Vec<ForwardZone>& field, const std::string& val, bool recurse)
489
{
1,363✔
490
  vector<string> zones;
1,363✔
491
  stringtok(zones, val, " ,\t\n\r");
1,363✔
492
  for (const auto& zone : zones) {
1,363✔
493
    auto headers = splitField(zone, '=');
1,359✔
494
    boost::trim(headers.first);
1,359✔
495
    boost::trim(headers.second);
1,359✔
496
    ::rust::Vec<::rust::String> addresses;
1,359✔
497
    stringtok(addresses, headers.second, " ;");
1,359✔
498
    ForwardZone forwardzone{headers.first, std::move(addresses), recurse, false};
1,359✔
499
    field.push_back(std::move(forwardzone));
1,359✔
500
  }
1,359✔
501
}
1,363✔
502

503
using FieldMap = std::map<pair<::rust::String, ::rust::String>, pdns::rust::settings::rec::OldStyle>;
504

505
static bool simpleRustType(const ::rust::String& rname)
506
{
×
507
  return rname == "bool" || rname == "u64" || rname == "f64" || rname == "String";
×
508
}
×
509

510
static void processLine(const std::string& arg, FieldMap& map, bool mainFile)
511
{
2,718✔
512
  string var;
2,718✔
513
  string val;
2,718✔
514
  string::size_type pos = 0;
2,718✔
515
  bool incremental = false;
2,718✔
516

517
  if (arg.find("--") == 0 && (pos = arg.find("+=")) != string::npos) // this is a --port+=25 case
2,718!
518
  {
1,359✔
519
    var = arg.substr(2, pos - 2);
1,359✔
520
    val = arg.substr(pos + 2);
1,359✔
521
    incremental = true;
1,359✔
522
  }
1,359✔
523
  else if (arg.find("--") == 0 && (pos = arg.find('=')) != string::npos) // this is a --port=25 case
1,359!
524
  {
×
525
    var = arg.substr(2, pos - 2);
×
526
    val = arg.substr(pos + 1);
×
527
  }
×
528
  else if (arg.find("--") == 0 && (arg.find('=') == string::npos)) // this is a --daemon case
1,359!
529
  {
1,359✔
530
    var = arg.substr(2);
1,359✔
531
    val = "";
1,359✔
532
  }
1,359✔
533
  else if (arg[0] == '-' && arg.length() > 1) {
×
534
    var = arg.substr(1);
×
535
    val = "";
×
536
  }
×
537
  boost::trim(var);
2,718✔
538
  if (var.empty()) {
2,718✔
539
    return;
1,359✔
540
  }
1,359✔
541
  pos = val.find_first_not_of(" \t"); // strip leading whitespace
1,359✔
542
  if (pos != 0 && pos != string::npos) {
1,359!
543
    val = val.substr(pos);
×
544
  }
×
545

546
  ::rust::String section;
1,359✔
547
  ::rust::String fieldname;
1,359✔
548
  ::rust::String type_name;
1,359✔
549
  pdns::rust::settings::rec::Value rustvalue = {false, 0, 0.0, "", {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}};
1,359✔
550
  if (pdns::settings::rec::oldKVToBridgeStruct(var, val, section, fieldname, type_name, rustvalue)) {
1,359!
551
    auto overriding = !mainFile && !incremental && !simpleRustType(type_name);
1,359!
552
    auto [existing, inserted] = map.emplace(std::pair{std::pair{section, fieldname}, pdns::rust::settings::rec::OldStyle{section, fieldname, var, std::move(type_name), rustvalue, overriding}});
1,359✔
553
    if (!inserted) {
1,359!
554
      // Simple values overwrite always
555
      existing->second.value.bool_val = rustvalue.bool_val;
×
556
      existing->second.value.u64_val = rustvalue.u64_val;
×
557
      existing->second.value.f64_val = rustvalue.f64_val;
×
558
      existing->second.value.string_val = rustvalue.string_val;
×
559
      // List values only if = was used
560
      if (!incremental) {
×
561
        existing->second.value.vec_string_val.clear();
×
562
        existing->second.value.vec_forwardzone_val.clear();
×
563
        existing->second.value.vec_authzone_val.clear();
×
564
      }
×
565
      for (const auto& add : rustvalue.vec_string_val) {
×
566
        existing->second.value.vec_string_val.emplace_back(add);
×
567
      }
×
568
      for (const auto& add : rustvalue.vec_forwardzone_val) {
×
569
        existing->second.value.vec_forwardzone_val.emplace_back(add);
×
570
      }
×
571
      for (const auto& add : rustvalue.vec_authzone_val) {
×
572
        existing->second.value.vec_authzone_val.emplace_back(add);
×
573
      }
×
574
    }
×
575
  }
1,359✔
576
}
1,359✔
577

578
std::string pdns::settings::rec::oldStyleSettingsFileToYaml(const string& fname, bool mainFile)
579
{
1,359✔
580
  string line;
1,359✔
581
  string pline;
1,359✔
582

583
  std::ifstream configFileStream(fname);
1,359✔
584
  if (!configFileStream) {
1,359!
585
    int err = errno;
×
586
    throw runtime_error("Cannot read " + fname + ": " + stringerror(err));
×
587
  }
×
588

589
  // Read the old-style config file and produce a map with the data, taking into account the
590
  // difference between = and +=
591
  FieldMap map;
1,359✔
592
  while (getline(configFileStream, pline)) {
4,077✔
593
    boost::trim_right(pline);
2,718✔
594

595
    if (!pline.empty() && pline[pline.size() - 1] == '\\') {
2,718!
596
      line += pline.substr(0, pline.length() - 1);
×
597
      continue;
×
598
    }
×
599

600
    line += pline;
2,718✔
601

602
    // strip everything after a #
603
    string::size_type pos = line.find('#');
2,718✔
604
    if (pos != string::npos) {
2,718✔
605
      // make sure it's either first char or has whitespace before
606
      // fixes issue #354
607
      if (pos == 0 || (std::isspace(line[pos - 1]) != 0)) {
1,359!
608
        line = line.substr(0, pos);
1,359✔
609
      }
1,359✔
610
    }
1,359✔
611

612
    // strip trailing spaces
613
    boost::trim_right(line);
2,718✔
614

615
    // strip leading spaces
616
    pos = line.find_first_not_of(" \t\r\n");
2,718✔
617
    if (pos != string::npos) {
2,718✔
618
      line = line.substr(pos);
1,359✔
619
    }
1,359✔
620

621
    processLine("--" + line, map, mainFile);
2,718✔
622
    line = "";
2,718✔
623
  }
2,718✔
624

625
  // Convert the map to a vector, as CXX does not have any dictionary like support.
626
  ::rust::Vec<pdns::rust::settings::rec::OldStyle> vec;
1,359✔
627
  vec.reserve(map.size());
1,359✔
628
  for (const auto& entry : map) {
1,359✔
629
    vec.emplace_back(entry.second);
1,359✔
630
  }
1,359✔
631
  return std::string(pdns::rust::settings::rec::map_to_yaml_string(vec));
1,359✔
632
}
1,359✔
633

634
std::string pdns::settings::rec::defaultsToYaml()
635
{
2✔
636
  // In this function we make use of the fact that we know a little about the formatting of YAML by
637
  // serde_yaml.  ATM there's no way around that, as serde_yaml itself does not have any support for
638
  // comments (other than stripping them while parsing). So produced YAML cannot have any comments.
639

640
  // First we construct a map of (section, name) to info about the field (oldname, type, value etc)
641
  FieldMap map;
2✔
642
  for (const auto& var : arg().list()) {
500✔
643
    if (const auto newname = ArgvMap::isDeprecated(var); !newname.empty()) {
500✔
644
      continue;
14✔
645
    }
14✔
646
    ::rust::String section;
486✔
647
    ::rust::String fieldname;
486✔
648
    ::rust::String type_name;
486✔
649
    pdns::rust::settings::rec::Value rustvalue{};
486✔
650
    string name = var;
486✔
651
    string val = arg().getDefault(var);
486✔
652
    if (pdns::settings::rec::oldKVToBridgeStruct(name, val, section, fieldname, type_name, rustvalue)) {
486✔
653
      map.emplace(std::pair{std::pair{section, fieldname}, pdns::rust::settings::rec::OldStyle{section, fieldname, name, std::move(type_name), std::move(rustvalue), false}});
428✔
654
    }
428✔
655
  }
486✔
656

657
  // Should be generated
658
  auto def = [&](const string& section, const string& name, const string& type) {
28✔
659
    pdns::rust::settings::rec::Value rustvalue{};
28✔
660
    // Dirty hack: trustanchorfile_interval is the only u64 value, set the right default for it.
661
    // And for all other values below, the default is either an empty string or an empty vector.
662
    // Once we get more u64 values below with different default values this hack no longer works.
663
    rustvalue.u64_val = 24;
28✔
664
    map.emplace(std::pair{std::pair{section, name}, pdns::rust::settings::rec::OldStyle{section, name, name, type, std::move(rustvalue), false}});
28✔
665
  };
28✔
666
  def("dnssec", "trustanchors", "Vec<TrustAnchor>");
2✔
667
  def("dnssec", "negative_trustanchors", "Vec<NegativeTrustAnchor>");
2✔
668
  def("dnssec", "trustanchorfile", "String");
2✔
669
  def("dnssec", "trustanchorfile_interval", "u64");
2✔
670
  def("logging", "protobuf_servers", "Vec<ProtobufServer>");
2✔
671
  def("logging", "outgoing_protobuf_servers", "Vec<ProtobufServer>");
2✔
672
  def("logging", "dnstap_framestream_servers", "Vec<DNSTapFrameStreamServer>");
2✔
673
  def("logging", "dnstap_nod_framestream_servers", "Vec<DNSTapNODFrameStreamServer>");
2✔
674
  def("recursor", "rpzs", "Vec<RPZ>");
2✔
675
  def("recursor", "sortlists", "Vec<SortList>");
2✔
676
  def("recordcache", "zonetocaches", "Vec<ZoneToCache>");
2✔
677
  def("recursor", "allowed_additional_qtypes", "Vec<AllowedAdditionalQType>");
2✔
678
  def("incoming", "proxymappings", "Vec<ProxyMapping>");
2✔
679
  def("recursor", "forwarding_catalog_zones", "Vec<ForwardingCatalogZone>");
2✔
680
  // End of should be generated XXX
681

682
  // Convert the map to a vector, as CXX does not have any dictionary like support.
683
  ::rust::Vec<pdns::rust::settings::rec::OldStyle> vec;
2✔
684
  vec.reserve(map.size());
2✔
685
  for (const auto& entry : map) {
456✔
686
    vec.emplace_back(entry.second);
456✔
687
  }
456✔
688
  const auto defs = std::string(pdns::rust::settings::rec::map_to_yaml_string(vec));
2✔
689

690
  // We now have a YAML string, with all sections and all default values. Do a litle bit of parsing
691
  // to insert the help text lines.
692
  std::vector<std::string> lines;
2✔
693
  stringtok(lines, defs, "\n");
2✔
694
  std::string res;
2✔
695

696
  // These two RE's know about the formatting generated by serde_yaml
697
  std::regex sectionRE("^(\\w+):");
2✔
698
  std::regex fieldRE("^  (\\w+):");
2✔
699
  std::string section;
2✔
700
  std::string field;
2✔
701

702
  for (const auto& line : lines) {
1,906✔
703
    bool withHelp = false;
1,906✔
704
    bool sectionChange = false;
1,906✔
705
    std::smatch matches;
1,906✔
706
    std::regex_search(line, matches, sectionRE);
1,906✔
707
    if (!matches.empty()) {
1,906✔
708
      section = matches[1];
24✔
709
      sectionChange = true;
24✔
710
    }
24✔
711
    std::regex_search(line, matches, fieldRE);
1,906✔
712
    if (!matches.empty()) {
1,906✔
713
      field = matches[1];
456✔
714
      withHelp = true;
456✔
715
    }
456✔
716
    if (withHelp) {
1,906✔
717
      std::string oldname;
456✔
718
      if (auto iter = map.find(make_pair(section, field)); iter != map.end()) {
456!
719
        oldname = std::string(iter->second.old_name);
456✔
720
      }
456✔
721
      res += "##### ";
456✔
722
      auto help = arg().getHelp(oldname);
456✔
723
      if (help.empty()) {
456✔
724
        replace(oldname.begin(), oldname.end(), '_', '-');
16✔
725
        help = arg().getHelp(oldname);
16✔
726
      }
16✔
727
      res += help;
456✔
728
      res += '\n';
456✔
729
    }
456✔
730
    if (sectionChange) {
1,906✔
731
      res += "\n######### SECTION ";
24✔
732
      res += section;
24✔
733
      res += " #########\n";
24✔
734
      res += line;
24✔
735
    }
24✔
736
    else {
1,882✔
737
      res += "# ";
1,882✔
738
      res += line;
1,882✔
739
    }
1,882✔
740
    res += '\n';
1,906✔
741
  }
1,906✔
742
  return res;
2✔
743
}
2✔
744

745
namespace
746
{
747
void fromLuaToRust(const LuaConfigItems& luaConfig, pdns::rust::settings::rec::Dnssec& dnssec)
748
{
2✔
749
  dnssec.trustanchorfile = luaConfig.trustAnchorFileInfo.fname;
2✔
750
  dnssec.trustanchorfile_interval = luaConfig.trustAnchorFileInfo.interval;
2✔
751
  dnssec.trustanchors.clear();
2✔
752
  for (const auto& anchors : luaConfig.dsAnchors) {
4✔
753
    ::rust::Vec<::rust::String> dsRecords;
4✔
754
    for (const auto& dsRecord : anchors.second) {
6✔
755
      const auto dsString = dsRecord.getZoneRepresentation();
6✔
756
      if (anchors.first != g_rootdnsname || std::find(rootDSs.begin(), rootDSs.end(), dsString) == rootDSs.end()) {
6!
757
        dsRecords.emplace_back(dsRecord.getZoneRepresentation());
6✔
758
      }
6✔
759
    }
6✔
760
    if (!dsRecords.empty()) {
4!
761
      pdns::rust::settings::rec::TrustAnchor trustAnchor{anchors.first.toString(), std::move(dsRecords)};
4✔
762
      dnssec.trustanchors.emplace_back(trustAnchor);
4✔
763
    }
4✔
764
  }
4✔
765
  for (const auto& anchors : luaConfig.negAnchors) {
4✔
766
    pdns::rust::settings::rec::NegativeTrustAnchor negtrustAnchor{anchors.first.toString(), anchors.second};
4✔
767
    dnssec.negative_trustanchors.emplace_back(negtrustAnchor);
4✔
768
  }
4✔
769
}
2✔
770

771
void fromLuaToRust(const ProtobufExportConfig& pbConfig, pdns::rust::settings::rec::ProtobufServer& pbServer)
772
{
4✔
773
  for (const auto& server : pbConfig.servers) {
4✔
774
    pbServer.servers.emplace_back(server.toStringWithPort());
4✔
775
  }
4✔
776
  pbServer.timeout = pbConfig.timeout;
4✔
777
  pbServer.maxQueuedEntries = pbConfig.maxQueuedEntries;
4✔
778
  pbServer.reconnectWaitTime = pbConfig.reconnectWaitTime;
4✔
779
  pbServer.taggedOnly = pbConfig.taggedOnly;
4✔
780
  pbServer.asyncConnect = pbConfig.asyncConnect;
4✔
781
  pbServer.logQueries = pbConfig.logQueries;
4✔
782
  pbServer.logResponses = pbConfig.logResponses;
4✔
783
  for (const auto num : pbConfig.exportTypes) {
8✔
784
    pbServer.exportTypes.emplace_back(QType(num).toString());
8✔
785
  }
8✔
786
  pbServer.logMappedFrom = pbConfig.logMappedFrom;
4✔
787
}
4✔
788

789
void fromLuaToRust(const FrameStreamExportConfig& fsc, pdns::rust::settings::rec::DNSTapFrameStreamServer& dnstap)
790
{
2✔
791
  for (const auto& server : fsc.servers) {
2✔
792
    dnstap.servers.emplace_back(server);
2✔
793
  }
2✔
794
  dnstap.logQueries = fsc.logQueries;
2✔
795
  dnstap.logResponses = fsc.logResponses;
2✔
796
  dnstap.bufferHint = fsc.bufferHint;
2✔
797
  dnstap.flushTimeout = fsc.flushTimeout;
2✔
798
  dnstap.inputQueueSize = fsc.inputQueueSize;
2✔
799
  dnstap.outputQueueSize = fsc.outputQueueSize;
2✔
800
  dnstap.queueNotifyThreshold = fsc.queueNotifyThreshold;
2✔
801
  dnstap.reopenInterval = fsc.reopenInterval;
2✔
802
}
2✔
803

804
void fromLuaToRust(const FrameStreamExportConfig& fsc, pdns::rust::settings::rec::DNSTapNODFrameStreamServer& dnstap)
805
{
2✔
806
  for (const auto& server : fsc.servers) {
2✔
807
    dnstap.servers.emplace_back(server);
2✔
808
  }
2✔
809
  dnstap.logNODs = fsc.logNODs;
2✔
810
  dnstap.logUDRs = fsc.logUDRs;
2✔
811
  dnstap.bufferHint = fsc.bufferHint;
2✔
812
  dnstap.flushTimeout = fsc.flushTimeout;
2✔
813
  dnstap.inputQueueSize = fsc.inputQueueSize;
2✔
814
  dnstap.outputQueueSize = fsc.outputQueueSize;
2✔
815
  dnstap.queueNotifyThreshold = fsc.queueNotifyThreshold;
2✔
816
  dnstap.reopenInterval = fsc.reopenInterval;
2✔
817
}
2✔
818

819
void assign(pdns::rust::settings::rec::TSIGTriplet& var, const TSIGTriplet& tsig)
820
{
6✔
821
  var.name = tsig.name.empty() ? "" : tsig.name.toStringNoDot();
6!
822
  var.algo = tsig.algo.empty() ? "" : tsig.algo.toStringNoDot();
6!
823
  var.secret = Base64Encode(tsig.secret);
6✔
824
}
6✔
825

826
void assign(TSIGTriplet& var, const pdns::rust::settings::rec::TSIGTriplet& tsig)
827
{
7✔
828
  if (!tsig.name.empty()) {
7!
829
    var.name = DNSName(std::string(tsig.name));
×
830
  }
×
831
  if (!tsig.algo.empty()) {
7!
832
    var.algo = DNSName(std::string(tsig.algo));
×
833
  }
×
834
  B64Decode(std::string(tsig.secret), var.secret);
7✔
835
}
7✔
836

837
std::string cvt(DNSFilterEngine::PolicyKind kind)
838
{
2✔
839
  switch (kind) {
2!
840
  case DNSFilterEngine::PolicyKind::NoAction:
×
841
    return "NoAction";
×
842
  case DNSFilterEngine::PolicyKind::Drop:
×
843
    return "Drop";
×
844
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
×
845
    return "NXDOMAIN";
×
846
  case DNSFilterEngine::PolicyKind::NODATA:
×
847
    return "NODATA";
×
848
  case DNSFilterEngine::PolicyKind::Truncate:
×
849
    return "Truncate";
×
850
  case DNSFilterEngine::PolicyKind::Custom:
2!
851
    return "Custom";
2✔
852
  }
2✔
853
  return "UnknownPolicyKind";
×
854
}
2✔
855

856
void fromLuaToRust(const vector<RPZTrackerParams>& rpzs, pdns::rust::settings::rec::Recursor& rec)
857
{
2✔
858
  for (const auto& rpz : rpzs) {
6✔
859
    pdns::rust::settings::rec::RPZ rustrpz{
6✔
860
      .name = "",
6✔
861
      .addresses = {},
6✔
862
      .defcontent = "",
6✔
863
      .defpol = "",
6✔
864
      .defpolOverrideLocalData = true,
6✔
865
      .defttl = std::numeric_limits<uint32_t>::max(),
6✔
866
      .extendedErrorCode = std::numeric_limits<uint32_t>::max(),
6✔
867
      .extendedErrorExtra = "",
6✔
868
      .includeSOA = false,
6✔
869
      .ignoreDuplicates = false,
6✔
870
      .maxTTL = std::numeric_limits<uint32_t>::max(),
6✔
871
      .policyName = "",
6✔
872
      .tags = {},
6✔
873
      .overridesGettag = true,
6✔
874
      .zoneSizeHint = 0,
6✔
875
      .tsig = {},
6✔
876
      .refresh = 0,
6✔
877
      .maxReceivedMBytes = 0,
6✔
878
      .localAddress = "",
6✔
879
      .axfrTimeout = 20,
6✔
880
      .dumpFile = "",
6✔
881
      .seedFile = "",
6✔
882
    };
6✔
883

884
    for (const auto& address : rpz.zoneXFRParams.primaries) {
6✔
885
      rustrpz.addresses.emplace_back(address);
4✔
886
    }
4✔
887
    rustrpz.name = rpz.zoneXFRParams.name;
6✔
888
    rustrpz.defcontent = rpz.defcontent;
6✔
889
    if (rpz.defpol) {
6✔
890
      rustrpz.defpol = cvt(rpz.defpol->d_kind);
2✔
891
      rustrpz.defttl = rpz.defpol->d_ttl;
2✔
892
    }
2✔
893
    rustrpz.defpolOverrideLocalData = rpz.defpolOverrideLocal;
6✔
894
    rustrpz.extendedErrorCode = rpz.extendedErrorCode;
6✔
895
    rustrpz.extendedErrorExtra = rpz.extendedErrorExtra;
6✔
896
    rustrpz.includeSOA = rpz.includeSOA;
6✔
897
    rustrpz.ignoreDuplicates = rpz.ignoreDuplicates;
6✔
898
    rustrpz.maxTTL = rpz.maxTTL;
6✔
899
    rustrpz.policyName = rpz.polName;
6✔
900
    for (const auto& tag : rpz.tags) {
6✔
901
      rustrpz.tags.emplace_back(tag);
4✔
902
    }
4✔
903
    rustrpz.overridesGettag = rpz.defpolOverrideLocal;
6✔
904
    rustrpz.zoneSizeHint = rpz.zoneXFRParams.zoneSizeHint;
6✔
905
    assign(rustrpz.tsig, rpz.zoneXFRParams.tsigtriplet);
6✔
906
    rustrpz.refresh = rpz.zoneXFRParams.refreshFromConf;
6✔
907
    rustrpz.maxReceivedMBytes = rpz.zoneXFRParams.maxReceivedMBytes;
6✔
908
    if (rpz.zoneXFRParams.localAddress != ComboAddress()) {
6!
909
      rustrpz.localAddress = rpz.zoneXFRParams.localAddress.toString();
×
910
    }
×
911
    rustrpz.axfrTimeout = rpz.zoneXFRParams.xfrTimeout;
6✔
912
    rustrpz.dumpFile = rpz.dumpZoneFileName;
6✔
913
    rustrpz.seedFile = rpz.seedFileName;
6✔
914

915
    rec.rpzs.emplace_back(rustrpz);
6✔
916
  }
6✔
917
}
2✔
918

919
string cvt(pdns::ZoneMD::Config cfg)
920
{
8✔
921
  switch (cfg) {
8!
922
  case pdns::ZoneMD::Config::Ignore:
2✔
923
    return "ignore";
2✔
924
  case pdns::ZoneMD::Config::Validate:
4✔
925
    return "validate";
4✔
926
  case pdns::ZoneMD::Config::Require:
2✔
927
    return "require";
2✔
928
  }
8✔
929
  return "UnknownZoneMDConfig";
×
930
}
8✔
931

932
void fromLuaToRust(const map<DNSName, RecZoneToCache::Config>& ztcConfigs, pdns::rust::settings::rec::Recordcache& recordcache)
933
{
2✔
934
  for (const auto& [_, iter] : ztcConfigs) {
4✔
935
    pdns::rust::settings::rec::ZoneToCache ztc;
4✔
936
    ztc.zone = iter.d_zone;
4✔
937
    ztc.method = iter.d_method;
4✔
938
    for (const auto& src : iter.d_sources) {
4✔
939
      ztc.sources.emplace_back(src);
4✔
940
    }
4✔
941
    ztc.timeout = iter.d_timeout;
4✔
942
    if (!iter.d_tt.name.empty()) {
4✔
943
      ztc.tsig.name = iter.d_tt.name.toString();
2✔
944
      ztc.tsig.algo = iter.d_tt.algo.toString();
2✔
945
      ztc.tsig.secret = Base64Encode(iter.d_tt.secret);
2✔
946
    }
2✔
947
    ztc.refreshPeriod = iter.d_refreshPeriod;
4✔
948
    ztc.retryOnErrorPeriod = iter.d_retryOnError;
4✔
949
    ztc.maxReceivedMBytes = iter.d_maxReceivedBytes;
4✔
950
    if (iter.d_local != ComboAddress()) {
4✔
951
      ztc.localAddress = iter.d_local.toString();
2✔
952
    }
2✔
953
    ztc.zonemd = cvt(iter.d_zonemd);
4✔
954
    ztc.dnssec = cvt(iter.d_dnssec);
4✔
955
    recordcache.zonetocaches.emplace_back(ztc);
4✔
956
  }
4✔
957
}
2✔
958

959
std::string cvt(AdditionalMode mode)
960
{
4✔
961
  switch (mode) {
4!
962
  case AdditionalMode::Ignore:
×
963
    return "Ignore";
×
964
  case AdditionalMode::CacheOnly:
2✔
965
    return "CacheOnly";
2✔
966
  case AdditionalMode::CacheOnlyRequireAuth:
2✔
967
    return "CacheOnlyRequireAuth";
2✔
968
  case AdditionalMode::ResolveImmediately:
×
969
    return "ResolveImmediately";
×
970
  case AdditionalMode::ResolveDeferred:
×
971
    return "ResolveDeferred";
×
972
  }
4✔
973
  return "UnknownAdditionalMode";
×
974
}
4✔
975

976
AdditionalMode cvtAdditional(const std::string& mode)
977
{
4✔
978
  static const std::map<std::string, AdditionalMode> map = {
4✔
979
    {"Ignore", AdditionalMode::Ignore},
4✔
980
    {"CacheOnly", AdditionalMode::CacheOnly},
4✔
981
    {"CacheOnlyRequireAuth", AdditionalMode::CacheOnlyRequireAuth},
4✔
982
    {"ResolveImmediately", AdditionalMode::ResolveImmediately},
4✔
983
    {"ResolveDeferred", AdditionalMode::ResolveDeferred}};
4✔
984
  if (auto iter = map.find(mode); iter != map.end()) {
4!
985
    return iter->second;
4✔
986
  }
4✔
987
  throw runtime_error("AdditionalMode '" + mode + "' unknown");
×
988
}
4✔
989

990
pdns::ZoneMD::Config cvtZoneMDConfig(const std::string& mode)
991
{
10✔
992
  static const std::map<std::string, pdns::ZoneMD::Config> map = {
10✔
993
    {"ignore", pdns::ZoneMD::Config::Ignore},
10✔
994
    {"validate", pdns::ZoneMD::Config::Validate},
10✔
995
    {"require", pdns::ZoneMD::Config::Require},
10✔
996
  };
10✔
997
  if (auto iter = map.find(mode); iter != map.end()) {
10!
998
    return iter->second;
10✔
999
  }
10✔
1000
  throw runtime_error("ZoneMD config '" + mode + "' unknown");
×
1001
}
10✔
1002

1003
void fromLuaToRust(const std::map<QType, std::pair<std::set<QType>, AdditionalMode>>& allowAdditionalQTypes, pdns::rust::settings::rec::Recursor& rec)
1004
{
2✔
1005
  for (const auto& [qtype, data] : allowAdditionalQTypes) {
4✔
1006
    const auto& [qtypeset, mode] = data;
4✔
1007
    pdns::rust::settings::rec::AllowedAdditionalQType add;
4✔
1008
    add.qtype = qtype.toString();
4✔
1009
    for (const auto& extra : qtypeset) {
10✔
1010
      add.targets.emplace_back(extra.toString());
10✔
1011
    }
10✔
1012
    add.mode = cvt(mode);
4✔
1013
    rec.allowed_additional_qtypes.emplace_back(add);
4✔
1014
  }
4✔
1015
}
2✔
1016

1017
void fromLuaToRust(const ProxyMapping& proxyMapping, pdns::rust::settings::rec::Incoming& incoming)
1018
{
2✔
1019
  for (const auto& mapping : proxyMapping) {
4✔
1020
    pdns::rust::settings::rec::ProxyMapping pmap;
4✔
1021
    pmap.subnet = mapping.first.toString();
4✔
1022
    pmap.address = mapping.second.address.toString();
4✔
1023
    if (mapping.second.suffixMatchNode) {
4✔
1024
      for (const auto& domain : mapping.second.suffixMatchNode->d_tree.getNodes()) {
6✔
1025
        pmap.domains.emplace_back(domain.toString());
6✔
1026
      }
6✔
1027
    }
2✔
1028
    incoming.proxymappings.emplace_back(pmap);
4✔
1029
  }
4✔
1030
}
2✔
1031

1032
void fromLuaToRust(const SortList& arg, pdns::rust::settings::rec::Recursor& rec)
1033
{
2✔
1034
  const auto& sortlist = arg.getTree();
2✔
1035
  for (const auto& iter : sortlist) {
2✔
1036
    pdns::rust::settings::rec::SortList rsl;
2✔
1037
    rsl.key = iter.first.toString();
2✔
1038
    const auto& sub = iter.second;
2✔
1039
    // Some extra work to present them ordered in the YAML
1040
    std::set<int> indexes;
2✔
1041
    std::multimap<int, Netmask> ordered;
2✔
1042
    for (auto& order : sub.d_orders) {
2✔
1043
      indexes.emplace(order.second);
2✔
1044
      ordered.emplace(order.second, order.first);
2✔
1045
    }
2✔
1046
    for (const auto& index : indexes) {
2✔
1047
      const auto& range = ordered.equal_range(index);
2✔
1048
      for (auto subnet = range.first; subnet != range.second; ++subnet) {
4✔
1049
        pdns::rust::settings::rec::SubnetOrder snorder;
2✔
1050
        snorder.order = index;
2✔
1051
        snorder.subnet = subnet->second.toString();
2✔
1052
        rsl.subnets.emplace_back(snorder);
2✔
1053
      }
2✔
1054
    }
2✔
1055
    rec.sortlists.emplace_back(rsl);
2✔
1056
  }
2✔
1057
}
2✔
1058
} // namespace
1059

1060
void pdns::settings::rec::fromLuaConfigToBridgeStruct(LuaConfigItems& luaConfig, const ProxyMapping& proxyMapping, pdns::rust::settings::rec::Recursorsettings& settings)
1061
{
2✔
1062

1063
  fromLuaToRust(luaConfig, settings.dnssec);
2✔
1064
  settings.logging.protobuf_mask_v4 = luaConfig.protobufMaskV4;
2✔
1065
  settings.logging.protobuf_mask_v6 = luaConfig.protobufMaskV6;
2✔
1066
  if (luaConfig.protobufExportConfig.enabled) {
2!
1067
    pdns::rust::settings::rec::ProtobufServer pbServer;
2✔
1068
    fromLuaToRust(luaConfig.protobufExportConfig, pbServer);
2✔
1069
    settings.logging.protobuf_servers.emplace_back(pbServer);
2✔
1070
  }
2✔
1071
  if (luaConfig.outgoingProtobufExportConfig.enabled) {
2!
1072
    pdns::rust::settings::rec::ProtobufServer pbServer;
2✔
1073
    fromLuaToRust(luaConfig.outgoingProtobufExportConfig, pbServer);
2✔
1074
    settings.logging.outgoing_protobuf_servers.emplace_back(pbServer);
2✔
1075
  }
2✔
1076
  if (luaConfig.frameStreamExportConfig.enabled) {
2!
1077
    pdns::rust::settings::rec::DNSTapFrameStreamServer dnstap;
2✔
1078
    fromLuaToRust(luaConfig.frameStreamExportConfig, dnstap);
2✔
1079
    settings.logging.dnstap_framestream_servers.emplace_back(dnstap);
2✔
1080
  }
2✔
1081
  if (luaConfig.nodFrameStreamExportConfig.enabled) {
2!
1082
    pdns::rust::settings::rec::DNSTapNODFrameStreamServer dnstap;
2✔
1083
    fromLuaToRust(luaConfig.nodFrameStreamExportConfig, dnstap);
2✔
1084
    settings.logging.dnstap_nod_framestream_servers.emplace_back(dnstap);
2✔
1085
  }
2✔
1086
  fromLuaToRust(luaConfig.rpzs, settings.recursor);
2✔
1087
  fromLuaToRust(luaConfig.sortlist, settings.recursor);
2✔
1088
  fromLuaToRust(luaConfig.ztcConfigs, settings.recordcache);
2✔
1089
  fromLuaToRust(luaConfig.allowAdditionalQTypes, settings.recursor);
2✔
1090
  fromLuaToRust(proxyMapping, settings.incoming);
2✔
1091
}
2✔
1092

1093
namespace
1094
{
1095
void fromRustToLuaConfig(const pdns::rust::settings::rec::Dnssec& dnssec, LuaConfigItems& luaConfig)
1096
{
25✔
1097
  // This function fills a luaConfig equivalent to the given YAML config, assuming luaConfig has
1098
  // its default content.  The docs say: "If the sequence contains an entry for the root zone, the
1099
  // default root zone trust anchor is not included."  By default, a newly created luaConfig has the
1100
  // TAs for the root in it.  If the YAML config has an entry for these, clear them from
1101
  // luaConfig. Otherwise the default TA's would remain even if the YAML config is trying to set
1102
  // them.  This code actually clears all of the TAs in luaConfig mentioned in the YAML config, but
1103
  // as the luaConfig only contains the root TAs, this is equivalent (but not *very* efficient).
1104
  for (const auto& trustAnchor : dnssec.trustanchors) {
31✔
1105
    auto name = DNSName(std::string(trustAnchor.name));
31✔
1106
    luaConfig.dsAnchors.erase(name);
31✔
1107
  }
31✔
1108
  for (const auto& trustAnchor : dnssec.trustanchors) {
31✔
1109
    auto name = DNSName(std::string(trustAnchor.name));
31✔
1110
    for (const auto& dsRecord : trustAnchor.dsrecords) {
50✔
1111
      auto dsRecContent = std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(std::string(dsRecord)));
50✔
1112
      luaConfig.dsAnchors[name].emplace(*dsRecContent);
50✔
1113
    }
50✔
1114
  }
31✔
1115
  for (const auto& nta : dnssec.negative_trustanchors) {
25✔
1116
    luaConfig.negAnchors[DNSName(std::string(nta.name))] = std::string(nta.reason);
4✔
1117
  }
4✔
1118
  luaConfig.trustAnchorFileInfo.fname = std::string(dnssec.trustanchorfile);
25✔
1119
  luaConfig.trustAnchorFileInfo.interval = dnssec.trustanchorfile_interval;
25✔
1120
}
25✔
1121

1122
void fromRustToLuaConfig(const pdns::rust::settings::rec::ProtobufServer& pbServer, ProtobufExportConfig& exp)
1123
{
13✔
1124
  exp.enabled = true;
13✔
1125
  exp.exportTypes.clear();
13✔
1126
  for (const auto& type : pbServer.exportTypes) {
35✔
1127
    exp.exportTypes.emplace(QType::chartocode(std::string(type).c_str()));
35✔
1128
  }
35✔
1129
  for (const auto& server : pbServer.servers) {
22✔
1130
    exp.servers.emplace_back(std::string(server));
22✔
1131
  }
22✔
1132
  exp.maxQueuedEntries = pbServer.maxQueuedEntries;
13✔
1133
  exp.timeout = pbServer.timeout;
13✔
1134
  exp.reconnectWaitTime = pbServer.reconnectWaitTime;
13✔
1135
  exp.asyncConnect = pbServer.asyncConnect;
13✔
1136
  exp.logQueries = pbServer.logQueries;
13✔
1137
  exp.logResponses = pbServer.logResponses;
13✔
1138
  exp.taggedOnly = pbServer.taggedOnly;
13✔
1139
  exp.logMappedFrom = pbServer.logMappedFrom;
13✔
1140
}
13✔
1141

1142
void fromRustToLuaConfig(const pdns::rust::settings::rec::DNSTapFrameStreamServer& dnstap, FrameStreamExportConfig& exp)
1143
{
2✔
1144
  exp.enabled = true;
2✔
1145
  for (const auto& server : dnstap.servers) {
2✔
1146
    exp.servers.emplace_back(std::string(server));
2✔
1147
  }
2✔
1148
  exp.logQueries = dnstap.logQueries;
2✔
1149
  exp.logResponses = dnstap.logResponses;
2✔
1150
  exp.bufferHint = dnstap.bufferHint;
2✔
1151
  exp.flushTimeout = dnstap.flushTimeout;
2✔
1152
  exp.inputQueueSize = dnstap.inputQueueSize;
2✔
1153
  exp.outputQueueSize = dnstap.outputQueueSize;
2✔
1154
  exp.queueNotifyThreshold = dnstap.queueNotifyThreshold;
2✔
1155
  exp.reopenInterval = dnstap.reopenInterval;
2✔
1156
}
2✔
1157

1158
void fromRustToLuaConfig(const pdns::rust::settings::rec::DNSTapNODFrameStreamServer& dnstap, FrameStreamExportConfig& exp)
1159
{
2✔
1160
  exp.enabled = true;
2✔
1161
  for (const auto& server : dnstap.servers) {
2✔
1162
    exp.servers.emplace_back(std::string(server));
2✔
1163
  }
2✔
1164
  exp.logNODs = dnstap.logNODs;
2✔
1165
  exp.logUDRs = dnstap.logUDRs;
2✔
1166
  exp.bufferHint = dnstap.bufferHint;
2✔
1167
  exp.flushTimeout = dnstap.flushTimeout;
2✔
1168
  exp.inputQueueSize = dnstap.inputQueueSize;
2✔
1169
  exp.outputQueueSize = dnstap.outputQueueSize;
2✔
1170
  exp.queueNotifyThreshold = dnstap.queueNotifyThreshold;
2✔
1171
  exp.reopenInterval = dnstap.reopenInterval;
2✔
1172
}
2✔
1173

1174
DNSFilterEngine::PolicyKind cvtKind(const std::string& kind)
1175
{
2✔
1176
  static const std::map<std::string, DNSFilterEngine::PolicyKind> map = {
2✔
1177
    {"Custom", DNSFilterEngine::PolicyKind::Custom},
2✔
1178
    {"Drop", DNSFilterEngine::PolicyKind::Drop},
2✔
1179
    {"NoAction", DNSFilterEngine::PolicyKind::NoAction},
2✔
1180
    {"NODATA", DNSFilterEngine::PolicyKind::NODATA},
2✔
1181
    {"NXDOMAIN", DNSFilterEngine::PolicyKind::NXDOMAIN},
2✔
1182
    {"Truncate", DNSFilterEngine::PolicyKind::Truncate}};
2✔
1183
  if (auto iter = map.find(kind); iter != map.end()) {
2!
1184
    return iter->second;
2✔
1185
  }
2✔
1186
  throw runtime_error("PolicyKind '" + kind + "' unknown");
×
1187
}
2✔
1188

1189
void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::RPZ>& rpzs, LuaConfigItems& luaConfig)
1190
{
25✔
1191
  for (const auto& rpz : rpzs) {
25✔
1192
    RPZTrackerParams params;
6✔
1193
    for (const auto& address : rpz.addresses) {
6✔
1194
      params.zoneXFRParams.primaries.emplace_back(address);
4✔
1195
    }
4✔
1196
    params.zoneXFRParams.name = std::string(rpz.name);
6✔
1197
    params.polName = std::string(rpz.policyName);
6✔
1198

1199
    if (!rpz.defpol.empty()) {
6✔
1200
      params.defpol = DNSFilterEngine::Policy();
2✔
1201
      params.defcontent = std::string(rpz.defcontent);
2✔
1202
      params.defpol->d_kind = cvtKind(std::string(rpz.defpol));
2✔
1203
      params.defpol->setName(params.polName);
2✔
1204
      if (params.defpol->d_kind == DNSFilterEngine::PolicyKind::Custom) {
2!
1205
        if (!params.defpol->d_custom) {
2!
1206
          params.defpol->d_custom = make_unique<DNSFilterEngine::Policy::CustomData>();
2✔
1207
        }
2✔
1208
        params.defpol->d_custom->push_back(DNSRecordContent::make(QType::CNAME, QClass::IN,
2✔
1209
                                                                  std::string(params.defcontent)));
2✔
1210

1211
        if (rpz.defttl != std::numeric_limits<uint32_t>::max()) {
2!
1212
          params.defpol->d_ttl = static_cast<int>(rpz.defttl);
2✔
1213
        }
2✔
1214
        else {
×
1215
          params.defpol->d_ttl = -1; // get it from the zone
×
1216
        }
×
1217
      }
2✔
1218
    }
2✔
1219
    params.defpolOverrideLocal = rpz.defpolOverrideLocalData;
6✔
1220
    params.extendedErrorCode = rpz.extendedErrorCode;
6✔
1221
    params.extendedErrorExtra = std::string(rpz.extendedErrorExtra);
6✔
1222
    params.includeSOA = rpz.includeSOA;
6✔
1223
    params.ignoreDuplicates = rpz.ignoreDuplicates;
6✔
1224
    params.maxTTL = rpz.maxTTL;
6✔
1225

1226
    for (const auto& tag : rpz.tags) {
6✔
1227
      params.tags.emplace(std::string(tag));
4✔
1228
    }
4✔
1229
    params.defpolOverrideLocal = rpz.overridesGettag;
6✔
1230
    params.zoneXFRParams.zoneSizeHint = rpz.zoneSizeHint;
6✔
1231
    assign(params.zoneXFRParams.tsigtriplet, rpz.tsig);
6✔
1232
    params.zoneXFRParams.refreshFromConf = rpz.refresh;
6✔
1233
    params.zoneXFRParams.maxReceivedMBytes = rpz.maxReceivedMBytes;
6✔
1234
    if (!rpz.localAddress.empty()) {
6!
1235
      params.zoneXFRParams.localAddress = ComboAddress(std::string(rpz.localAddress));
×
1236
    }
×
1237
    params.zoneXFRParams.xfrTimeout = rpz.axfrTimeout;
6✔
1238
    params.dumpZoneFileName = std::string(rpz.dumpFile);
6✔
1239
    params.seedFileName = std::string(rpz.seedFile);
6✔
1240
    luaConfig.rpzs.emplace_back(params);
6✔
1241
  }
6✔
1242
}
25✔
1243

1244
void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::ZoneToCache>& ztcs, map<DNSName, RecZoneToCache::Config>& lua)
1245
{
25✔
1246
  for (const auto& ztc : ztcs) {
25✔
1247
    DNSName zone = DNSName(std::string(ztc.zone));
5✔
1248
    RecZoneToCache::Config lztc;
5✔
1249
    for (const auto& source : ztc.sources) {
5✔
1250
      lztc.d_sources.emplace_back(std::string(source));
5✔
1251
    }
5✔
1252
    lztc.d_zone = std::string(ztc.zone);
5✔
1253
    lztc.d_method = std::string(ztc.method);
5✔
1254
    if (!ztc.localAddress.empty()) {
5✔
1255
      lztc.d_local = ComboAddress(std::string(ztc.localAddress));
2✔
1256
    }
2✔
1257
    if (!ztc.tsig.name.empty()) {
5✔
1258
      lztc.d_tt.name = DNSName(std::string(ztc.tsig.name));
2✔
1259
      lztc.d_tt.algo = DNSName(std::string(ztc.tsig.algo));
2✔
1260
      B64Decode(std::string(ztc.tsig.secret), lztc.d_tt.secret);
2✔
1261
    }
2✔
1262
    lztc.d_maxReceivedBytes = ztc.maxReceivedMBytes;
5✔
1263
    lztc.d_retryOnError = static_cast<time_t>(ztc.retryOnErrorPeriod);
5✔
1264
    lztc.d_refreshPeriod = static_cast<time_t>(ztc.refreshPeriod);
5✔
1265
    lztc.d_timeout = ztc.timeout;
5✔
1266
    lztc.d_zonemd = cvtZoneMDConfig(std::string(ztc.zonemd));
5✔
1267
    lztc.d_dnssec = cvtZoneMDConfig(std::string(ztc.dnssec));
5✔
1268
    lua.emplace(zone, lztc);
5✔
1269
  }
5✔
1270
}
25✔
1271

1272
void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::SortList>& sortlists, SortList& lua)
1273
{
25✔
1274
  for (const auto& sortlist : sortlists) {
25✔
1275
    for (const auto& entry : sortlist.subnets) {
2✔
1276
      lua.addEntry(Netmask(std::string(sortlist.key)), Netmask(std::string(entry.subnet)), static_cast<int>(entry.order));
2✔
1277
    }
2✔
1278
  }
2✔
1279
}
25✔
1280

1281
void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::AllowedAdditionalQType>& alloweds, std::map<QType, std::pair<std::set<QType>, AdditionalMode>>& lua)
1282
{
25✔
1283
  for (const auto& allowed : alloweds) {
25✔
1284
    QType qtype(QType::chartocode(std::string(allowed.qtype).c_str()));
4✔
1285
    std::set<QType> set;
4✔
1286
    for (const auto& target : allowed.targets) {
10✔
1287
      set.emplace(QType::chartocode(std::string(target).c_str()));
10✔
1288
    }
10✔
1289
    AdditionalMode mode = AdditionalMode::CacheOnlyRequireAuth;
4✔
1290
    mode = cvtAdditional(std::string(allowed.mode));
4✔
1291
    lua.emplace(qtype, std::pair{set, mode});
4✔
1292
  }
4✔
1293
}
25✔
1294

1295
void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::ProxyMapping>& pmaps, ProxyMapping& proxyMapping)
1296
{
25✔
1297
  for (const auto& pmap : pmaps) {
25✔
1298
    Netmask subnet = Netmask(std::string(pmap.subnet));
4✔
1299
    ComboAddress address(std::string(pmap.address));
4✔
1300
    boost::optional<SuffixMatchNode> smn;
4✔
1301
    if (!pmap.domains.empty()) {
4✔
1302
      smn = boost::make_optional(SuffixMatchNode{});
2✔
1303
      for (const auto& dom : pmap.domains) {
6✔
1304
        smn->add(DNSName(std::string(dom)));
6✔
1305
      }
6✔
1306
    }
2✔
1307
    proxyMapping.insert_or_assign(subnet, {address, smn});
4✔
1308
  }
4✔
1309
}
25✔
1310

1311
void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::ForwardingCatalogZone>& catzones, std::vector<FWCatalogZone>& lua)
1312
{
25✔
1313
  for (const auto& catz : catzones) {
25✔
1314
    FWCatalogZone fwcatz;
1✔
1315
    for (const auto& def : catz.groups) {
2✔
1316
      fwcatz.d_defaults.emplace(def.name, def);
2✔
1317
    }
2✔
1318
    fwcatz.d_catz = std::make_shared<CatalogZone>();
1✔
1319

1320
    for (const auto& address : catz.xfr.addresses) {
2✔
1321
      fwcatz.d_params.primaries.emplace_back(address);
2✔
1322
    }
2✔
1323
    fwcatz.d_params.name = std::string(catz.zone);
1✔
1324
    fwcatz.d_params.zoneSizeHint = catz.xfr.zoneSizeHint;
1✔
1325
    assign(fwcatz.d_params.tsigtriplet, catz.xfr.tsig);
1✔
1326
    fwcatz.d_params.refreshFromConf = catz.xfr.refresh;
1✔
1327
    fwcatz.d_params.maxReceivedMBytes = catz.xfr.maxReceivedMBytes;
1✔
1328
    if (!catz.xfr.localAddress.empty()) {
1!
1329
      fwcatz.d_params.localAddress = ComboAddress(std::string(catz.xfr.localAddress));
×
1330
    }
×
1331
    fwcatz.d_params.xfrTimeout = catz.xfr.axfrTimeout;
1✔
1332
    lua.emplace_back(std::move(fwcatz));
1✔
1333
  }
1✔
1334
}
25✔
1335

1336
void fromRustToOTTraceConditions(const rust::Vec<pdns::rust::settings::rec::OpenTelemetryTraceCondition>& settings, OpenTelemetryTraceConditions& conditions)
1337
{
25✔
1338
  for (const auto& setting : settings) {
25✔
1339
    OpenTelemetryTraceCondition condition;
9✔
1340
    if (!setting.qnames.empty()) {
9✔
1341
      condition.d_qnames = SuffixMatchNode();
2✔
1342
    }
2✔
1343
    for (const auto& qname : setting.qnames) {
9✔
1344
      condition.d_qnames->add(DNSName(std::string(qname)));
2✔
1345
    }
2✔
1346
    if (!setting.qtypes.empty()) {
9✔
1347
      condition.d_qtypes = std::unordered_set<QType>();
2✔
1348
    }
2✔
1349
    for (const auto& qtype : setting.qtypes) {
9✔
1350
      condition.d_qtypes->insert(QType::chartocode(std::string(qtype).data()));
2✔
1351
    }
2✔
1352
    if (setting.qid != std::numeric_limits<uint32_t>::max()) {
9!
NEW
1353
      condition.d_qid = setting.qid;
×
NEW
1354
    }
×
1355
    condition.d_edns_option_required = setting.edns_option_required;
9✔
1356
    condition.d_traceid_only = setting.traceid_only;
9✔
1357
    for (const auto& acl : setting.acls) {
9✔
1358
      conditions.insert(std::string(acl)).second = condition;
9✔
1359
    }
9✔
1360
  }
9✔
1361
}
25✔
1362
}
1363

1364
void pdns::settings::rec::fromBridgeStructToLuaConfig(const pdns::rust::settings::rec::Recursorsettings& settings, LuaConfigItems& luaConfig, ProxyMapping& proxyMapping, OpenTelemetryTraceConditions& conditions)
1365
{
25✔
1366
  fromRustToLuaConfig(settings.dnssec, luaConfig);
25✔
1367
  luaConfig.protobufMaskV4 = settings.logging.protobuf_mask_v4;
25✔
1368
  luaConfig.protobufMaskV6 = settings.logging.protobuf_mask_v6;
25✔
1369
  if (!settings.logging.protobuf_servers.empty()) {
25✔
1370
    fromRustToLuaConfig(settings.logging.protobuf_servers.at(0), luaConfig.protobufExportConfig);
11✔
1371
  }
11✔
1372
  if (!settings.logging.outgoing_protobuf_servers.empty()) {
25✔
1373
    fromRustToLuaConfig(settings.logging.outgoing_protobuf_servers.at(0), luaConfig.outgoingProtobufExportConfig);
2✔
1374
  }
2✔
1375
  if (!settings.logging.dnstap_framestream_servers.empty()) {
25✔
1376
    fromRustToLuaConfig(settings.logging.dnstap_framestream_servers.at(0), luaConfig.frameStreamExportConfig);
2✔
1377
  }
2✔
1378
  if (!settings.logging.dnstap_nod_framestream_servers.empty()) {
25✔
1379
    fromRustToLuaConfig(settings.logging.dnstap_nod_framestream_servers.at(0), luaConfig.nodFrameStreamExportConfig);
2✔
1380
  }
2✔
1381
  fromRustToLuaConfig(settings.recursor.rpzs, luaConfig);
25✔
1382
  fromRustToLuaConfig(settings.recursor.sortlists, luaConfig.sortlist);
25✔
1383
  fromRustToLuaConfig(settings.recordcache.zonetocaches, luaConfig.ztcConfigs);
25✔
1384
  fromRustToLuaConfig(settings.recursor.allowed_additional_qtypes, luaConfig.allowAdditionalQTypes);
25✔
1385
  fromRustToLuaConfig(settings.recursor.forwarding_catalog_zones, luaConfig.catalogzones);
25✔
1386
  fromRustToLuaConfig(settings.incoming.proxymappings, proxyMapping);
25✔
1387
  fromRustToOTTraceConditions(settings.logging.opentelemetry_trace_conditions, conditions);
25✔
1388
}
25✔
1389

1390
// Return true if an item that's (also) a Lua config item is set
1391
bool pdns::settings::rec::luaItemSet(const pdns::rust::settings::rec::Recursorsettings& settings)
1392
{
37✔
1393
  bool alldefault = true;
37✔
1394
  for (const auto& trustanchor : settings.dnssec.trustanchors) {
37✔
1395
    if (trustanchor.name == ".") {
37!
1396
      if (trustanchor.dsrecords.size() != rootDSs.size()) {
37✔
1397
        alldefault = false;
2✔
1398
        break;
2✔
1399
      }
2✔
1400
      for (const auto& dsRecord : trustanchor.dsrecords) {
70✔
1401
        if (std::find(rootDSs.begin(), rootDSs.end(), std::string(dsRecord)) == rootDSs.end()) {
70!
1402
          alldefault = false;
×
1403
          break;
×
1404
        }
×
1405
      }
70✔
1406
    }
35✔
1407
    else {
×
1408
      alldefault = false;
×
1409
      break;
×
1410
    }
×
1411
  }
37✔
1412
  alldefault = alldefault && settings.dnssec.negative_trustanchors.empty();
37!
1413
  alldefault = alldefault && settings.dnssec.trustanchorfile.empty();
37!
1414
  alldefault = alldefault && settings.dnssec.trustanchorfile_interval == 24;
37!
1415
  alldefault = alldefault && settings.logging.protobuf_mask_v4 == 32;
37!
1416
  alldefault = alldefault && settings.logging.protobuf_mask_v6 == 128;
37!
1417
  alldefault = alldefault && settings.logging.protobuf_servers.empty();
37✔
1418
  alldefault = alldefault && settings.logging.outgoing_protobuf_servers.empty();
37!
1419
  alldefault = alldefault && settings.logging.dnstap_framestream_servers.empty();
37!
1420
  alldefault = alldefault && settings.logging.dnstap_nod_framestream_servers.empty();
37!
1421
  alldefault = alldefault && settings.recursor.sortlists.empty();
37!
1422
  alldefault = alldefault && settings.recursor.rpzs.empty();
37!
1423
  alldefault = alldefault && settings.recursor.forwarding_catalog_zones.empty();
37✔
1424
  alldefault = alldefault && settings.recordcache.zonetocaches.empty();
37✔
1425
  alldefault = alldefault && settings.recursor.allowed_additional_qtypes.empty();
37!
1426
  alldefault = alldefault && settings.incoming.proxymappings.empty();
37!
1427
  alldefault = alldefault && settings.outgoing.tls_configurations.empty(); // actually not a Lua item, but very much alike
37✔
1428
  alldefault = alldefault && settings.logging.opentelemetry_trace_conditions.empty(); // actually not a Lua item, but very much alike
37!
1429
  return !alldefault;
37✔
1430
}
37✔
1431

1432
pdns::settings::rec::YamlSettingsStatus pdns::settings::rec::tryReadYAML(const string& yamlconfigname, bool setGlobals, bool& yamlSettings, bool& luaSettingsInYAML, rust::settings::rec::Recursorsettings& settings, Logr::log_t startupLog, Logr::Priority level)
1433
{
375✔
1434
  string msg;
375✔
1435
  // TODO: handle include-dir on command line
1436
  auto yamlstatus = pdns::settings::rec::readYamlSettings(yamlconfigname, ::arg()["include-dir"], settings, msg, startupLog);
375✔
1437

1438
  switch (yamlstatus) {
375!
1439
  case pdns::settings::rec::YamlSettingsStatus::CannotOpen:
186✔
1440
    startupLog->error(level, msg, "No YAML config found", "configname", Logging::Loggable(yamlconfigname));
186✔
1441
    break;
186✔
1442

1443
  case pdns::settings::rec::YamlSettingsStatus::PresentButFailed:
152✔
1444
    startupLog->error(Logr::Error, msg, "YAML config found, but error occurred processing it", "configname", Logging::Loggable(yamlconfigname));
152✔
1445
    break;
152✔
1446

1447
  case pdns::settings::rec::YamlSettingsStatus::OK:
37✔
1448
    yamlSettings = true;
37✔
1449
    startupLog->info(Logr::Notice, "YAML config found and processed", "configname", Logging::Loggable(yamlconfigname));
37✔
1450
    pdns::settings::rec::processAPIDir(arg()["include-dir"], settings, startupLog);
37✔
1451
    luaSettingsInYAML = pdns::settings::rec::luaItemSet(settings);
37✔
1452
    if (luaSettingsInYAML && !settings.recursor.lua_config_file.empty()) {
37!
1453
      const std::string err = "YAML settings include values originally in Lua but also sets `recursor.lua_config_file`. This is unsupported";
×
1454
      startupLog->info(Logr::Error, err, "configname", Logging::Loggable(yamlconfigname));
×
1455
      yamlstatus = pdns::settings::rec::PresentButFailed;
×
1456
    }
×
1457
    else if (!settings.recursor.forwarding_catalog_zones.empty() && settings.webservice.api_dir.empty()) {
37!
1458
      startupLog->info(Logr::Error, "Catalog zones defined, but webservice.api_dir is not set", "configname", Logging::Loggable(yamlconfigname));
×
1459
      yamlstatus = pdns::settings::rec::PresentButFailed;
×
1460
    }
×
1461
    else if (setGlobals) {
37✔
1462
      pdns::settings::rec::bridgeStructToOldStyleSettings(settings);
18✔
1463
    }
18✔
1464
    break;
37✔
1465
  }
375✔
1466
  return yamlstatus;
375✔
1467
}
375✔
1468

1469
namespace pdns::rust::misc
1470
{
1471

1472
template <typename M>
1473
Wrapper<M>::Wrapper(const M& arg) :
1474
  d_ptr(std::make_unique<M>(arg))
1475
{
169✔
1476
}
169✔
1477

1478
template <typename M>
1479
Wrapper<M>::~Wrapper() = default;
1480

1481
template <typename M>
1482
[[nodiscard]] const M& Wrapper<M>::get() const
1483
{
290✔
1484
  return *d_ptr;
290✔
1485
}
290✔
1486

1487
template class Wrapper<::NetmaskGroup>;
1488
template class Wrapper<::ComboAddress>;
1489

1490
uint16_t qTypeStringToCode(::rust::Str str)
1491
{
93✔
1492
  std::string tmp(str.data(), str.length());
93✔
1493
  return QType::chartocode(tmp.c_str());
93✔
1494
}
93✔
1495

1496
bool isValidHostname(::rust::Str str)
1497
{
28✔
1498
  try {
28✔
1499
    auto name = DNSName(string(str));
28✔
1500
    return name.isHostname();
28✔
1501
  }
28✔
1502
  catch (...) {
28✔
1503
    return false;
×
1504
  }
×
1505
}
28✔
1506

1507
std::unique_ptr<ComboAddress> comboaddress(::rust::Str str)
1508
{
145✔
1509
  return std::make_unique<ComboAddress>(::ComboAddress(std::string(str)));
145✔
1510
}
145✔
1511

1512
bool matches(const std::unique_ptr<NetmaskGroup>& nmg, const std::unique_ptr<ComboAddress>& address)
1513
{
145✔
1514
  return nmg->get().match(address->get());
145✔
1515
}
145✔
1516

1517
void log(const std::shared_ptr<Logger>& logger, pdns::rust::misc::Priority log_level, ::rust::Str msg, const ::rust::Vec<KeyValue>& values)
1518
{
134✔
1519
  auto log = logger;
134✔
1520
  for (const auto& [key, value] : values) {
698✔
1521
    log = log->withValues(std::string(key), Logging::Loggable(std::string(value)));
698✔
1522
  }
698✔
1523
  log->info(static_cast<Logr::Priority>(log_level), std::string(msg));
134✔
1524
}
134✔
1525

1526
void error(const std::shared_ptr<Logger>& logger, pdns::rust::misc::Priority log_level, ::rust::Str error, ::rust::Str msg, const ::rust::Vec<KeyValue>& values)
1527
{
1✔
1528
  auto log = logger;
1✔
1529
  for (const auto& [key, value] : values) {
1!
1530
    log = log->withValues(std::string(key), Logging::Loggable(std::string(value)));
×
1531
  }
×
1532
  log->error(static_cast<Logr::Priority>(log_level), std::string(error), std::string(msg));
1✔
1533
}
1✔
1534

1535
std::shared_ptr<Logger> withValue(const std::shared_ptr<Logger>& logger, ::rust::Str key, ::rust::Str val)
1536
{
250✔
1537
  auto ret = logger->withValues(std::string(key), Logging::Loggable(std::string(val)));
250✔
1538
  return ret;
250✔
1539
}
250✔
1540

1541
::rust::String getUUID()
1542
{
108✔
1543
  auto uuid = getUniqueID();
108✔
1544
  return to_string(uuid);
108✔
1545
}
108✔
1546
}
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