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

PowerDNS / pdns / 15920880335

26 Jun 2025 03:30PM UTC coverage: 61.923% (-3.7%) from 65.652%
15920880335

push

github

web-flow
Merge pull request #15669 from miodvallat/serial_keyer

Increase zone serial number after zone key operations

38311 of 91850 branches covered (41.71%)

Branch coverage included in aggregate %.

27 of 29 new or added lines in 1 file covered. (93.1%)

6308 existing lines in 78 files now uncovered.

120482 of 164587 relevant lines covered (73.2%)

5965233.22 hits per line

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

49.42
/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)
UNCOV
136
{
×
UNCOV
137
  auto file = ifstream(configname);
×
UNCOV
138
  if (!file.is_open()) {
×
UNCOV
139
    if (allowabsent) {
×
UNCOV
140
      return;
×
UNCOV
141
    }
×
142
    throw runtime_error("Cannot open " + configname);
×
UNCOV
143
  }
×
UNCOV
144
  SLOG(g_log << Logger::Notice << "Processing YAML settings from " << configname << endl,
×
UNCOV
145
       log->info(Logr::Notice, "Processing YAML settings", "path", Logging::Loggable(configname)));
×
UNCOV
146
  auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
×
UNCOV
147
  pdns::rust::settings::rec::merge(settings, data);
×
UNCOV
148
}
×
149

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

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

UNCOV
190
  string yamlfilename = apiDir;
×
UNCOV
191
  yamlfilename.append("/").append(filename).append(".yml");
×
UNCOV
192
  string tmpfilename = yamlfilename + ".tmp";
×
UNCOV
193
  ofstream ofconf(tmpfilename);
×
UNCOV
194
  if (!ofconf) {
×
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
  }
×
UNCOV
199
  ofconf << "# Generated by pdns-recursor REST API, DO NOT EDIT" << endl;
×
UNCOV
200
  ofconf << yaml << endl;
×
UNCOV
201
  ofconf.close();
×
UNCOV
202
  if (ofconf.bad()) {
×
203
    log->error(Logr::Error, "Error writing YAML", "to", Logging::Loggable(tmpfilename));
×
204
    unlink(tmpfilename.c_str());
×
205
    throw runtime_error("YAML Conversion");
×
206
  }
×
UNCOV
207
  if (rename(path.c_str(), (path + ".converted").c_str()) != 0) {
×
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

UNCOV
214
  if (rename(tmpfilename.c_str(), yamlfilename.c_str()) != 0) {
×
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
  }
×
UNCOV
223
  log->info(Logr::Notice, "Converted to YAML", "file", Logging::Loggable(path), "to", Logging::Loggable(yamlfilename));
×
UNCOV
224
}
×
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)
UNCOV
264
{
×
UNCOV
265
  std::vector<std::string> forwAndAuthFiles{};
×
UNCOV
266
  ::arg().gatherIncludes(includeDir, "..conf", forwAndAuthFiles);
×
UNCOV
267
  pdns::rust::settings::rec::Recursorsettings settings{};
×
UNCOV
268
  for (const auto& file : forwAndAuthFiles) {
×
UNCOV
269
    auto yaml = pdns::settings::rec::oldStyleSettingsFileToYaml(file, false);
×
UNCOV
270
    pdns::rust::settings::rec::merge(settings, yaml);
×
UNCOV
271
  }
×
UNCOV
272
  const string yamlAPiZonesFile = apiDir + "/apizones";
×
273

UNCOV
274
  for (auto& zone : settings.recursor.auth_zones) {
×
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
  }
×
UNCOV
283
  api_add_forward_zones(yamlAPiZonesFile, settings.recursor.forward_zones);
×
UNCOV
284
  api_add_forward_zones(yamlAPiZonesFile, settings.recursor.forward_zones_recurse);
×
UNCOV
285
  for (const auto& file : forwAndAuthFiles) {
×
UNCOV
286
    if (rename(file.c_str(), (file + ".converted").c_str()) != 0) {
×
287
      int err = errno;
×
288
      log->error(Logr::Error, err, "Rename failed", "file", Logging::Loggable(file), "to", Logging::Loggable(file + ".converted"));
×
289
    }
×
UNCOV
290
  }
×
UNCOV
291
}
×
292

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

