• 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

75.64
/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

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

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

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

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

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

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

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

135
static void mergeYamlSubFile(const std::string& configname, Recursorsettings& settings, bool allowabsent, Logr::log_t log)
136
{
56✔
137
  auto file = ifstream(configname);
56✔
138
  if (!file.is_open()) {
56✔
139
    if (allowabsent) {
4!
140
      return;
4✔
141
    }
4✔
142
    throw runtime_error("Cannot open " + configname);
×
143
  }
4✔
144
  SLOG(g_log << Logger::Notice << "Processing YAML settings from " << configname << endl,
52✔
145
       log->info(Logr::Notice, "Processing YAML settings", "path", Logging::Loggable(configname)));
52✔
146
  auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
52✔
147
  pdns::rust::settings::rec::merge(settings, data);
52✔
148
}
52✔
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
{
10✔
295
  auto apiDir = std::string(settings.webservice.api_dir);
10✔
296
  if (apiDir.empty()) {
10✔
297
    return;
7✔
298
  }
7✔
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
{
81✔
321
  for (const auto& item : vec) {
81!
322
    if (item.notify_allowed) {
13!
323
      settings.incoming.allow_notify_for.emplace_back(item.zone);
13✔
324
    }
13✔
325
  }
13✔
326
}
81✔
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,659✔
330
  auto file = ifstream(configname);
1,659✔
331
  if (!file.is_open()) {
1,659✔
332
    msg = stringerror(errno);
908✔
333
    return YamlSettingsStatus::CannotOpen;
908✔
334
  }
908✔
335
  SLOG(g_log << Logger::Notice << "Processing main YAML settings from " << configname << endl,
751✔
336
       log->info(Logr::Notice, "Processing main YAML settings", "path", Logging::Loggable(configname)));
751✔
337
  try {
751✔
338
    auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
751✔
339
    auto yamlstruct = pdns::rust::settings::rec::parse_yaml_string(data);
751✔
340
    std::vector<std::string> yamlFiles;
751✔
341
    ::arg().gatherIncludes(!includeDirOnCommandLine.empty() ? includeDirOnCommandLine : string(yamlstruct.recursor.include_dir),
751✔
342
                           ".yml", yamlFiles);
751✔
343
    for (const auto& yamlfile : yamlFiles) {
751✔
344
      mergeYamlSubFile(yamlfile, yamlstruct, false, log);
50✔
345
    }
50✔
346
    // Add the zones with notify_allowed to allow_notify_for. For a forward_zones_file that will be
347
    // taken care of elsewhere.  One drawback: the zones will be shown in allow_notify_for if you
348
    // run --config, while they aren't actually there in any config file.
349
    addToAllowNotifyFor(yamlstruct, yamlstruct.recursor.forward_zones);
751✔
350
    addToAllowNotifyFor(yamlstruct, yamlstruct.recursor.forward_zones_recurse);
751✔
351
    addToAllowNotifyFor(yamlstruct, yamlstruct.recursor.forwarding_catalog_zones);
751✔
352
    yamlstruct.validate();
751✔
353
    settings = std::move(yamlstruct);
751✔
354
    return YamlSettingsStatus::OK;
751✔
355
  }
751✔
356
  catch (const ::rust::Error& ex) {
751✔
357
    msg = ex.what();
724✔
358
    return YamlSettingsStatus::PresentButFailed;
724✔
359
  }
724✔
360
  catch (const std::exception& ex) {
751✔
361
    msg = ex.what();
×
362
    return YamlSettingsStatus::PresentButFailed;
×
363
  }
×
364
  catch (...) {
751✔
365
    msg = "Unexpected exception processing YAML";
×
366
    return YamlSettingsStatus::PresentButFailed;
×
367
  }
×
368
}
751✔
369

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

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

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

412
std::string pdns::settings::rec::to_arg(const AuthZone& authzone)
413
{
4✔
414
  std::ostringstream str;
4✔
415
  str << to_arg(authzone.zone) << '=' << to_arg(authzone.file);
4✔
416
  return str.str();
4✔
417
}
4✔
418

419
std::string pdns::settings::rec::to_arg(const ForwardZone& forwardzone)
420
{
×
421
  std::ostringstream str;
×
422
  str << to_arg(forwardzone.zone) << '=';
×
423
  const auto& vec = forwardzone.forwarders;
×
424
  for (auto iter = vec.begin(); iter != vec.end(); ++iter) {
×
425
    if (iter != vec.begin()) {
×
426
      str << ';';
×
427
    }
×
428
    str << to_arg(*iter);
×
429
  }
×
430
  return str.str();
×
431
}
×
432

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

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

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

