]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursordist/settings/cxxsupport.cc
Make a isValidHostname() callable from Rust that calls into DNSName::is_hostname()
[thirdparty/pdns.git] / pdns / recursordist / settings / cxxsupport.cc
CommitLineData
9883d3f9
OM
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>
93522cd2
OM
25#include <unistd.h>
26#include <cstdio>
27#include <libgen.h>
9883d3f9
OM
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"
bee74d90 36#include "rec-lua-conf.hh"
bab7bbd2
OM
37#include "root-dnssec.hh"
38#include "dnsrecords.hh"
39#include "base64.hh"
69da77c2 40#include "validate-recursor.hh"
9883d3f9
OM
41
42::rust::Vec<::rust::String> pdns::settings::rec::getStrings(const std::string& name)
43{
44 ::rust::Vec<::rust::String> vec;
45 to_yaml(vec, arg()[name]);
46 return vec;
47}
48
49::rust::Vec<ForwardZone> pdns::settings::rec::getForwardZones(const string& name)
50{
51 ::rust::Vec<ForwardZone> vec;
52 const auto recurse = name == "forward-zones-recurse";
53 to_yaml(vec, arg()[name], recurse);
54 return vec;
55}
56
57::rust::Vec<AuthZone> pdns::settings::rec::getAuthZones(const string& name)
58{
59 ::rust::Vec<AuthZone> vec;
60 to_yaml(vec, arg()[name]);
61 return vec;
62}
63
64void pdns::settings::rec::oldStyleForwardsFileToBridgeStruct(const std::string& file, ::rust::Vec<ForwardZone>& vec)
65{
01807c64 66 auto filePtr = pdns::UniqueFilePtr(fopen(file.c_str(), "r"));
9883d3f9
OM
67 if (!filePtr) {
68 throw PDNSException("Error opening forward-zones-file '" + file + "': " + stringerror());
69 }
70
71 string line;
72 int linenum = 0;
73 while (linenum++, stringfgets(filePtr.get(), line)) {
74 boost::trim(line);
75 if (line.length() == 0 || line.at(0) == '#') { // Comment line, skip to the next line
76 continue;
77 }
78 auto [domain, instructions] = splitField(line, '=');
79 instructions = splitField(instructions, '#').first; // Remove EOL comments
80 boost::trim(domain);
81 boost::trim(instructions);
82 if (domain.empty() || instructions.empty()) {
83 throw PDNSException("Error parsing line " + std::to_string(linenum) + " of " + file);
84 }
85 bool allowNotify = false;
86 bool recurse = false;
87 for (; !domain.empty(); domain.erase(0, 1)) {
88 switch (domain[0]) {
89 case '+':
90 recurse = true;
91 continue;
92 case '^':
93 allowNotify = true;
94 continue;
95 }
96 break;
97 }
98 if (domain.empty()) {
99 throw PDNSException("Error parsing line " + std::to_string(linenum) + " of " + file);
100 }
101 ::rust::Vec<::rust::String> addresses;
102 stringtok(addresses, instructions, ",; ");
17417d54 103 ForwardZone forwardzone{domain, std::move(addresses), recurse, allowNotify};
9883d3f9
OM
104 vec.push_back(std::move(forwardzone));
105 }
106}
107
108void pdns::settings::rec::oldStyleAllowFileToBridgeStruct(const std::string& file, ::rust::Vec<::rust::String>& vec)
109{
110 string line;
111 ifstream ifs(file);
112 if (!ifs) {
113 int err = errno;
114 throw runtime_error("Could not open '" + file + "': " + stringerror(err));
115 }
116
117 while (getline(ifs, line)) {
118 auto pos = line.find('#');
119 if (pos != string::npos) {
120 line.resize(pos);
121 }
122 boost::trim(line);
123 if (line.empty()) {
124 continue;
125 }
126 vec.emplace_back(line);
127 }
128}
129
d4081787 130static void mergeYamlSubFile(const std::string& configname, Recursorsettings& settings, bool allowabsent, Logr::log_t log)
9883d3f9 131{
9883d3f9
OM
132 auto file = ifstream(configname);
133 if (!file.is_open()) {
d4081787
OM
134 if (allowabsent) {
135 return;
136 }
9883d3f9
OM
137 throw runtime_error("Cannot open " + configname);
138 }
3056b382
OM
139 SLOG(g_log << Logger::Notice << "Processing YAML settings from " << configname << endl,
140 log->info(Logr::Notice, "Processing YAML settings", "path", Logging::Loggable(configname)));
9883d3f9
OM
141 auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
142 pdns::rust::settings::rec::merge(settings, data);
143}
144
d6ee98b0 145static void possiblyConvertACLFile(const string& includeDir, const string& apiDir, const std::string& filename, Logr::log_t log)
93522cd2
OM
146{
147 auto path = includeDir;
148 path.append("/").append(filename).append(".conf");
149 auto file = ifstream(path);
150 if (!file.is_open()) {
d6ee98b0 151 // Not an error, file just is not there
93522cd2
OM
152 return;
153 }
154 rust::vec<rust::string> result;
155 std::string line;
156 while (getline(file, line)) {
157 auto pos = line.find('#');
158 if (pos != string::npos) {
159 line.resize(pos);
160 }
161 boost::trim(line);
162 if (line.empty()) {
163 continue;
164 }
165 auto plusis = line.find("+=");
166 if (plusis != string::npos) {
167 auto val = line.substr(plusis + 2);
168 boost::trim(val);
169 result.emplace_back(val);
170 }
171 }
07bee03a
OM
172
173 rust::string key = "allow_from";
174 rust::string filekey = "allow_from_file";
d4081787 175 if (filename == "allow-notify-from") {
07bee03a
OM
176 key = "allow_notify_from";
177 filekey = "allow_notify_from_file";
93522cd2 178 }
07bee03a
OM
179 const auto yaml = pdns::rust::settings::rec::allow_from_to_yaml_string_incoming(key, filekey, result);
180
aa171aa0 181 string yamlfilename = apiDir;
93522cd2 182 yamlfilename.append("/").append(filename).append(".yml");
d6ee98b0
OM
183 string tmpfilename = yamlfilename + ".tmp";
184 ofstream ofconf(tmpfilename);
93522cd2 185 if (!ofconf) {
d6ee98b0
OM
186 int err = errno;
187 log->error(Logr::Error, err, "Cannot open for file for writing YAML", "to", Logging::Loggable(tmpfilename));
188 throw runtime_error("YAML Conversion");
93522cd2
OM
189 }
190 ofconf << "# Generated by pdns-recursor REST API, DO NOT EDIT" << endl;
191 ofconf << yaml << endl;
192 ofconf.close();
a778d82b
OM
193 if (ofconf.bad()) {
194 log->error(Logr::Error, "Error writing YAML", "to", Logging::Loggable(tmpfilename));
195 unlink(tmpfilename.c_str());
196 throw runtime_error("YAML Conversion");
197 }
93522cd2 198 if (rename(path.c_str(), (path + ".converted").c_str()) != 0) {
d6ee98b0
OM
199 int err = errno;
200 log->error(Logr::Error, err, "Rename failed", "file", Logging::Loggable(path), "to", Logging::Loggable(path + ".converted"));
201 unlink(tmpfilename.c_str());
202 throw runtime_error("YAML Conversion");
203 }
204
205 if (rename(tmpfilename.c_str(), yamlfilename.c_str()) != 0) {
206 int err = errno;
207 log->error(Logr::Error, err, "Rename failed", "file", Logging::Loggable(tmpfilename), "to", Logging::Loggable(yamlfilename));
d70c017c
RG
208 if (rename((path + ".converted").c_str(), path.c_str()) != 0) {
209 err = errno;
210 log->error(Logr::Error, err, "Rename failed", "file", Logging::Loggable(path + ".converted"), "to", Logging::Loggable(path));
211 }
d6ee98b0 212 throw runtime_error("YAML Conversion");
93522cd2 213 }
d6ee98b0 214 log->info(Logr::Notice, "Converted to YAML", "file", Logging::Loggable(path), "to", Logging::Loggable(yamlfilename));
93522cd2
OM
215}
216
d6ee98b0 217static void fileCopy(const string& src, const string& dst, Logr::log_t log)
93522cd2 218{
d6ee98b0
OM
219 ifstream ifconf(src);
220 if (!ifconf) {
221 log->info(Logr::Error, "Cannot open for file for reading", "file", Logging::Loggable(src));
222 throw runtime_error("YAML Conversion");
223 }
224 ofstream ofconf(dst);
225 if (!ofconf) {
226 log->info(Logr::Error, "Cannot open for file for writing YAML", "to", Logging::Loggable(dst));
227 throw runtime_error("YAML Conversion");
228 }
229 for (;;) {
230 auto character = ifconf.get();
231 if (ifconf.eof()) {
232 break;
233 }
234 if (ifconf.bad()) {
235 int err = errno;
236 log->error(Logr::Error, err, "Error reading", "to", Logging::Loggable(src));
237 throw runtime_error("YAML Conversion");
238 }
07bee03a 239 ofconf.put(static_cast<char>(character));
d6ee98b0
OM
240 if (ofconf.bad()) {
241 int err = errno;
242 log->error(Logr::Error, err, "Error writing YAML", "to", Logging::Loggable(dst));
243 throw runtime_error("YAML Conversion");
244 }
245 }
d6ee98b0 246 ifconf.close();
a778d82b
OM
247 ofconf.close();
248 if (ofconf.bad()) {
249 log->error(Logr::Error, "Error writing YAML", "to", Logging::Loggable(dst));
250 throw runtime_error("YAML Conversion");
251 }
d6ee98b0 252}
a778d82b 253
d6ee98b0
OM
254static void possiblyConvertForwardsandAuths(const std::string& includeDir, const std::string& apiDir, Logr::log_t log)
255{
07bee03a 256 std::vector<std::string> forwAndAuthFiles{};
d6ee98b0 257 ::arg().gatherIncludes(includeDir, "..conf", forwAndAuthFiles);
07bee03a 258 pdns::rust::settings::rec::Recursorsettings settings{};
d6ee98b0 259 for (const auto& file : forwAndAuthFiles) {
93522cd2 260 auto yaml = pdns::settings::rec::oldStyleSettingsFileToYaml(file, false);
93522cd2
OM
261 pdns::rust::settings::rec::merge(settings, yaml);
262 }
263 const string yamlAPiZonesFile = apiDir + "/apizones";
264
265 for (auto& zone : settings.recursor.auth_zones) {
266 const std::string origName(zone.file);
267 std::string newName(zone.file);
268 newName.replace(0, includeDir.length(), apiDir);
d6ee98b0
OM
269 log->info(Logr::Notice, "Copying auth zone file", "file", Logging::Loggable(origName), "to", Logging::Loggable(newName));
270 fileCopy(origName, newName, log);
93522cd2
OM
271 zone.file = ::rust::String(newName);
272 api_add_auth_zone(yamlAPiZonesFile, zone);
273 }
d6ee98b0
OM
274 api_add_forward_zones(yamlAPiZonesFile, settings.recursor.forward_zones);
275 api_add_forward_zones(yamlAPiZonesFile, settings.recursor.forward_zones_recurse);
276 for (const auto& file : forwAndAuthFiles) {
277 if (rename(file.c_str(), (file + ".converted").c_str()) != 0) {
278 int err = errno;
279 log->error(Logr::Error, err, "Rename failed", "file", Logging::Loggable(file), "to", Logging::Loggable(file + ".converted"));
280 }
93522cd2
OM
281 }
282}
283
284void pdns::settings::rec::processAPIDir(const string& includeDirOnCommandLine, pdns::rust::settings::rec::Recursorsettings& settings, Logr::log_t log)
285{
286 auto apiDir = std::string(settings.webservice.api_dir);
287 if (apiDir.empty()) {
288 return;
289 }
290 auto includeDir = std::string(settings.recursor.include_dir);
291 if (!includeDirOnCommandLine.empty()) {
292 includeDir = includeDirOnCommandLine;
293 }
294 if (includeDir == apiDir) {
295 throw runtime_error("Active YAML settings do not allow include_dir to be equal to api_dir");
296 }
297 const std::array<std::string, 2> aclFiles = {
298 "allow-from",
aa171aa0 299 "allow-notify-from"};
93522cd2 300 for (const auto& file : aclFiles) {
d6ee98b0 301 possiblyConvertACLFile(includeDir, apiDir, file, log);
93522cd2
OM
302 auto path = apiDir;
303 path.append("/").append(file).append(".yml");
d4081787 304 mergeYamlSubFile(path, settings, true, log);
93522cd2 305 }
d6ee98b0 306 possiblyConvertForwardsandAuths(includeDir, apiDir, log);
93522cd2
OM
307}
308
9883d3f9
OM
309pdns::settings::rec::YamlSettingsStatus pdns::settings::rec::readYamlSettings(const std::string& configname, const std::string& includeDirOnCommandLine, Recursorsettings& settings, std::string& msg, Logr::log_t log)
310{
311 auto file = ifstream(configname);
312 if (!file.is_open()) {
313 msg = stringerror(errno);
314 return YamlSettingsStatus::CannotOpen;
315 }
316 SLOG(g_log << Logger::Notice << "Processing main YAML settings from " << configname << endl,
317 log->info(Logr::Notice, "Processing main YAML settings", "path", Logging::Loggable(configname)));
318 try {
319 auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
320 auto yamlstruct = pdns::rust::settings::rec::parse_yaml_string(data);
321 std::vector<std::string> yamlFiles;
322 ::arg().gatherIncludes(!includeDirOnCommandLine.empty() ? includeDirOnCommandLine : string(yamlstruct.recursor.include_dir),
323 ".yml", yamlFiles);
324 for (const auto& yamlfile : yamlFiles) {
d4081787 325 mergeYamlSubFile(yamlfile, yamlstruct, false, log);
9883d3f9
OM
326 }
327 yamlstruct.validate();
17417d54 328 settings = std::move(yamlstruct);
9883d3f9
OM
329 return YamlSettingsStatus::OK;
330 }
331 catch (const ::rust::Error& ex) {
332 msg = ex.what();
333 return YamlSettingsStatus::PresentButFailed;
334 }
335 catch (const std::exception& ex) {
336 msg = ex.what();
337 return YamlSettingsStatus::PresentButFailed;
338 }
339 catch (...) {
340 msg = "Unexpected exception processing YAML";
341 return YamlSettingsStatus::PresentButFailed;
342 }
343}
344
345void pdns::settings::rec::readYamlAllowFromFile(const std::string& filename, ::rust::Vec<::rust::String>& vec, Logr::log_t log)
346{
347 SLOG(g_log << Logger::Notice << "Processing allow YAML settings from " << filename << endl,
348 log->info(Logr::Notice, "Processing allow YAML settings", "path", Logging::Loggable(filename)));
349 auto file = ifstream(filename);
350 if (!file.is_open()) {
351 throw runtime_error(stringerror(errno));
352 }
353 auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
354 auto yamlvec = pdns::rust::settings::rec::parse_yaml_string_to_allow_from(data);
355 pdns::rust::settings::rec::validate_allow_from(filename, yamlvec);
17417d54 356 vec = std::move(yamlvec);
9883d3f9
OM
357}
358
359void pdns::settings::rec::readYamlForwardZonesFile(const std::string& filename, ::rust::Vec<pdns::rust::settings::rec::ForwardZone>& vec, Logr::log_t log)
360{
361 SLOG(g_log << Logger::Notice << "Processing forwarding YAML settings from " << filename << endl,
362 log->info(Logr::Notice, "Processing forwarding YAML settings", "path", Logging::Loggable(filename)));
363 auto file = ifstream(filename);
364 if (!file.is_open()) {
365 throw runtime_error(stringerror(errno));
366 }
367 auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
368 auto yamlvec = pdns::rust::settings::rec::parse_yaml_string_to_forward_zones(data);
369 pdns::rust::settings::rec::validate_forward_zones("forward_zones", yamlvec);
17417d54 370 vec = std::move(yamlvec);
9883d3f9
OM
371}
372
373void pdns::settings::rec::readYamlAllowNotifyForFile(const std::string& filename, ::rust::Vec<::rust::String>& vec, Logr::log_t log)
374{
375 SLOG(g_log << Logger::Notice << "Processing allow-notify-for YAML settings from " << filename << endl,
376 log->info(Logr::Notice, "Processing allow-notify-for YAML settings", "path", Logging::Loggable(filename)));
377 auto file = ifstream(filename);
378 if (!file.is_open()) {
379 throw runtime_error(stringerror(errno));
380 }
381 auto data = string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
382 auto yamlvec = pdns::rust::settings::rec::parse_yaml_string_to_allow_notify_for(data);
383 pdns::rust::settings::rec::validate_allow_notify_for("allow-notify-for", yamlvec);
17417d54 384 vec = std::move(yamlvec);
9883d3f9
OM
385}
386
387std::string pdns::settings::rec::to_arg(const AuthZone& authzone)
388{
389 std::ostringstream str;
390 str << to_arg(authzone.zone) << '=' << to_arg(authzone.file);
391 return str.str();
392}
393
394std::string pdns::settings::rec::to_arg(const ForwardZone& forwardzone)
395{
396 std::ostringstream str;
397 str << to_arg(forwardzone.zone) << '=';
398 const auto& vec = forwardzone.forwarders;
399 for (auto iter = vec.begin(); iter != vec.end(); ++iter) {
400 if (iter != vec.begin()) {
401 str << ';';
402 }
403 str << to_arg(*iter);
404 }
405 return str.str();
406}
407
408void pdns::settings::rec::setArgsForZoneRelatedSettings(Recursorsettings& settings)
409{
410 ::arg().set("forward-zones") = to_arg(settings.recursor.forward_zones);
411 ::arg().set("forward-zones-file") = to_arg(settings.recursor.forward_zones_file);
412 ::arg().set("forward-zones-recurse") = to_arg(settings.recursor.forward_zones_recurse);
413 ::arg().set("auth-zones") = to_arg(settings.recursor.auth_zones);
414 ::arg().set("allow-notify-for") = to_arg(settings.incoming.allow_notify_for);
415 ::arg().set("allow-notify-for-file") = to_arg(settings.incoming.allow_notify_for_file);
416 ::arg().set("export-etc-hosts") = to_arg(settings.recursor.export_etc_hosts);
417 ::arg().set("serve-rfc1918") = to_arg(settings.recursor.serve_rfc1918);
418}
419
420void pdns::settings::rec::setArgsForACLRelatedSettings(Recursorsettings& settings)
421{
422 ::arg().set("allow-from") = to_arg(settings.incoming.allow_from);
423 ::arg().set("allow-from-file") = to_arg(settings.incoming.allow_from_file);
424 ::arg().set("allow-notify-from") = to_arg(settings.incoming.allow_notify_from);
425 ::arg().set("allow-notify-from-file") = to_arg(settings.incoming.allow_notify_from_file);
426}
427
428void pdns::settings::rec::to_yaml(uint64_t& field, const std::string& val)
429{
430 if (val.empty()) {
431 field = 0;
432 return;
433 }
434
435 checked_stoi_into(field, val, nullptr, 0);
436}
437
438void pdns::settings::rec::to_yaml(double& field, const std::string& val)
439{
440 if (val.empty()) {
441 field = 0.0;
442 return;
443 }
444
445 const auto* cptr_orig = val.c_str();
446 char* cptr_ret = nullptr;
447 auto retval = strtod(cptr_orig, &cptr_ret);
448
449 field = retval;
450}
451
452void pdns::settings::rec::to_yaml(::rust::Vec<AuthZone>& field, const std::string& val)
453{
454 vector<string> zones;
455 stringtok(zones, val, " ,\t\n\r");
456 for (const auto& zone : zones) {
457 auto headers = splitField(zone, '=');
458 boost::trim(headers.first);
459 boost::trim(headers.second);
460 AuthZone authzone{headers.first, headers.second};
461 field.push_back(std::move(authzone));
462 }
463}
464
465void pdns::settings::rec::to_yaml(::rust::Vec<ForwardZone>& field, const std::string& val, bool recurse)
466{
467 vector<string> zones;
468 stringtok(zones, val, " ,\t\n\r");
469 for (const auto& zone : zones) {
470 auto headers = splitField(zone, '=');
471 boost::trim(headers.first);
472 boost::trim(headers.second);
473 ::rust::Vec<::rust::String> addresses;
474 stringtok(addresses, headers.second, " ;");
17417d54 475 ForwardZone forwardzone{headers.first, std::move(addresses), recurse, false};
9883d3f9
OM
476 field.push_back(std::move(forwardzone));
477 }
478}
479
480using FieldMap = std::map<pair<::rust::String, ::rust::String>, pdns::rust::settings::rec::OldStyle>;
481
482static bool simpleRustType(const ::rust::String& rname)
483{
484 return rname == "bool" || rname == "u64" || rname == "f64" || rname == "String";
485}
486
487static void processLine(const std::string& arg, FieldMap& map, bool mainFile)
488{
489 string var;
490 string val;
491 string::size_type pos = 0;
492 bool incremental = false;
493
494 if (arg.find("--") == 0 && (pos = arg.find("+=")) != string::npos) // this is a --port+=25 case
495 {
496 var = arg.substr(2, pos - 2);
497 val = arg.substr(pos + 2);
498 incremental = true;
499 }
500 else if (arg.find("--") == 0 && (pos = arg.find('=')) != string::npos) // this is a --port=25 case
501 {
502 var = arg.substr(2, pos - 2);
503 val = arg.substr(pos + 1);
504 }
505 else if (arg.find("--") == 0 && (arg.find('=') == string::npos)) // this is a --daemon case
506 {
507 var = arg.substr(2);
508 val = "";
509 }
510 else if (arg[0] == '-' && arg.length() > 1) {
511 var = arg.substr(1);
512 val = "";
513 }
514 boost::trim(var);
515 if (var.empty()) {
516 return;
517 }
518 pos = val.find_first_not_of(" \t"); // strip leading whitespace
519 if (pos != 0 && pos != string::npos) {
520 val = val.substr(pos);
521 }
522
523 ::rust::String section;
524 ::rust::String fieldname;
525 ::rust::String type_name;
bee74d90 526 pdns::rust::settings::rec::Value rustvalue = {false, 0, 0.0, "", {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}};
9883d3f9
OM
527 if (pdns::settings::rec::oldKVToBridgeStruct(var, val, section, fieldname, type_name, rustvalue)) {
528 auto overriding = !mainFile && !incremental && !simpleRustType(type_name);
17417d54 529 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}});
9883d3f9
OM
530 if (!inserted) {
531 // Simple values overwrite always
532 existing->second.value.bool_val = rustvalue.bool_val;
533 existing->second.value.u64_val = rustvalue.u64_val;
534 existing->second.value.f64_val = rustvalue.f64_val;
535 existing->second.value.string_val = rustvalue.string_val;
536 // List values only if = was used
537 if (!incremental) {
538 existing->second.value.vec_string_val.clear();
539 existing->second.value.vec_forwardzone_val.clear();
540 existing->second.value.vec_authzone_val.clear();
541 }
542 for (const auto& add : rustvalue.vec_string_val) {
543 existing->second.value.vec_string_val.emplace_back(add);
544 }
545 for (const auto& add : rustvalue.vec_forwardzone_val) {
546 existing->second.value.vec_forwardzone_val.emplace_back(add);
547 }
548 for (const auto& add : rustvalue.vec_authzone_val) {
549 existing->second.value.vec_authzone_val.emplace_back(add);
550 }
551 }
552 }
553}
554
555std::string pdns::settings::rec::oldStyleSettingsFileToYaml(const string& fname, bool mainFile)
556{
557 string line;
558 string pline;
559
560 std::ifstream configFileStream(fname);
561 if (!configFileStream) {
562 int err = errno;
563 throw runtime_error("Cannot read " + fname + ": " + stringerror(err));
564 }
565
566 // Read the old-style config file and produce a map with the data, taking into account the
567 // difference between = and +=
568 FieldMap map;
569 while (getline(configFileStream, pline)) {
570 boost::trim_right(pline);
571
572 if (!pline.empty() && pline[pline.size() - 1] == '\\') {
573 line += pline.substr(0, pline.length() - 1);
574 continue;
575 }
576
577 line += pline;
578
579 // strip everything after a #
580 string::size_type pos = line.find('#');
581 if (pos != string::npos) {
582 // make sure it's either first char or has whitespace before
583 // fixes issue #354
584 if (pos == 0 || (std::isspace(line[pos - 1]) != 0)) {
585 line = line.substr(0, pos);
586 }
587 }
588
589 // strip trailing spaces
590 boost::trim_right(line);
591
592 // strip leading spaces
593 pos = line.find_first_not_of(" \t\r\n");
594 if (pos != string::npos) {
595 line = line.substr(pos);
596 }
597
598 processLine("--" + line, map, mainFile);
599 line = "";
600 }
601
602 // Convert the map to a vector, as CXX does not have any dictionary like support.
603 ::rust::Vec<pdns::rust::settings::rec::OldStyle> vec;
604 vec.reserve(map.size());
605 for (const auto& entry : map) {
606 vec.emplace_back(entry.second);
607 }
608 return std::string(pdns::rust::settings::rec::map_to_yaml_string(vec));
609}
610
611std::string pdns::settings::rec::defaultsToYaml()
612{
613 // In this function we make use of the fact that we know a little about the formatting of YAML by
614 // serde_yaml. ATM there's no way around that, as serde_yaml itself does not have any support for
615 // comments (other than stripping them while parsing). So produced YAML cannot have any comments.
616
617 // First we construct a map of (section, name) to info about the field (oldname, type, value etc)
618 FieldMap map;
619 for (const auto& var : arg().list()) {
620 if (const auto newname = ArgvMap::isDeprecated(var); !newname.empty()) {
621 continue;
622 }
623 ::rust::String section;
624 ::rust::String fieldname;
625 ::rust::String type_name;
e2000873 626 pdns::rust::settings::rec::Value rustvalue{};
9883d3f9
OM
627 string name = var;
628 string val = arg().getDefault(var);
629 if (pdns::settings::rec::oldKVToBridgeStruct(name, val, section, fieldname, type_name, rustvalue)) {
17417d54 630 map.emplace(std::pair{std::pair{section, fieldname}, pdns::rust::settings::rec::OldStyle{section, fieldname, name, std::move(type_name), std::move(rustvalue), false}});
9883d3f9
OM
631 }
632 }
bee74d90
OM
633
634 // Should be generated
635 auto def = [&](const string& section, const string& name, const string& type) {
e2000873
OM
636 pdns::rust::settings::rec::Value rustvalue{};
637 rustvalue.u64_val = 24; // XXX
bee74d90
OM
638 map.emplace(std::pair{std::pair{section, name}, pdns::rust::settings::rec::OldStyle{section, name, name, type, rustvalue, false}});
639 };
640 def("dnssec", "trustanchors", "Vec<TrustAnchor>");
641 def("dnssec", "negative_trustanchors", "Vec<NegativeTrustAnchor>");
642 def("dnssec", "trustanchorfile", "String");
643 def("dnssec", "trustanchorfile_interval", "u64");
644 def("logging", "protobuf_servers", "Vec<ProtobufServer>");
645 def("logging", "outgoing_protobuf_servers", "Vec<ProtobufServer>");
646 def("logging", "dnstap_framestream_servers", "Vec<DNSTapFrameStreamServer>");
647 def("logging", "dnstap_nod_framestream_servers", "Vec<DNSTapNODFrameStreamServer>");
648 def("recursor", "rpzs", "Vec<RPZ>");
649 def("recursor", "sortlists", "Vec<SortList>");
650 def("recordcache", "zonetocaches", "Vec<ZoneToCache>");
651 def("recursor", "allowed_additional_qtypes", "Vec<AllowedAdditionalQType>");
652 def("incoming", "proxymappings", "Vec<ProxyMapping>");
653 // End of should be generated XXX
654
9883d3f9
OM
655 // Convert the map to a vector, as CXX does not have any dictionary like support.
656 ::rust::Vec<pdns::rust::settings::rec::OldStyle> vec;
657 vec.reserve(map.size());
658 for (const auto& entry : map) {
659 vec.emplace_back(entry.second);
660 }
661 const auto defs = std::string(pdns::rust::settings::rec::map_to_yaml_string(vec));
662
663 // We now have a YAML string, with all sections and all default values. Do a litle bit of parsing
664 // to insert the help text lines.
665 std::vector<std::string> lines;
666 stringtok(lines, defs, "\n");
667 std::string res;
668
669 // These two RE's know about the formatting generated by serde_yaml
670 std::regex sectionRE("^(\\w+):");
671 std::regex fieldRE("^ (\\w+):");
672 std::string section;
673 std::string field;
674
675 for (const auto& line : lines) {
676 bool withHelp = false;
677 bool sectionChange = false;
678 std::smatch matches;
679 std::regex_search(line, matches, sectionRE);
680 if (!matches.empty()) {
681 section = matches[1];
682 sectionChange = true;
683 }
684 std::regex_search(line, matches, fieldRE);
685 if (!matches.empty()) {
686 field = matches[1];
687 withHelp = true;
688 }
689 if (withHelp) {
4e259dad
OM
690 std::string oldname;
691 if (auto iter = map.find(make_pair(section, field)); iter != map.end()) {
692 oldname = std::string(iter->second.old_name);
693 }
9883d3f9
OM
694 res += "##### ";
695 res += arg().getHelp(oldname);
696 res += '\n';
697 }
698 if (sectionChange) {
699 res += "\n######### SECTION ";
700 res += section;
701 res += " #########\n";
702 res += line;
703 }
704 else {
705 res += "# ";
706 res += line;
707 }
708 res += '\n';
709 }
710 return res;
711}
bee74d90
OM
712
713namespace
714{
69da77c2 715void fromLuaToRust(const LuaConfigItems& luaConfig, pdns::rust::settings::rec::Dnssec& dnssec)
bab7bbd2 716{
69da77c2
OM
717 dnssec.trustanchorfile = luaConfig.trustAnchorFileInfo.fname;
718 dnssec.trustanchorfile_interval = luaConfig.trustAnchorFileInfo.interval;
eaae11f8 719 dnssec.trustanchors.clear();
69da77c2 720 for (const auto& anchors : luaConfig.dsAnchors) {
bab7bbd2
OM
721 ::rust::Vec<::rust::String> dsRecords;
722 for (const auto& dsRecord : anchors.second) {
723 const auto dsString = dsRecord.getZoneRepresentation();
e2000873 724 if (anchors.first != g_rootdnsname || std::find(rootDSs.begin(), rootDSs.end(), dsString) == rootDSs.end()) {
bab7bbd2
OM
725 dsRecords.emplace_back(dsRecord.getZoneRepresentation());
726 }
727 }
e2000873
OM
728 if (!dsRecords.empty()) {
729 pdns::rust::settings::rec::TrustAnchor trustAnchor{anchors.first.toString(), dsRecords};
730 dnssec.trustanchors.emplace_back(trustAnchor);
731 }
bab7bbd2 732 }
69da77c2 733 for (const auto& anchors : luaConfig.negAnchors) {
bab7bbd2
OM
734 pdns::rust::settings::rec::NegativeTrustAnchor negtrustAnchor{anchors.first.toString(), anchors.second};
735 dnssec.negative_trustanchors.emplace_back(negtrustAnchor);
736 }
737}
738
739void fromLuaToRust(const ProtobufExportConfig& pbConfig, pdns::rust::settings::rec::ProtobufServer& pbServer)
bee74d90
OM
740{
741 for (const auto& server : pbConfig.servers) {
742 pbServer.servers.emplace_back(server.toStringWithPort());
743 }
744 pbServer.timeout = pbConfig.timeout;
745 pbServer.maxQueuedEntries = pbConfig.maxQueuedEntries;
746 pbServer.reconnectWaitTime = pbConfig.reconnectWaitTime;
747 pbServer.taggedOnly = pbConfig.taggedOnly;
748 pbServer.asyncConnect = pbConfig.asyncConnect;
749 pbServer.logQueries = pbConfig.logQueries;
750 pbServer.logResponses = pbConfig.logResponses;
751 for (const auto num : pbConfig.exportTypes) {
752 pbServer.exportTypes.emplace_back(QType(num).toString());
753 }
754 pbServer.logMappedFrom = pbConfig.logMappedFrom;
755}
756
bab7bbd2 757void fromLuaToRust(const FrameStreamExportConfig& fsc, pdns::rust::settings::rec::DNSTapFrameStreamServer& dnstap)
bee74d90 758{
bab7bbd2
OM
759 for (const auto& server : fsc.servers) {
760 dnstap.servers.emplace_back(server);
761 }
762 dnstap.logQueries = fsc.logQueries;
763 dnstap.logResponses = fsc.logQueries;
e2000873 764 dnstap.bufferHint = fsc.bufferHint;
bab7bbd2
OM
765 dnstap.flushTimeout = fsc.flushTimeout;
766 dnstap.inputQueueSize = fsc.inputQueueSize;
767 dnstap.outputQueueSize = fsc.outputQueueSize;
768 dnstap.queueNotifyThreshold = fsc.queueNotifyThreshold;
769 dnstap.reopenInterval = fsc.reopenInterval;
bee74d90
OM
770}
771
bab7bbd2
OM
772void fromLuaToRust(const FrameStreamExportConfig& fsc, pdns::rust::settings::rec::DNSTapNODFrameStreamServer& dnstap)
773{
774 for (const auto& server : fsc.servers) {
775 dnstap.servers.emplace_back(server);
776 }
777 dnstap.logNODs = fsc.logNODs;
778 dnstap.logUDRs = fsc.logUDRs;
e2000873 779 dnstap.bufferHint = fsc.bufferHint;
bab7bbd2
OM
780 dnstap.flushTimeout = fsc.flushTimeout;
781 dnstap.inputQueueSize = fsc.inputQueueSize;
782 dnstap.outputQueueSize = fsc.outputQueueSize;
783 dnstap.queueNotifyThreshold = fsc.queueNotifyThreshold;
784 dnstap.reopenInterval = fsc.reopenInterval;
785}
786
69da77c2 787void assign(pdns::rust::settings::rec::TSIGTriplet& var, const TSIGTriplet& tsig)
bee74d90 788{
69da77c2
OM
789 var.name = tsig.name.empty() ? "" : tsig.name.toStringNoDot();
790 var.algo = tsig.algo.empty() ? "" : tsig.algo.toStringNoDot();
791 var.secret = Base64Encode(tsig.secret);
bee74d90
OM
792}
793
69da77c2 794void assign(TSIGTriplet& var, const pdns::rust::settings::rec::TSIGTriplet& tsig)
bee74d90 795{
e2000873
OM
796 if (!tsig.name.empty()) {
797 var.name = DNSName(std::string(tsig.name));
798 }
799 if (!tsig.algo.empty()) {
800 var.algo = DNSName(std::string(tsig.algo));
801 }
69da77c2 802 B64Decode(std::string(tsig.secret), var.secret);
bee74d90
OM
803}
804
69da77c2 805std::string cvt(DNSFilterEngine::PolicyKind kind)
bee74d90 806{
69da77c2
OM
807 switch (kind) {
808 case DNSFilterEngine::PolicyKind::NoAction:
809 return "NoAction";
810 case DNSFilterEngine::PolicyKind::Drop:
811 return "Drop";
812 case DNSFilterEngine::PolicyKind::NXDOMAIN:
813 return "NXDOMAIN";
814 case DNSFilterEngine::PolicyKind::NODATA:
815 return "NODATA";
816 case DNSFilterEngine::PolicyKind::Truncate:
817 return "Truncate";
818 case DNSFilterEngine::PolicyKind::Custom:
819 return "Custom";
bee74d90 820 }
e2000873 821 return "UnknownPolicyKind";
bee74d90 822}
69da77c2
OM
823
824void fromLuaToRust(const vector<RPZTrackerParams>& rpzs, pdns::rust::settings::rec::Recursor& rec)
bee74d90
OM
825{
826 for (const auto& rpz : rpzs) {
827 pdns::rust::settings::rec::RPZ rustrpz{
e2000873
OM
828 .name = "",
829 .addresses = {},
830 .defcontent = "",
831 .defpol = "",
bee74d90
OM
832 .defpolOverrideLocalData = true,
833 .defttl = std::numeric_limits<uint32_t>::max(),
69da77c2 834 .extendedErrorCode = std::numeric_limits<uint32_t>::max(),
e2000873 835 .extendedErrorExtra = "",
bee74d90
OM
836 .includeSOA = false,
837 .ignoreDuplicates = false,
838 .maxTTL = std::numeric_limits<uint32_t>::max(),
e2000873
OM
839 .policyName = "",
840 .tags = {},
bee74d90
OM
841 .overridesGettag = true,
842 .zoneSizeHint = 0,
e2000873 843 .tsig = {},
bee74d90
OM
844 .refresh = 0,
845 .maxReceivedMBytes = 0,
e2000873
OM
846 .localAddress = "",
847 .axfrTimeout = 20,
848 .dumpFile = "",
849 .seedFile = "",
850 };
bee74d90 851
69da77c2 852 for (const auto& address : rpz.primaries) {
bee74d90
OM
853 rustrpz.addresses.emplace_back(address.toStringWithPort());
854 }
855 rustrpz.name = rpz.name;
69da77c2
OM
856 rustrpz.defcontent = rpz.defcontent;
857 if (rpz.defpol) {
858 rustrpz.defpol = cvt(rpz.defpol->d_kind);
859 rustrpz.defttl = rpz.defpol->d_ttl;
860 }
861 rustrpz.defpolOverrideLocalData = rpz.defpolOverrideLocal;
862 rustrpz.extendedErrorCode = rpz.extendedErrorCode;
863 rustrpz.extendedErrorExtra = rpz.extendedErrorExtra;
864 rustrpz.includeSOA = rpz.includeSOA;
865 rustrpz.ignoreDuplicates = rpz.ignoreDuplicates;
866 rustrpz.maxTTL = rpz.maxTTL;
867 rustrpz.policyName = rpz.polName;
868 for (const auto& tag : rpz.tags) {
869 rustrpz.tags.emplace_back(tag);
bee74d90 870 }
69da77c2
OM
871 rustrpz.overridesGettag = rpz.defpolOverrideLocal;
872 rustrpz.zoneSizeHint = rpz.zoneSizeHint;
873 assign(rustrpz.tsig, rpz.tsigtriplet);
874 rustrpz.refresh = rpz.refreshFromConf;
875 rustrpz.maxReceivedMBytes = rpz.maxReceivedMBytes;
876 if (rpz.localAddress != ComboAddress()) {
877 rustrpz.localAddress = rpz.localAddress.toString();
878 }
879 rustrpz.axfrTimeout = rpz.xfrTimeout;
880 rustrpz.dumpFile = rpz.dumpZoneFileName;
881 rustrpz.seedFile = rpz.seedFileName;
882
bee74d90
OM
883 rec.rpzs.emplace_back(rustrpz);
884 }
885}
886
887string cvt(pdns::ZoneMD::Config cfg)
888{
889 switch (cfg) {
890 case pdns::ZoneMD::Config::Ignore:
891 return "ignore";
892 case pdns::ZoneMD::Config::Validate:
893 return "validate";
894 case pdns::ZoneMD::Config::Require:
895 return "require";
896 }
e2000873 897 return "UnknownZoneMDConfig";
bee74d90
OM
898}
899
bab7bbd2 900void fromLuaToRust(const map<DNSName, RecZoneToCache::Config>& ztcConfigs, pdns::rust::settings::rec::Recordcache& recordcache)
bee74d90
OM
901{
902 for (const auto& [_, iter] : ztcConfigs) {
903 pdns::rust::settings::rec::ZoneToCache ztc;
904 ztc.zone = iter.d_zone;
905 ztc.method = iter.d_method;
906 for (const auto& src : iter.d_sources) {
907 ztc.sources.emplace_back(src);
908 }
909 ztc.timeout = iter.d_timeout;
910 if (!iter.d_tt.name.empty()) {
911 ztc.tsig.name = iter.d_tt.name.toString();
912 ztc.tsig.algo = iter.d_tt.algo.toString();
bab7bbd2 913 ztc.tsig.secret = Base64Encode(iter.d_tt.secret);
bee74d90
OM
914 }
915 ztc.refreshPeriod = iter.d_refreshPeriod;
916 ztc.retryOnErrorPeriod = iter.d_retryOnError;
917 ztc.maxReceivedMBytes = iter.d_maxReceivedBytes;
bab7bbd2
OM
918 if (iter.d_local != ComboAddress()) {
919 ztc.localAddress = iter.d_local.toString();
920 }
bee74d90
OM
921 ztc.zonemd = cvt(iter.d_zonemd);
922 ztc.dnssec = cvt(iter.d_dnssec);
923 recordcache.zonetocaches.emplace_back(ztc);
924 }
925}
bab7bbd2
OM
926
927std::string cvt(AdditionalMode mode)
bee74d90
OM
928{
929 switch (mode) {
930 case AdditionalMode::Ignore:
931 return "Ignore";
932 case AdditionalMode::CacheOnly:
933 return "CacheOnly";
934 case AdditionalMode::CacheOnlyRequireAuth:
935 return "CacheOnlyRequireAuth";
936 case AdditionalMode::ResolveImmediately:
937 return "ResolveImmediately";
938 case AdditionalMode::ResolveDeferred:
939 return "ResolveDeferred";
940 }
e2000873 941 return "UnknownAdditionalMode";
bee74d90
OM
942}
943
bab7bbd2
OM
944AdditionalMode cvtAdditional(const std::string& mode)
945{
946 static const std::map<std::string, AdditionalMode> map = {
e2000873
OM
947 {"Ignore", AdditionalMode::Ignore},
948 {"CacheOnly", AdditionalMode::CacheOnly},
949 {"CacheOnlyRequireAuth", AdditionalMode::CacheOnlyRequireAuth},
950 {"ResolveImmediately", AdditionalMode::ResolveImmediately},
951 {"ResolveDeferred", AdditionalMode::ResolveDeferred}};
bab7bbd2
OM
952 if (auto iter = map.find(mode); iter != map.end()) {
953 return iter->second;
954 }
955 throw runtime_error("AdditionalMode '" + mode + "' unknown");
956}
957
958pdns::ZoneMD::Config cvtZoneMDConfig(const std::string& mode)
959{
960 static const std::map<std::string, pdns::ZoneMD::Config> map = {
e2000873
OM
961 {"ignore", pdns::ZoneMD::Config::Ignore},
962 {"validate", pdns::ZoneMD::Config::Validate},
963 {"require", pdns::ZoneMD::Config::Require},
bab7bbd2
OM
964 };
965 if (auto iter = map.find(mode); iter != map.end()) {
966 return iter->second;
967 }
968 throw runtime_error("ZoneMD config '" + mode + "' unknown");
969}
970
971void fromLuaToRust(const std::map<QType, std::pair<std::set<QType>, AdditionalMode>>& allowAdditionalQTypes, pdns::rust::settings::rec::Recursor& rec)
bee74d90
OM
972{
973 for (const auto& [qtype, data] : allowAdditionalQTypes) {
974 const auto& [qtypeset, mode] = data;
975 pdns::rust::settings::rec::AllowedAdditionalQType add;
976 add.qtype = qtype.toString();
977 for (const auto& extra : qtypeset) {
978 add.targets.emplace_back(extra.toString());
979 }
980 add.mode = cvt(mode);
981 rec.allowed_additional_qtypes.emplace_back(add);
982 }
983}
984
bab7bbd2 985void fromLuaToRust(const ProxyMapping& proxyMapping, pdns::rust::settings::rec::Incoming& incoming)
bee74d90
OM
986{
987 for (const auto& mapping : proxyMapping) {
988 pdns::rust::settings::rec::ProxyMapping pmap;
989 pmap.subnet = mapping.first.toString();
990 pmap.address = mapping.second.address.toString();
991 if (mapping.second.suffixMatchNode) {
992 for (const auto& domain : mapping.second.suffixMatchNode->d_tree.getNodes()) {
993 pmap.domains.emplace_back(domain.toString());
994 }
995 }
996 incoming.proxymappings.emplace_back(pmap);
997 }
998}
999
bab7bbd2 1000void fromLuaToRust(const SortList& arg, pdns::rust::settings::rec::Recursor& rec)
bee74d90
OM
1001{
1002 const auto& sortlist = arg.getTree();
1003 for (const auto& iter : sortlist) {
1004 pdns::rust::settings::rec::SortList rsl;
1005 rsl.key = iter.first.toString();
1006 const auto& sub = iter.second;
1007 // Some extra work to present them ordered in the YAML
1008 std::set<int> indexes;
1009 std::multimap<int, Netmask> ordered;
1010 for (auto& order : sub.d_orders) {
1011 indexes.emplace(order.second);
1012 ordered.emplace(order.second, order.first);
1013 }
1014 for (const auto& index : indexes) {
1015 const auto& range = ordered.equal_range(index);
1016 for (auto subnet = range.first; subnet != range.second; ++subnet) {
1017 pdns::rust::settings::rec::SubnetOrder snorder;
1018 snorder.order = index;
1019 snorder.subnet = subnet->second.toString();
1020 rsl.subnets.emplace_back(snorder);
1021 }
1022 }
1023 rec.sortlists.emplace_back(rsl);
1024 }
1025}
1026} // namespace
1027
69da77c2 1028void pdns::settings::rec::fromLuaConfigToBridgeStruct(LuaConfigItems& luaConfig, const ProxyMapping& proxyMapping, pdns::rust::settings::rec::Recursorsettings& settings)
bee74d90
OM
1029{
1030
bab7bbd2 1031 fromLuaToRust(luaConfig, settings.dnssec);
69da77c2
OM
1032 settings.logging.protobuf_mask_v4 = luaConfig.protobufMaskV4;
1033 settings.logging.protobuf_mask_v6 = luaConfig.protobufMaskV6;
1034 if (luaConfig.protobufExportConfig.enabled) {
bee74d90 1035 pdns::rust::settings::rec::ProtobufServer pbServer;
69da77c2 1036 fromLuaToRust(luaConfig.protobufExportConfig, pbServer);
bee74d90
OM
1037 settings.logging.protobuf_servers.emplace_back(pbServer);
1038 }
69da77c2 1039 if (luaConfig.outgoingProtobufExportConfig.enabled) {
bee74d90 1040 pdns::rust::settings::rec::ProtobufServer pbServer;
69da77c2 1041 fromLuaToRust(luaConfig.outgoingProtobufExportConfig, pbServer);
bee74d90
OM
1042 settings.logging.outgoing_protobuf_servers.emplace_back(pbServer);
1043 }
69da77c2 1044 if (luaConfig.frameStreamExportConfig.enabled) {
bab7bbd2 1045 pdns::rust::settings::rec::DNSTapFrameStreamServer dnstap;
69da77c2 1046 fromLuaToRust(luaConfig.frameStreamExportConfig, dnstap);
bab7bbd2
OM
1047 settings.logging.dnstap_framestream_servers.emplace_back(dnstap);
1048 }
69da77c2 1049 if (luaConfig.nodFrameStreamExportConfig.enabled) {
bab7bbd2 1050 pdns::rust::settings::rec::DNSTapNODFrameStreamServer dnstap;
69da77c2 1051 fromLuaToRust(luaConfig.nodFrameStreamExportConfig, dnstap);
bab7bbd2
OM
1052 settings.logging.dnstap_nod_framestream_servers.emplace_back(dnstap);
1053 }
69da77c2
OM
1054 fromLuaToRust(luaConfig.rpzs, settings.recursor);
1055 fromLuaToRust(luaConfig.sortlist, settings.recursor);
1056 fromLuaToRust(luaConfig.ztcConfigs, settings.recordcache);
1057 fromLuaToRust(luaConfig.allowAdditionalQTypes, settings.recursor);
bab7bbd2
OM
1058 fromLuaToRust(proxyMapping, settings.incoming);
1059}
bee74d90 1060
e2000873
OM
1061namespace
1062{
bab7bbd2
OM
1063void fromRustToLuaConfig(const pdns::rust::settings::rec::Dnssec& dnssec, LuaConfigItems& luaConfig)
1064{
1065 for (const auto& trustAnchor : dnssec.trustanchors) {
eaae11f8
OM
1066 auto name = DNSName(std::string(trustAnchor.name));
1067 luaConfig.dsAnchors.erase(name);
1068 }
1069 for (const auto& trustAnchor : dnssec.trustanchors) {
1070 auto name = DNSName(std::string(trustAnchor.name));
1071 for (const auto& dsRecord : trustAnchor.dsrecords) {
1072 auto dsRecContent = std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(std::string(dsRecord)));
1073 luaConfig.dsAnchors[name].emplace(*dsRecContent);
bab7bbd2
OM
1074 }
1075 }
1076 for (const auto& nta : dnssec.negative_trustanchors) {
1077 luaConfig.negAnchors[DNSName(std::string(nta.name))] = std::string(nta.reason);
1078 }
1079 luaConfig.trustAnchorFileInfo.fname = std::string(dnssec.trustanchorfile);
1080 luaConfig.trustAnchorFileInfo.interval = dnssec.trustanchorfile_interval;
1081}
1082
1083void fromRustToLuaConfig(const pdns::rust::settings::rec::ProtobufServer& pbServer, ProtobufExportConfig& exp)
1084{
1085 exp.enabled = true;
1086 exp.exportTypes.clear();
1087 for (const auto& type : pbServer.exportTypes) {
1088 exp.exportTypes.emplace(QType::chartocode(std::string(type).c_str()));
1089 }
1090 for (const auto& server : pbServer.servers) {
1091 exp.servers.emplace_back(std::string(server));
1092 }
1093 exp.maxQueuedEntries = pbServer.maxQueuedEntries;
1094 exp.timeout = pbServer.timeout;
1095 exp.reconnectWaitTime = pbServer.reconnectWaitTime;
1096 exp.asyncConnect = pbServer.asyncConnect;
1097 exp.logQueries = pbServer.logQueries;
1098 exp.logResponses = pbServer.logResponses;
1099 exp.taggedOnly = pbServer.taggedOnly;
1100 exp.logMappedFrom = pbServer.logMappedFrom;
1101}
1102
1103void fromRustToLuaConfig(const pdns::rust::settings::rec::DNSTapFrameStreamServer& dnstap, FrameStreamExportConfig& exp)
1104{
1105 exp.enabled = true;
1106 for (const auto& server : dnstap.servers) {
1107 exp.servers.emplace_back(std::string(server));
1108 }
1109 exp.logQueries = dnstap.logQueries;
1110 exp.logResponses = dnstap.logResponses;
1111 exp.bufferHint = dnstap.bufferHint;
1112 exp.flushTimeout = dnstap.flushTimeout;
1113 exp.inputQueueSize = dnstap.inputQueueSize;
1114 exp.outputQueueSize = dnstap.outputQueueSize;
1115 exp.queueNotifyThreshold = dnstap.queueNotifyThreshold;
1116 exp.reopenInterval = dnstap.reopenInterval;
1117}
1118
1119void fromRustToLuaConfig(const pdns::rust::settings::rec::DNSTapNODFrameStreamServer& dnstap, FrameStreamExportConfig& exp)
1120{
1121 exp.enabled = true;
1122 for (const auto& server : dnstap.servers) {
1123 exp.servers.emplace_back(std::string(server));
1124 }
1125 exp.logNODs = dnstap.logNODs;
1126 exp.logUDRs = dnstap.logUDRs;
1127 exp.bufferHint = dnstap.bufferHint;
1128 exp.flushTimeout = dnstap.flushTimeout;
1129 exp.inputQueueSize = dnstap.inputQueueSize;
1130 exp.outputQueueSize = dnstap.outputQueueSize;
1131 exp.queueNotifyThreshold = dnstap.queueNotifyThreshold;
1132 exp.reopenInterval = dnstap.reopenInterval;
1133}
1134
69da77c2
OM
1135DNSFilterEngine::PolicyKind cvtKind(const std::string& kind)
1136{
1137 static const std::map<std::string, DNSFilterEngine::PolicyKind> map = {
e2000873
OM
1138 {"Custom", DNSFilterEngine::PolicyKind::Custom},
1139 {"Drop", DNSFilterEngine::PolicyKind::Drop},
1140 {"NoAction", DNSFilterEngine::PolicyKind::NoAction},
1141 {"NODATA", DNSFilterEngine::PolicyKind::NODATA},
1142 {"NXDOMAIN", DNSFilterEngine::PolicyKind::NXDOMAIN},
1143 {"Truncate", DNSFilterEngine::PolicyKind::Truncate}};
69da77c2
OM
1144 if (auto iter = map.find(kind); iter != map.end()) {
1145 return iter->second;
1146 }
1147 throw runtime_error("PolicyKind '" + kind + "' unknown");
1148}
1149
bab7bbd2
OM
1150void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::RPZ>& rpzs, LuaConfigItems& luaConfig)
1151{
1152 for (const auto& rpz : rpzs) {
69da77c2 1153 RPZTrackerParams params;
bab7bbd2 1154 for (const auto& address : rpz.addresses) {
eaae11f8
OM
1155 ComboAddress combo = ComboAddress(std::string(address), 53);
1156 params.primaries.emplace_back(combo.toStringWithPort());
bab7bbd2 1157 }
69da77c2
OM
1158 params.name = std::string(rpz.name);
1159 params.polName = std::string(rpz.policyName);
1160
1161 if (!rpz.defpol.empty()) {
1162 params.defpol = DNSFilterEngine::Policy();
1163 params.defcontent = std::string(rpz.defcontent);
1164 params.defpol->d_kind = cvtKind(std::string(rpz.defpol));
1165 params.defpol->setName(params.polName);
1166 if (params.defpol->d_kind == DNSFilterEngine::PolicyKind::Custom) {
1167 if (!params.defpol->d_custom) {
1168 params.defpol->d_custom = make_unique<DNSFilterEngine::Policy::CustomData>();
1169 }
1170 params.defpol->d_custom->push_back(DNSRecordContent::make(QType::CNAME, QClass::IN,
1171 std::string(params.defcontent)));
bab7bbd2 1172
69da77c2
OM
1173 if (rpz.defttl != std::numeric_limits<uint32_t>::max()) {
1174 params.defpol->d_ttl = static_cast<int>(rpz.defttl);
1175 }
1176 else {
1177 params.defpol->d_ttl = -1; // get it from the zone
1178 }
bab7bbd2 1179 }
bab7bbd2 1180 }
69da77c2
OM
1181 params.defpolOverrideLocal = rpz.defpolOverrideLocalData;
1182 params.extendedErrorCode = rpz.extendedErrorCode;
1183 params.extendedErrorExtra = std::string(rpz.extendedErrorExtra);
1184 params.includeSOA = rpz.includeSOA;
1185 params.ignoreDuplicates = rpz.ignoreDuplicates;
1186 params.maxTTL = rpz.maxTTL;
1187
bab7bbd2 1188 for (const auto& tag : rpz.tags) {
69da77c2 1189 params.tags.emplace(std::string(tag));
bab7bbd2 1190 }
69da77c2
OM
1191 params.defpolOverrideLocal = rpz.overridesGettag;
1192 params.zoneSizeHint = rpz.zoneSizeHint;
1193 assign(params.tsigtriplet, rpz.tsig);
1194 params.refreshFromConf = rpz.refresh;
1195 params.maxReceivedMBytes = rpz.maxReceivedMBytes;
1196 if (!rpz.localAddress.empty()) {
1197 params.localAddress = ComboAddress(std::string(rpz.localAddress));
bab7bbd2 1198 }
69da77c2
OM
1199 params.xfrTimeout = rpz.axfrTimeout;
1200 params.dumpZoneFileName = std::string(rpz.dumpFile);
1201 params.seedFileName = std::string(rpz.seedFile);
1202 luaConfig.rpzs.emplace_back(params);
bab7bbd2
OM
1203 }
1204}
1205
e2000873 1206void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::ZoneToCache>& ztcs, map<DNSName, RecZoneToCache::Config>& lua)
bab7bbd2
OM
1207{
1208 for (const auto& ztc : ztcs) {
1209 DNSName zone = DNSName(std::string(ztc.zone));
1210 RecZoneToCache::Config lztc;
1211 for (const auto& source : ztc.sources) {
1212 lztc.d_sources.emplace_back(std::string(source));
e2000873 1213 }
bab7bbd2
OM
1214 lztc.d_zone = std::string(ztc.zone);
1215 lztc.d_method = std::string(ztc.method);
1216 if (!ztc.localAddress.empty()) {
1217 lztc.d_local = ComboAddress(std::string(ztc.localAddress));
1218 }
1219 if (!ztc.tsig.name.empty()) {
1220 lztc.d_tt.name = DNSName(std::string(ztc.tsig.name));
1221 lztc.d_tt.algo = DNSName(std::string(ztc.tsig.algo));
1222 B64Decode(std::string(ztc.tsig.secret), lztc.d_tt.secret);
1223 }
1224 lztc.d_maxReceivedBytes = ztc.maxReceivedMBytes;
1225 lztc.d_retryOnError = static_cast<time_t>(ztc.retryOnErrorPeriod);
1226 lztc.d_refreshPeriod = static_cast<time_t>(ztc.refreshPeriod);
1227 lztc.d_timeout = ztc.timeout;
1228 lztc.d_zonemd = cvtZoneMDConfig(std::string(ztc.zonemd));
1229 lztc.d_dnssec = cvtZoneMDConfig(std::string(ztc.dnssec));
1230 lua.emplace(zone, lztc);
1231 }
1232}
1233
1234void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::SortList>& sortlists, SortList& lua)
1235{
1236 for (const auto& sortlist : sortlists) {
1237 for (const auto& entry : sortlist.subnets) {
1238 lua.addEntry(Netmask(std::string(sortlist.key)), Netmask(std::string(entry.subnet)), static_cast<int>(entry.order));
1239 }
1240 }
1241}
1242
1243void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::AllowedAdditionalQType>& alloweds, std::map<QType, std::pair<std::set<QType>, AdditionalMode>>& lua)
1244{
1245 for (const auto& allowed : alloweds) {
1246 QType qtype(QType::chartocode(std::string(allowed.qtype).c_str()));
1247 std::set<QType> set;
1248 for (const auto& target : allowed.targets) {
1249 set.emplace(QType::chartocode(std::string(target).c_str()));
1250 }
1251 AdditionalMode mode = AdditionalMode::CacheOnlyRequireAuth;
1252 mode = cvtAdditional(std::string(allowed.mode));
1253 lua.emplace(qtype, std::pair{set, mode});
1254 }
1255}
1256
1257void fromRustToLuaConfig(const rust::Vec<pdns::rust::settings::rec::ProxyMapping>& pmaps, ProxyMapping& proxyMapping)
1258{
1259 for (const auto& pmap : pmaps) {
1260 Netmask subnet = Netmask(std::string(pmap.subnet));
1261 ComboAddress address(std::string(pmap.address));
1262 boost::optional<SuffixMatchNode> smn;
1263 if (!pmap.domains.empty()) {
1264 smn = boost::make_optional(SuffixMatchNode{});
1265 for (const auto& dom : pmap.domains) {
1266 smn->add(DNSName(std::string(dom)));
1267 }
1268 }
1269 proxyMapping.insert_or_assign(subnet, {address, smn});
1270 }
1271}
1272}
1273
1274void pdns::settings::rec::fromBridgeStructToLuaConfig(const pdns::rust::settings::rec::Recursorsettings& settings, LuaConfigItems& luaConfig, ProxyMapping& proxyMapping)
1275{
1276 fromRustToLuaConfig(settings.dnssec, luaConfig);
69da77c2
OM
1277 luaConfig.protobufMaskV4 = settings.logging.protobuf_mask_v4;
1278 luaConfig.protobufMaskV6 = settings.logging.protobuf_mask_v6;
bab7bbd2
OM
1279 if (!settings.logging.protobuf_servers.empty()) {
1280 fromRustToLuaConfig(settings.logging.protobuf_servers.at(0), luaConfig.protobufExportConfig);
1281 }
1282 if (!settings.logging.outgoing_protobuf_servers.empty()) {
1283 fromRustToLuaConfig(settings.logging.outgoing_protobuf_servers.at(0), luaConfig.outgoingProtobufExportConfig);
1284 }
1285 if (!settings.logging.dnstap_framestream_servers.empty()) {
1286 fromRustToLuaConfig(settings.logging.dnstap_framestream_servers.at(0), luaConfig.frameStreamExportConfig);
1287 }
1288 if (!settings.logging.dnstap_nod_framestream_servers.empty()) {
1289 fromRustToLuaConfig(settings.logging.dnstap_nod_framestream_servers.at(0), luaConfig.nodFrameStreamExportConfig);
1290 }
1291 fromRustToLuaConfig(settings.recursor.rpzs, luaConfig);
1292 fromRustToLuaConfig(settings.recursor.sortlists, luaConfig.sortlist);
1293 fromRustToLuaConfig(settings.recordcache.zonetocaches, luaConfig.ztcConfigs);
1294 fromRustToLuaConfig(settings.recursor.allowed_additional_qtypes, luaConfig.allowAdditionalQTypes);
1295 fromRustToLuaConfig(settings.incoming.proxymappings, proxyMapping);
bee74d90 1296}
69da77c2 1297
e2000873
OM
1298// Return true if an item that's (also) a Lua config ite is set
1299bool pdns::settings::rec::luaItemSet(const pdns::rust::settings::rec::Recursorsettings& settings)
1300{
1301 bool alldefault = true;
eaae11f8
OM
1302 for (const auto& trustanchor : settings.dnssec.trustanchors) {
1303 if (trustanchor.name == ".") {
1304 if (trustanchor.dsrecords.size() != rootDSs.size()) {
1305 alldefault = false;
1306 break;
60434ad5
OM
1307 }
1308 for (const auto& dsRecord : trustanchor.dsrecords) {
1309 if (std::find(rootDSs.begin(), rootDSs.end(), std::string(dsRecord)) == rootDSs.end()) {
1310 alldefault = false;
1311 break;
eaae11f8
OM
1312 }
1313 }
1314 }
1315 else {
1316 alldefault = false;
1317 break;
1318 }
1319 }
e2000873
OM
1320 alldefault = alldefault && settings.dnssec.negative_trustanchors.empty();
1321 alldefault = alldefault && settings.dnssec.trustanchorfile.empty();
1322 alldefault = alldefault && settings.dnssec.trustanchorfile_interval == 24;
1323 alldefault = alldefault && settings.logging.protobuf_mask_v4 == 32;
1324 alldefault = alldefault && settings.logging.protobuf_mask_v6 == 128;
1325 alldefault = alldefault && settings.logging.protobuf_servers.empty();
1326 alldefault = alldefault && settings.logging.outgoing_protobuf_servers.empty();
1327 alldefault = alldefault && settings.logging.dnstap_framestream_servers.empty();
1328 alldefault = alldefault && settings.logging.dnstap_nod_framestream_servers.empty();
1329 alldefault = alldefault && settings.recursor.sortlists.empty();
1330 alldefault = alldefault && settings.recursor.rpzs.empty();
1331 alldefault = alldefault && settings.recordcache.zonetocaches.empty();
1332 alldefault = alldefault && settings.recursor.allowed_additional_qtypes.empty();
1333 alldefault = alldefault && settings.incoming.proxymappings.empty();
1334 return !alldefault;
1335}
1336
1337pdns::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)
1338{
1339 string msg;
1340 // TODO: handle include-dir on command line
1341 auto yamlstatus = pdns::settings::rec::readYamlSettings(yamlconfigname, ::arg()["include-dir"], settings, msg, startupLog);
1342
1343 switch (yamlstatus) {
1344 case pdns::settings::rec::YamlSettingsStatus::CannotOpen:
1345 SLOG(g_log << Logger::Debug << "No YAML config found for configname '" << yamlconfigname << "': " << msg << endl,
1346 startupLog->error(Logr::Debug, msg, "No YAML config found", "configname", Logging::Loggable(yamlconfigname)));
1347 break;
1348
1349 case pdns::settings::rec::YamlSettingsStatus::PresentButFailed:
1350 SLOG(g_log << Logger::Error << "YAML config found for configname '" << yamlconfigname << "' but error ocurred processing it" << endl,
1351 startupLog->error(Logr::Error, msg, "YAML config found, but error occurred processsing it", "configname", Logging::Loggable(yamlconfigname)));
1352 break;
1353
1354 case pdns::settings::rec::YamlSettingsStatus::OK:
1355 yamlSettings = true;
1356 SLOG(g_log << Logger::Notice << "YAML config found and processed for configname '" << yamlconfigname << "'" << endl,
1357 startupLog->info(Logr::Notice, "YAML config found and processed", "configname", Logging::Loggable(yamlconfigname)));
1358 pdns::settings::rec::processAPIDir(arg()["include-dir"], settings, startupLog);
1359 luaSettingsInYAML = pdns::settings::rec::luaItemSet(settings);
e2000873
OM
1360 if (luaSettingsInYAML && !settings.recursor.lua_config_file.empty()) {
1361 const std::string err = "YAML settings include values originally in Lua but also sets `recursor.lua_config_file`. This is unsupported";
1362 SLOG(g_log << Logger::Error << err << endl,
1363 startupLog->info(Logr::Error, err, "configname", Logging::Loggable(yamlconfigname)));
1364 yamlstatus = pdns::settings::rec::PresentButFailed;
1365 }
1366 else if (setGlobals) {
1367 pdns::settings::rec::bridgeStructToOldStyleSettings(settings);
1368 }
1369 break;
1370 }
1371 return yamlstatus;
1372}
4792eae8
OM
1373
1374uint16_t pdns::rust::settings::rec::qTypeStringToCode(::rust::Str str)
1375{
1376 std::string tmp(str.data(), str.length());
1377 return QType::chartocode(tmp.c_str());
1378}
7bbf885a
OM
1379
1380bool pdns::rust::settings::rec::isValidHostname(::rust::Str str)
1381{
1382 try {
1383 auto name = DNSName(string(str));
1384 return name.isHostname();
1385 }
1386 catch (...) {
1387 return false;
1388 }
1389}