318
template <typename T>
319
static void addToAllowNotifyFor(Recursorsettings& settings, const rust::Vec<T>& vec)
UNCOV
320
{
×
UNCOV
321
  for (const auto& item : vec) {
×
UNCOV
322
    if (item.notify_allowed) {
×
UNCOV
323
      settings.incoming.allow_notify_for.emplace_back(item.zone);
×
UNCOV
324
    }
×
UNCOV
325
  }
×
UNCOV
326
}
×
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
{
184✔
330
  auto file = ifstream(configname);
184✔
331
  if (!file.is_open()) {
184!
332
    msg = stringerror(errno);
184✔
333
    return YamlSettingsStatus::CannotOpen;
184✔
334
  }
184✔
UNCOV
335
  SLOG(g_log << Logger::Notice << "Processing main YAML settings from " << configname << endl,
×
UNCOV
336
       log->info(Logr::Notice, "Processing main YAML settings", "path", Logging::Loggable(configname)));
×
UNCOV
337
  try {
×
UNCOV
338
    auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
×
UNCOV
339
    auto yamlstruct = pdns::rust::settings::rec::parse_yaml_string(data);
×
UNCOV
340
    std::vector<std::string> yamlFiles;
×
UNCOV
341
    ::arg().gatherIncludes(!includeDirOnCommandLine.empty() ? includeDirOnCommandLine : string(yamlstruct.recursor.include_dir),
×
UNCOV
342
                           ".yml", yamlFiles);
×
UNCOV
343
    for (const auto& yamlfile : yamlFiles) {
×
UNCOV
344
      mergeYamlSubFile(yamlfile, yamlstruct, false, log);
×
UNCOV
345
    }
×
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.
UNCOV
349
    addToAllowNotifyFor(yamlstruct, yamlstruct.recursor.forward_zones);
×
UNCOV
350
    addToAllowNotifyFor(yamlstruct, yamlstruct.recursor.forward_zones_recurse);
×
UNCOV
351
    addToAllowNotifyFor(yamlstruct, yamlstruct.recursor.forwarding_catalog_zones);
×
UNCOV
352
    yamlstruct.validate();
×
UNCOV
353
    settings = std::move(yamlstruct);
×
UNCOV
354
    return YamlSettingsStatus::OK;
×
UNCOV
355
  }
×
UNCOV
356
  catch (const ::rust::Error& ex) {
×
UNCOV
357
    msg = ex.what();
×
UNCOV
358
    return YamlSettingsStatus::PresentButFailed;
×
UNCOV
359
  }
×
UNCOV
360
  catch (const std::exception& ex) {
×
361
    msg = ex.what();
×
362
    return YamlSettingsStatus::PresentButFailed;
×
363
  }
×
UNCOV
364
  catch (...) {
×
365
    msg = "Unexpected exception processing YAML";
×
366
    return YamlSettingsStatus::PresentButFailed;
×
367
  }
×
UNCOV
368
}
×
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)
UNCOV
413
{
×
UNCOV
414
  std::ostringstream str;
×
UNCOV
415
  str << to_arg(authzone.zone) << '=' << to_arg(authzone.file);
×
UNCOV
416
  return str.str();
×
UNCOV
417
}
×
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)
UNCOV
434
{
×
UNCOV
435
  ::arg().set("forward-zones") = to_arg(settings.recursor.forward_zones);
×
UNCOV
436
  ::arg().set("forward-zones-file") = to_arg(settings.recursor.forward_zones_file);
×
UNCOV
437
  ::arg().set("forward-zones-recurse") = to_arg(settings.recursor.forward_zones_recurse);
×
UNCOV
438
  ::arg().set("auth-zones") = to_arg(settings.recursor.auth_zones);
×
UNCOV
439
  ::arg().set("allow-notify-for") = to_arg(settings.incoming.allow_notify_for);
×
UNCOV
440
  ::arg().set("allow-notify-for-file") = to_arg(settings.incoming.allow_notify_for_file);
×
UNCOV
441
  ::arg().set("export-etc-hosts") = to_arg(settings.recursor.export_etc_hosts);
×
UNCOV
442
  ::arg().set("serve-rfc1918") = to_arg(settings.recursor.serve_rfc1918);
×
UNCOV
443
}
×
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
{
4✔
494
  vector<string> zones;
4✔
495
  stringtok(zones, val, " ,\t\n\r");
4✔
496
  for (const auto& zone : zones) {
4!
UNCOV
497
    auto headers = splitField(zone, '=');
×
UNCOV
498
    boost::trim(headers.first);
×
UNCOV
499
    boost::trim(headers.second);
×
UNCOV
500
    ::rust::Vec<::rust::String> addresses;
×
UNCOV
501
    stringtok(addresses, headers.second, " ;");
×
UNCOV
502
    ForwardZone forwardzone{headers.first, std::move(addresses), recurse, false};
×
UNCOV
503
    field.push_back(std::move(forwardzone));
×
UNCOV
504
  }
×
505
}
4✔
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)
UNCOV
515
{
×
UNCOV
516
  string var;
×
UNCOV
517
  string val;
×
UNCOV
518
  string::size_type pos = 0;
×
UNCOV
519
  bool incremental = false;
×
520

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

UNCOV
550
  ::rust::String section;
×
UNCOV
551
  ::rust::String fieldname;
×
UNCOV
552
  ::rust::String type_name;
×
UNCOV
553
  pdns::rust::settings::rec::Value rustvalue = {false, 0, 0.0, "", {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}};
×
UNCOV
554
  if (pdns::settings::rec::oldKVToBridgeStruct(var, val, section, fieldname, type_name, rustvalue)) {
×
UNCOV
555
    auto overriding = !mainFile && !incremental && !simpleRustType(type_name);
×
UNCOV
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}});
×
UNCOV
557
    if (!inserted) {
×
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
    }