462
  checked_stoi_into(field, val, nullptr, 0);
178✔
463
}
178✔
464

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

472
  const auto* cptr_orig = val.c_str();
2✔
473
  char* cptr_ret = nullptr;
2✔
474
  auto retval = strtod(cptr_orig, &cptr_ret);
2✔
475

476
  field = retval;
2✔
477
}
2✔
478

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

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

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

509
static bool simpleRustType(const ::rust::String& rname)
510
{
×
511
  return rname == "bool" || rname == "u64" || rname == "f64" || rname == "String";
×
512
}
×
513

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

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

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

582
std::string pdns::settings::rec::oldStyleSettingsFileToYaml(const string& fname, bool mainFile)
583
{
1,359✔
584
  string line;
1,359✔
585
  string pline;
1,359✔
586

587
  std::ifstream configFileStream(fname);
1,359✔
588
  if (!configFileStream) {
1,359!
589
    int err = errno;
×
590
    throw runtime_error("Cannot read " + fname + ": " + stringerror(err));
×
591
  }
×
592

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

599
    if (!pline.empty() && pline[pline.size() - 1] == '\\') {
2,718!
600
      line += pline.substr(0, pline.length() - 1);
×
601
      continue;
×
602
    }
×
603

604
    line += pline;
2,718✔
605

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

616
    // strip trailing spaces
617
    boost::trim_right(line);
2,718✔
618

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

625
    processLine("--" + line, map, mainFile);
2,718✔
626
    line = "";
2,718✔
627
  }
2,718✔
628

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

919
    rec.rpzs.emplace_back(rustrpz);
6✔
920
  }
6✔
921
}
2✔
922

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

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

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

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

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

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

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

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