×
UNCOV
579
  }
×
UNCOV
580
}
×
581

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

UNCOV
587
  std::ifstream configFileStream(fname);
×
UNCOV
588
  if (!configFileStream) {
×
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 +=
UNCOV
595
  FieldMap map;
×
UNCOV
596
  while (getline(configFileStream, pline)) {
×
UNCOV
597
    boost::trim_right(pline);
×
598

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

UNCOV
604
    line += pline;
×
605

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

616
    // strip trailing spaces
UNCOV
617
    boost::trim_right(line);
×
618

619
    // strip leading spaces
UNCOV
620
    pos = line.find_first_not_of(" \t\r\n");
×
UNCOV
621
    if (pos != string::npos) {
×
UNCOV
622
      line = line.substr(pos);
×
UNCOV
623
    }
×
624

UNCOV
625
    processLine("--" + line, map, mainFile);
×
UNCOV
626
    line = "";
×
UNCOV
627
  }
×
628

629
  // Convert the map to a vector, as CXX does not have any dictionary like support.
UNCOV
630
  ::rust::Vec<pdns::rust::settings::rec::OldStyle> vec;
×
UNCOV
631
  vec.reserve(map.size());
×
UNCOV
632
  for (const auto& entry : map) {
×
UNCOV
633
    vec.emplace_back(entry.second);
×
UNCOV
634
  }
×
UNCOV
635
  return std::string(pdns::rust::settings::rec::map_to_yaml_string(vec));
×
UNCOV
636
}
×
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()) {
490✔
647
    if (const auto newname = ArgvMap::isDeprecated(var); !newname.empty()) {
490✔
648
      continue;
14✔
649
    }
14✔
650
    ::rust::String section;
476✔
651
    ::rust::String fieldname;
476✔
652
    ::rust::String type_name;
476✔
653
    pdns::rust::settings::rec::Value rustvalue{};
476✔
654
    string name = var;
476✔
655
    string val = arg().getDefault(var);
476✔
656
    if (pdns::settings::rec::oldKVToBridgeStruct(name, val, section, fieldname, type_name, rustvalue)) {
476✔
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}});
422✔
658
    }
422✔
659
  }
476✔
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) {
450✔
690
    vec.emplace_back(entry.second);
450✔
691
  }
450✔
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,898✔
707
    bool withHelp = false;
1,898✔
708
    bool sectionChange = false;
1,898✔
709
    std::smatch matches;
1,898✔
710
    std::regex_search(line, matches, sectionRE);
1,898✔
711
    if (!matches.empty()) {
1,898✔
712
      section = matches[1];
24✔
713
      sectionChange = true;
24✔
714
    }
24✔
715
    std::regex_search(line, matches, fieldRE);