1064
void pdns::settings::rec::fromLuaConfigToBridgeStruct(LuaConfigItems& luaConfig, const ProxyMapping& proxyMapping, pdns::rust::settings::rec::Recursorsettings& settings)
1065
{
2✔
1066

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1341
void pdns::settings::rec::fromBridgeStructToLuaConfig(const pdns::rust::settings::rec::Recursorsettings& settings, LuaConfigItems& luaConfig, ProxyMapping& proxyMapping)
1342
{
9✔
1343
  fromRustToLuaConfig(settings.dnssec, luaConfig);
9✔
1344
  luaConfig.protobufMaskV4 = settings.logging.protobuf_mask_v4;
9✔
1345
  luaConfig.protobufMaskV6 = settings.logging.protobuf_mask_v6;
9✔
1346
  if (!settings.logging.protobuf_servers.empty()) {
9✔
1347
    fromRustToLuaConfig(settings.logging.protobuf_servers.at(0), luaConfig.protobufExportConfig);
2✔
1348
  }
2✔
1349
  if (!settings.logging.outgoing_protobuf_servers.empty()) {
9✔
1350
    fromRustToLuaConfig(settings.logging.outgoing_protobuf_servers.at(0), luaConfig.outgoingProtobufExportConfig);
2✔
1351
  }
2✔
1352
  if (!settings.logging.dnstap_framestream_servers.empty()) {
9✔
1353
    fromRustToLuaConfig(settings.logging.dnstap_framestream_servers.at(0), luaConfig.frameStreamExportConfig);
2✔
1354
  }
2✔
1355
  if (!settings.logging.dnstap_nod_framestream_servers.empty()) {
9✔
1356
    fromRustToLuaConfig(settings.logging.dnstap_nod_framestream_servers.at(0), luaConfig.nodFrameStreamExportConfig);
2✔
1357
  }
2✔
1358
  fromRustToLuaConfig(settings.recursor.rpzs, luaConfig);
9✔
1359
  fromRustToLuaConfig(settings.recursor.sortlists, luaConfig.sortlist);
9✔
1360
  fromRustToLuaConfig(settings.recordcache.zonetocaches, luaConfig.ztcConfigs);
9✔
1361
  fromRustToLuaConfig(settings.recursor.allowed_additional_qtypes, luaConfig.allowAdditionalQTypes);
9✔
1362
  fromRustToLuaConfig(settings.recursor.forwarding_catalog_zones, luaConfig.catalogzones);
9✔
1363
  fromRustToLuaConfig(settings.incoming.proxymappings, proxyMapping);
9✔
1364
}
9✔
1365

1366
// Return true if an item that's (also) a Lua config item is set
1367
bool pdns::settings::rec::luaItemSet(const pdns::rust::settings::rec::Recursorsettings& settings)
1368
{
10✔
1369
  bool alldefault = true;
10✔
1370
  for (const auto& trustanchor : settings.dnssec.trustanchors) {
10✔
1371
    if (trustanchor.name == ".") {
10!
1372
      if (trustanchor.dsrecords.size() != rootDSs.size()) {
10✔
1373
        alldefault = false;
2✔
1374
        break;
2✔
1375
      }
2✔
1376
      for (const auto& dsRecord : trustanchor.dsrecords) {
16✔
1377
        if (std::find(rootDSs.begin(), rootDSs.end(), std::string(dsRecord)) == rootDSs.end()) {
16!
1378
          alldefault = false;
×
1379
          break;
×
1380
        }
×
1381
      }
16✔
1382
    }
8✔
1383
    else {
×
1384
      alldefault = false;
×
1385
      break;
×
1386
    }
×
1387
  }
10✔
1388
  alldefault = alldefault && settings.dnssec.negative_trustanchors.empty();
10!
1389
  alldefault = alldefault && settings.dnssec.trustanchorfile.empty();
10!
1390
  alldefault = alldefault && settings.dnssec.trustanchorfile_interval == 24;
10!
1391
  alldefault = alldefault && settings.logging.protobuf_mask_v4 == 32;
10!
1392
  alldefault = alldefault && settings.logging.protobuf_mask_v6 == 128;
10!
1393
  alldefault = alldefault && settings.logging.protobuf_servers.empty();
10!
1394
  alldefault = alldefault && settings.logging.outgoing_protobuf_servers.empty();
10!
1395
  alldefault = alldefault && settings.logging.dnstap_framestream_servers.empty();
10!
1396
  alldefault = alldefault && settings.logging.dnstap_nod_framestream_servers.empty();
10!
1397
  alldefault = alldefault && settings.recursor.sortlists.empty();
10!
1398
  alldefault = alldefault && settings.recursor.rpzs.empty();
10!
1399
  alldefault = alldefault && settings.recursor.forwarding_catalog_zones.empty();
10✔
1400
  alldefault = alldefault && settings.recordcache.zonetocaches.empty();
10✔
1401
  alldefault = alldefault && settings.recursor.allowed_additional_qtypes.empty();
10!
1402
  alldefault = alldefault && settings.incoming.proxymappings.empty();
10!
1403
  return !alldefault;
10✔
1404
}
10✔
1405

1406
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)
1407
{
350✔
1408
  string msg;
350✔
1409
  // TODO: handle include-dir on command line
1410
  auto yamlstatus = pdns::settings::rec::readYamlSettings(yamlconfigname, ::arg()["include-dir"], settings, msg, startupLog);
350✔
1411

1412
  switch (yamlstatus) {
350!
1413
  case pdns::settings::rec::YamlSettingsStatus::CannotOpen:
187✔
1414
    SLOG(g_log << Logger::Debug << "No YAML config found for configname '" << yamlconfigname << "': " << msg << endl,
187✔
1415
         startupLog->error(Logr::Debug, msg, "No YAML config found", "configname", Logging::Loggable(yamlconfigname)));
187✔
1416
    break;
187✔
1417

1418
  case pdns::settings::rec::YamlSettingsStatus::PresentButFailed:
153✔
1419
    SLOG(g_log << Logger::Error << "YAML config found for configname '" << yamlconfigname << "' but error ocurred processing it" << endl,
153✔
1420
         startupLog->error(Logr::Error, msg, "YAML config found, but error occurred processing it", "configname", Logging::Loggable(yamlconfigname)));
153✔
1421
    break;
153✔
1422

1423
  case pdns::settings::rec::YamlSettingsStatus::OK:
10✔
1424
    yamlSettings = true;
10✔
1425
    SLOG(g_log << Logger::Notice << "YAML config found and processed for configname '" << yamlconfigname << "'" << endl,
10✔
1426
         startupLog->info(Logr::Notice, "YAML config found and processed", "configname", Logging::Loggable(yamlconfigname)));
10✔
1427
    pdns::settings::rec::processAPIDir(arg()["include-dir"], settings, startupLog);
10✔
1428
    luaSettingsInYAML = pdns::settings::rec::luaItemSet(settings);
10✔
1429
    if (luaSettingsInYAML && !settings.recursor.lua_config_file.empty()) {
10!
1430
      const std::string err = "YAML settings include values originally in Lua but also sets `recursor.lua_config_file`. This is unsupported";
×
1431
      SLOG(g_log << Logger::Error << err << endl,
×
1432
           startupLog->info(Logr::Error, err, "configname", Logging::Loggable(yamlconfigname)));
×
1433
      yamlstatus = pdns::settings::rec::PresentButFailed;
×
1434
    }
×
1435
    else if (!settings.recursor.forwarding_catalog_zones.empty() && settings.webservice.api_dir.empty()) {
10!
1436
      startupLog->info(Logr::Error, "Catalog zones defined, but webservice.api_dir is not set", "configname", Logging::Loggable(yamlconfigname));
×
1437
      yamlstatus = pdns::settings::rec::PresentButFailed;
×
1438
    }
×
1439
    else if (setGlobals) {
10✔
1440
      pdns::settings::rec::bridgeStructToOldStyleSettings(settings);
7✔
1441
    }
7✔
1442
    break;
10✔
1443
  }
350✔
1444
  return yamlstatus;
350✔
1445
}
350✔
1446