1,898✔
716
    if (!matches.empty()) {
1,898✔
717
      field = matches[1];
450✔
718
      withHelp = true;
450✔
719
    }
450✔
720
    if (withHelp) {
1,898✔
721
      std::string oldname;
450✔
722
      if (auto iter = map.find(make_pair(section, field)); iter != map.end()) {
450!
723
        oldname = std::string(iter->second.old_name);
450✔
724
      }
450✔
725
      res += "##### ";
450✔
726
      auto help = arg().getHelp(oldname);
450✔
727
      if (help.empty()) {
450✔
728
        replace(oldname.begin(), oldname.end(), '_', '-');
16✔
729
        help = arg().getHelp(oldname);
16✔
730
      }
16✔
731
      res += help;
450✔
732
      res += '\n';
450✔
733
    }
450✔
734
    if (sectionChange) {
1,898✔
735
      res += "\n######### SECTION ";
24✔
736
      res += section;
24✔
737
      res += " #########\n";
24✔
738
      res += line;
24✔
739
    }
24✔
740
    else {
1,874✔
741
      res += "# ";
1,874✔
742
      res += line;
1,874✔
743
    }
1,874✔
744
    res += '\n';
1,898✔
745
  }
1,898✔
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
{
6✔
832
  if (!tsig.name.empty()) {
6!
833
    var.name = DNSName(std::string(tsig.name));
×
834
  }
×
835
  if (!tsig.algo.empty()) {
6!
836
    var.algo = DNSName(std::string(tsig.algo));
×
837
  }
×
838
  B64Decode(std::string(tsig.secret), var.secret);
6✔
839
}
6✔
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
{
8✔
996
  static const std::map<std::string, pdns::ZoneMD::Config> map = {
8✔
997
    {"ignore", pdns::ZoneMD::Config::Ignore},
8✔
998
    {"validate", pdns::ZoneMD::Config::Validate},
8✔
999
    {"require", pdns::ZoneMD::Config::Require},
8✔
1000
  };
8✔
1001
  if (auto iter = map.find(mode); iter != map.end()) {
8!
1002
    return iter->second;
8✔
1003
  }
8✔
1004
  throw runtime_error("ZoneMD config '" + mode + "' unknown");
×
1005
}
8✔
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
{
6✔
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) {
12✔
1109
    auto name = DNSName(std::string(trustAnchor.name));
12✔
1110
    luaConfig.dsAnchors.erase(name);
12✔
1111
  }
12✔
1112
  for (const auto& trustAnchor : dnssec.trustanchors) {
12✔
1113
    auto name = DNSName(std::string(trustAnchor.name));
12✔
1114
    for (const auto& dsRecord : trustAnchor.dsrecords) {
14✔
1115
      auto dsRecContent = std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(std::string(dsRecord)));
14✔
1116
      luaConfig.dsAnchors[name].emplace(*dsRecContent);
14✔
1117
    }
14✔
1118
  }
12✔
1119
  for (const auto& nta : dnssec.negative_trustanchors) {
6✔
1120
    luaConfig.negAnchors[DNSName(std::string(nta.name))] = std::string(nta.reason);
4✔
1121
  }
4✔
1122
  luaConfig.trustAnchorFileInfo.fname = std::string(dnssec.trustanchorfile);
6✔
1123
  luaConfig.trustAnchorFileInfo.interval = dnssec.trustanchorfile_interval;