1447
namespace pdns::rust::misc
1448
{
1449

1450
template <typename M>
1451
Wrapper<M>::Wrapper(const M& arg) :
1452
  d_ptr(std::make_unique<M>(arg))
1453
{
157✔
1454
}
157✔
1455

1456
template <typename M>
1457
Wrapper<M>::~Wrapper() = default;
1458

1459
template <typename M>
1460
[[nodiscard]] const M& Wrapper<M>::get() const
1461
{
274✔
1462
  return *d_ptr;
274✔
1463
}
274✔
1464

1465
template class Wrapper<::NetmaskGroup>;
1466
template class Wrapper<::ComboAddress>;
1467

1468
uint16_t qTypeStringToCode(::rust::Str str)
1469
{
32✔
1470
  std::string tmp(str.data(), str.length());
32✔
1471
  return QType::chartocode(tmp.c_str());
32✔
1472
}
32✔
1473

1474
bool isValidHostname(::rust::Str str)
1475
{
28✔
1476
  try {
28✔
1477
    auto name = DNSName(string(str));
28✔
1478
    return name.isHostname();
28✔
1479
  }
28✔
1480
  catch (...) {
28✔
1481
    return false;
×
1482
  }
×
1483
}
28✔
1484

1485
std::unique_ptr<ComboAddress> comboaddress(::rust::Str str)
1486
{
137✔
1487
  return std::make_unique<ComboAddress>(::ComboAddress(std::string(str)));
137✔
1488
}
137✔
1489

1490
bool matches(const std::unique_ptr<NetmaskGroup>& nmg, const std::unique_ptr<ComboAddress>& address)
1491
{
137✔
1492
  return nmg->get().match(address->get());
137✔
1493
}
137✔
1494

1495
void log(const std::shared_ptr<Logger>& logger, pdns::rust::misc::Priority log_level, ::rust::Str msg, const ::rust::Vec<KeyValue>& values)
1496
{
126✔
1497
  auto log = logger;
126✔
1498
  for (const auto& [key, value] : values) {
666✔
1499
    log = log->withValues(std::string(key), Logging::Loggable(std::string(value)));
666✔
1500
  }
666✔
1501
  log->info(static_cast<Logr::Priority>(log_level), std::string(msg));
126✔
1502
}
126✔
1503

1504
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)
1505
{
1✔
1506
  auto log = logger;
1✔
1507
  for (const auto& [key, value] : values) {
1!
1508
    log = log->withValues(std::string(key), Logging::Loggable(std::string(value)));
×
1509
  }
×
1510
  log->error(static_cast<Logr::Priority>(log_level), std::string(error), std::string(msg));
1✔
1511
}
1✔
1512

1513
std::shared_ptr<Logger> withValue(const std::shared_ptr<Logger>& logger, ::rust::Str key, ::rust::Str val)
1514
{
238✔
1515
  auto ret = logger->withValues(std::string(key), Logging::Loggable(std::string(val)));
238✔
1516
  return ret;
238✔
1517
}
238✔
1518

1519
}
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