6✔
1124
}
6✔
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
{
6✔
1195
  for (const auto& rpz : rpzs) {
6✔
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
}
6✔
1247

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

1276
void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::SortList>& sortlists, SortList& lua)
1277
{
6✔
1278
  for (const auto& sortlist : sortlists) {
6✔
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
}
6✔
1284

1285
void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::AllowedAdditionalQType>& alloweds, std::map<QType, std::pair<std::set<QType>, AdditionalMode>>& lua)
1286
{
6✔
1287
  for (const auto& allowed : alloweds) {
6✔
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
}
6✔
1298

1299
void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::ProxyMapping>& pmaps, ProxyMapping& proxyMapping)
1300
{
6✔
1301
  for (const auto& pmap : pmaps) {
6✔
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
}
6✔
1314

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

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

1341
void pdns::settings::rec::fromBridgeStructToLuaConfig(const pdns::rust::settings::rec::Recursorsettings& settings, LuaConfigItems& luaConfig, ProxyMapping& proxyMapping)
1342
{
6✔
1343
  fromRustToLuaConfig(settings.dnssec, luaConfig);
6✔
1344
  luaConfig.protobufMaskV4 = settings.logging.protobuf_mask_v4;
6✔
1345
  luaConfig.protobufMaskV6 = settings.logging.protobuf_mask_v6;
6✔
1346
  if (!settings.logging.protobuf_servers.empty()) {
6✔
1347
    fromRustToLuaConfig(settings.logging.protobuf_servers.at(0), luaConfig.protobufExportConfig);
2✔
1348
  }
2✔
1349
  if (!settings.logging.outgoing_protobuf_servers.empty()) {
6✔
1350
    fromRustToLuaConfig(settings.logging.outgoing_protobuf_servers.at(0), luaConfig.outgoingProtobufExportConfig);
2✔
1351
  }
2✔
1352
  if (!settings.logging.dnstap_framestream_servers.empty()) {
6✔
1353
    fromRustToLuaConfig(settings.logging.dnstap_framestream_servers.at(0), luaConfig.frameStreamExportConfig);
2✔
1354
  }
2✔
1355
  if (!settings.logging.dnstap_nod_framestream_servers.empty()) {
6✔
1356
    fromRustToLuaConfig(settings.logging.dnstap_nod_framestream_servers.at(0), luaConfig.nodFrameStreamExportConfig);
2✔
1357
  }
2✔
1358
  fromRustToLuaConfig(settings.recursor.rpzs, luaConfig);
6✔
1359
  fromRustToLuaConfig(settings.recursor.sortlists, luaConfig.sortlist);
6✔
1360
  fromRustToLuaConfig(settings.recordcache.zonetocaches, luaConfig.ztcConfigs);
6✔
1361
  fromRustToLuaConfig(settings.recursor.allowed_additional_qtypes, luaConfig.allowAdditionalQTypes);
6✔
1362
  fromRustToLuaConfig(settings.recursor.forwarding_catalog_zones, luaConfig.catalogzones);
6✔
1363
  fromRustToLuaConfig(settings.incoming.proxymappings, proxyMapping);
6✔
1364
}
6✔
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)
UNCOV
1368
{
×
UNCOV
1369
  bool alldefault = true;
×
UNCOV
1370
  for (const auto& trustanchor : settings.dnssec.trustanchors) {
×
UNCOV
1371
    if (trustanchor.name == ".") {
×
UNCOV
1372
      if (trustanchor.dsrecords.size() != rootDSs.size()) {
×
UNCOV
1373
        alldefault = false;
×
UNCOV
1374
        break;
×
UNCOV
1375
      }
×
UNCOV
1376
      for (const auto& dsRecord : trustanchor.dsrecords) {
×
UNCOV
1377
        if (std::find(rootDSs.begin(), rootDSs.end(), std::string(dsRecord)) == rootDSs.end()) {
×
1378
          alldefault = false;
×
1379
          break;
×
1380
        }
×
UNCOV
1381
      }
×
UNCOV
1382
    }
×
1383
    else {
×
1384
      alldefault = false;
×
1385
      break;
×
1386
    }
×
UNCOV
1387
  }
×
UNCOV
1388
  alldefault = alldefault && settings.dnssec.negative_trustanchors.empty();
×
UNCOV
1389
  alldefault = alldefault && settings.dnssec.trustanchorfile.empty();
×
UNCOV
1390
  alldefault = alldefault && settings.dnssec.trustanchorfile_interval == 24;
×
UNCOV
1391
  alldefault = alldefault && settings.logging.protobuf_mask_v4 == 32;
×
UNCOV
1392
  alldefault = alldefault && settings.logging.protobuf_mask_v6 == 128;
×
UNCOV
1393
  alldefault = alldefault && settings.logging.protobuf_servers.empty();
×
UNCOV
1394
  alldefault = alldefault && settings.logging.outgoing_protobuf_servers.empty();
×
UNCOV
1395
  alldefault = alldefault && settings.logging.dnstap_framestream_servers.empty();
×
UNCOV
1396
  alldefault = alldefault && settings.logging.dnstap_nod_framestream_servers.empty();
×
UNCOV
1397
  alldefault = alldefault && settings.recursor.sortlists.empty();
×
UNCOV
1398
  alldefault = alldefault && settings.recursor.rpzs.empty();
×
UNCOV
1399
  alldefault = alldefault && settings.recursor.forwarding_catalog_zones.empty();
×
UNCOV
1400
  alldefault = alldefault && settings.recordcache.zonetocaches.empty();
×
UNCOV
1401
  alldefault = alldefault && settings.recursor.allowed_additional_qtypes.empty();
×
UNCOV
1402
  alldefault = alldefault && settings.incoming.proxymappings.empty();
×
UNCOV
1403
  return !alldefault;
×
UNCOV
1404
}
×
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
{
34✔
1408
  string msg;
34✔
1409
  // TODO: handle include-dir on command line
1410
  auto yamlstatus = pdns::settings::rec::readYamlSettings(yamlconfigname, ::arg()["include-dir"], settings, msg, startupLog);
34✔
1411

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

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

UNCOV
1423
  case pdns::settings::rec::YamlSettingsStatus::OK:
×
UNCOV
1424
    yamlSettings = true;
×
UNCOV
1425
    SLOG(g_log << Logger::Notice << "YAML config found and processed for configname '" << yamlconfigname << "'" << endl,
×
UNCOV
1426
         startupLog->info(Logr::Notice, "YAML config found and processed", "configname", Logging::Loggable(yamlconfigname)));
×
UNCOV
1427
    pdns::settings::rec::processAPIDir(arg()["include-dir"], settings, startupLog);
×
UNCOV
1428
    luaSettingsInYAML = pdns::settings::rec::luaItemSet(settings);
×
UNCOV
1429
    if (luaSettingsInYAML && !settings.recursor.lua_config_file.empty()) {
×
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
    }
×
UNCOV
1435
    else if (!settings.recursor.forwarding_catalog_zones.empty() && settings.webservice.api_dir.empty()) {
×
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
    }
×
UNCOV
1439
    else if (setGlobals) {
×
UNCOV
1440
      pdns::settings::rec::bridgeStructToOldStyleSettings(settings);
×
UNCOV
1441
    }
×
UNCOV
1442
    break;
×
1443
  }
34✔
1444
  return yamlstatus;
34✔
1445
}
34✔
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))
UNCOV
1453
{
×
UNCOV
1454
}
×
1455

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

1459
template <typename M>
1460
[[nodiscard]] const M& Wrapper<M>::get() const
UNCOV
1461
{
×
UNCOV
1462
  return *d_ptr;
×
UNCOV
1463
}
×
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
{
2✔
1476
  try {
2✔
1477
    auto name = DNSName(string(str));
2✔
1478
    return name.isHostname();
2✔
1479
  }
2✔
1480
  catch (...) {
2✔
1481
    return false;
×
1482
  }
×
1483
}
2✔
1484

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

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

1495
void log(const std::shared_ptr<Logger>& logger, pdns::rust::misc::Priority log_level, ::rust::Str msg, const ::rust::Vec<KeyValue>& values)
UNCOV
1496
{
×
UNCOV
1497
  auto log = logger;
×
UNCOV
1498
  for (const auto& [key, value] : values) {
×
UNCOV
1499
    log = log->withValues(std::string(key), Logging::Loggable(std::string(value)));
×
UNCOV
1500
  }
×
UNCOV
1501
  log->info(static_cast<Logr::Priority>(log_level), std::string(msg));
×
UNCOV
1502
}
×
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)
UNCOV
1505
{
×
UNCOV
1506
  auto log = logger;
×
UNCOV
1507
  for (const auto& [key, value] : values) {
×
1508
    log = log->withValues(std::string(key), Logging::Loggable(std::string(value)));
×
1509
  }
×
UNCOV
1510
  log->error(static_cast<Logr::Priority>(log_level), std::string(error), std::string(msg));
×
UNCOV
1511
}
×
1512

1513
std::shared_ptr<Logger> withValue(const std::shared_ptr<Logger>& logger, ::rust::Str key, ::rust::Str val)
UNCOV
1514
{
×
UNCOV
1515
  auto ret = logger->withValues(std::string(key), Logging::Loggable(std::string(val)));
×
UNCOV
1516
  return ret;
×
UNCOV
1517
}
×
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