]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursordist/rec_control.cc
rec: Remove experimental warnings for YAML
[thirdparty/pdns.git] / pdns / recursordist / rec_control.cc
CommitLineData
aaacf7f2 1/*
6edbf68a
PL
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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
5b6b0dae 25
1d5b3ce6 26#include <iostream>
a8f0a123 27#include <fcntl.h>
5b6b0dae 28
5c409fa2 29#include "pdnsexception.hh"
aaacf7f2 30#include "arguments.hh"
5b6b0dae 31#include "credentials.hh"
10f4eea8 32#include "namespaces.hh"
5b6b0dae 33#include "rec_channel.hh"
9883d3f9 34#include "settings/cxxsettings.hh"
1d5b3ce6 35
a7f98e34 36ArgvMap& arg()
aaacf7f2
BH
37{
38 static ArgvMap arg;
39 return arg;
40}
41
42static void initArguments(int argc, char** argv)
43{
a7f98e34
O
44 arg().set("config-dir", "Location of configuration directory (recursor.conf)") = SYSCONFDIR;
45
46 arg().set("socket-dir", string("Where the controlsocket will live, ") + LOCALSTATEDIR + "/pdns-recursor when unset and not chrooted") = "";
47 arg().set("chroot", "switch to chroot jail") = "";
48 arg().set("process", "When controlling multiple recursors, the target process number") = "";
49 arg().set("timeout", "Number of seconds to wait for the recursor to respond") = "5";
50 arg().set("config-name", "Name of this virtual configuration - will rename the binary image") = "";
51 arg().setCmd("help", "Provide this helpful message");
52 arg().setCmd("version", "Show the version of this program");
53
54 arg().laxParse(argc, argv);
2e21fc17
OM
55 if (arg().mustDo("version")) {
56 cout << "rec_control version " << VERSION << endl;
57 exit(0);
58 }
a7f98e34
O
59 if (arg().mustDo("help") || arg().getCommands().empty()) {
60 cout << "syntax: rec_control [options] command, options as below: " << endl
61 << endl;
62 cout << arg().helpstring(arg()["help"]) << endl;
63 cout << "In addition, 'rec_control help' can be used to retrieve a list\nof available commands from PowerDNS" << endl;
b710dd81 64 exit(arg().mustDo("help") ? 0 : 99);
aaacf7f2 65 }
5124de27 66
9883d3f9
OM
67 string configname = ::arg()["config-dir"] + "/recursor";
68 if (!::arg()["config-name"].empty()) {
69 configname = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
70 }
a7f98e34 71
832ad64e
PD
72 cleanSlashes(configname);
73
9883d3f9
OM
74 const string yamlconfigname = configname + ".yml";
75 string msg;
76 pdns::rust::settings::rec::Recursorsettings settings;
f0f3f0b0 77
9883d3f9
OM
78 auto yamlstatus = pdns::settings::rec::readYamlSettings(yamlconfigname, "", settings, msg, g_slog);
79
80 switch (yamlstatus) {
81 case pdns::settings::rec::YamlSettingsStatus::CannotOpen:
82 break;
83 case pdns::settings::rec::YamlSettingsStatus::PresentButFailed:
84 cerr << "YAML config found for configname '" << yamlconfigname << "' but error ocurred processing it" << endl;
85 exit(1); // NOLINT(concurrency-mt-unsafe)
86 break;
87 case pdns::settings::rec::YamlSettingsStatus::OK:
88 cerr << "YAML config found and processed for configname '" << yamlconfigname << "'" << endl;
89 pdns::settings::rec::bridgeStructToOldStyleSettings(settings);
90 break;
91 }
f0f3f0b0 92
9883d3f9
OM
93 if (yamlstatus == pdns::settings::rec::YamlSettingsStatus::CannotOpen) {
94 configname += ".conf";
95 arg().laxFile(configname);
96 }
97 arg().laxParse(argc, argv); // make sure the commandline wins
f0f3f0b0 98 if (::arg()["socket-dir"].empty()) {
9883d3f9 99 if (::arg()["chroot"].empty()) {
0524add9 100 ::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor";
9883d3f9
OM
101 }
102 else {
f0f3f0b0 103 ::arg().set("socket-dir") = ::arg()["chroot"] + "/";
9883d3f9 104 }
a7f98e34
O
105 }
106 else if (!::arg()["chroot"].empty()) {
f0f3f0b0
PL
107 ::arg().set("socket-dir") = ::arg()["chroot"] + "/" + ::arg()["socket-dir"];
108 }
aaacf7f2
BH
109}
110
9883d3f9
OM
111static std::string showIncludeYAML(::rust::String& rdirname)
112{
113 std::string msg;
114 if (rdirname.empty()) {
115 return msg;
116 }
117 const auto dirname = string(rdirname);
118
119 std::vector<std::string> confFiles;
120 ::arg().gatherIncludes(dirname, ".conf", confFiles);
121 msg += "# Found " + std::to_string(confFiles.size()) + " .conf file" + addS(confFiles.size()) + " in " + dirname + "\n";
122 for (const auto& confFile : confFiles) {
123 auto converted = pdns::settings::rec::oldStyleSettingsFileToYaml(confFile, false);
124 msg += "# Converted include-dir " + confFile + " to YAML format:\n";
125 msg += converted;
126 msg += "# Validation result: ";
127 try {
128 // Parse back and validate
129 auto settings = pdns::rust::settings::rec::parse_yaml_string(converted);
130 settings.validate();
131 msg += "OK";
132 }
133 catch (const rust::Error& err) {
134 msg += err.what();
135 }
136 msg += "\n# End of converted " + confFile + "\n#\n";
137 }
138 return msg;
139}
140
141static std::string showForwardFileYAML(const ::rust::string& rfilename)
142{
143 std::string msg;
144 if (rfilename.empty() || boost::ends_with(rfilename, ".yml")) {
145 return msg;
146 }
147 const std::string filename = string(rfilename);
148
149 msg += "# Converted " + filename + " to YAML format for recursor.forward_zones_file: \n";
150 rust::Vec<pdns::rust::settings::rec::ForwardZone> forwards;
151 pdns::settings::rec::oldStyleForwardsFileToBridgeStruct(filename, forwards);
152 auto yaml = pdns::rust::settings::rec::forward_zones_to_yaml_string(forwards);
153 msg += std::string(yaml);
154 msg += "# Validation result: ";
155 try {
156 pdns::rust::settings::rec::validate_forward_zones("forward_zones", forwards);
157 msg += "OK";
158 }
159 catch (const rust::Error& err) {
160 msg += err.what();
161 }
162 msg += "\n# End of converted " + filename + "\n#\n";
163 return msg;
164}
165
166static std::string showAllowYAML(const ::rust::String& rfilename, const string& section, const string& key, const std::function<void(const ::rust::String&, const ::rust::Vec<::rust::String>&)>& func)
167{
168 std::string msg;
169 if (rfilename.empty() || boost::ends_with(rfilename, ".yml")) {
170 return msg;
171 }
172 const std::string filename = string(rfilename);
173
174 msg += "# Converted " + filename + " to YAML format for " + section + "." + key + ": \n";
175 rust::Vec<::rust::String> allows;
176 pdns::settings::rec::oldStyleAllowFileToBridgeStruct(filename, allows);
177 auto yaml = pdns::rust::settings::rec::allow_from_to_yaml_string(allows);
178 msg += std::string(yaml);
179 msg += "# Validation result: ";
180 try {
181 func(key, allows);
182 msg += "OK";
183 }
184 catch (const rust::Error& err) {
185 msg += err.what();
186 }
187 msg += "\n# End of converted " + filename + "\n#\n";
188 return msg;
189}
190
191static RecursorControlChannel::Answer showYAML(const std::string& path)
192{
193 string configName = ::arg()["config-dir"] + "/recursor.conf";
194 if (!::arg()["config-name"].empty()) {
195 configName = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"] + ".conf";
196 }
197 if (!path.empty()) {
198 configName = path;
199 }
200 cleanSlashes(configName);
201
202 try {
203 std::string msg;
204 auto converted = pdns::settings::rec::oldStyleSettingsFileToYaml(configName, true);
9883d3f9
OM
205 msg += "# Start of converted recursor.yml based on " + configName + "\n";
206 msg += converted;
207 msg += "# Validation result: ";
208 pdns::rust::settings::rec::Recursorsettings mainsettings;
209 try {
210 // Parse back and validate
211 mainsettings = pdns::rust::settings::rec::parse_yaml_string(converted);
212 mainsettings.validate();
213 msg += "OK";
214 }
215 catch (const rust::Error& err) {
216 msg += err.what();
217 }
218 msg += "\n# End of converted " + configName + "\n#\n";
219
220 msg += showIncludeYAML(mainsettings.recursor.include_dir);
221 msg += showForwardFileYAML(mainsettings.recursor.forward_zones_file);
222 msg += showAllowYAML(mainsettings.incoming.allow_from_file, "incoming", "allow_from_file", pdns::rust::settings::rec::validate_allow_from);
223 msg += showAllowYAML(mainsettings.incoming.allow_notify_from_file, "incoming", "allow_notify_from_file", pdns::rust::settings::rec::validate_allow_from);
224 msg += showAllowYAML(mainsettings.incoming.allow_notify_for_file, "incoming", "allow_notify_for_file", pdns::rust::settings::rec::validate_allow_for);
225 return {0, msg};
226 }
227 catch (const rust::Error& err) {
228 return {1, std::string(err.what())};
229 }
230 catch (const PDNSException& err) {
231 return {1, std::string(err.reason)};
232 }
233 catch (const std::exception& err) {
234 return {1, std::string(err.what())};
235 }
236}
237
f6f016d1
BH
238int main(int argc, char** argv)
239{
ebaffe64 240 g_slogStructured = false;
51e90474
O
241 const set<string> fileCommands = {
242 "dump-cache",
243 "dump-edns",
244 "dump-ednsstatus",
245 "dump-nsspeeds",
246 "dump-failedservers",
247 "dump-rpz",
e329b582 248 "dump-throttlemap",
4e45e5bc 249 "dump-non-resolving",
6715fc09 250 "dump-saved-parent-ns-sets",
378437c9 251 "dump-dot-probe-map",
42ae54e3 252 "trace-regex",
378437c9 253 };
2157aec4
OM
254 try {
255 initArguments(argc, argv);
a7f98e34 256 string sockname = "pdns_recursor";
2157aec4
OM
257
258 if (arg()["config-name"] != "")
a7f98e34 259 sockname += "-" + arg()["config-name"];
2157aec4 260
a7f98e34
O
261 if (!arg()["process"].empty())
262 sockname += "." + arg()["process"];
2157aec4
OM
263
264 sockname.append(".controlsocket");
265
a7f98e34 266 const vector<string>& commands = arg().getCommands();
3c225728 267
9883d3f9
OM
268 if (!commands.empty() && commands.at(0) == "show-yaml") {
269 auto [ret, str] = showYAML(commands.size() > 1 ? commands.at(1) : "");
270 cout << str << endl;
271 return ret;
272 }
273
274 if (!commands.empty() && commands.at(0) == "hash-password") {
3c225728
RG
275 uint64_t workFactor = CredentialsHolder::s_defaultWorkFactor;
276 if (commands.size() > 1) {
277 try {
a0383aad 278 pdns::checked_stoi_into(workFactor, commands.at(1));
3c225728
RG
279 }
280 catch (const std::exception& e) {
281 cerr << "Unable to parse the supplied work factor: " << e.what() << endl;
282 return EXIT_FAILURE;
283 }
284 }
285
286 auto password = CredentialsHolder::readFromTerminal();
287
288 try {
289 cout << hashPassword(password.getString(), workFactor, CredentialsHolder::s_defaultParallelFactor, CredentialsHolder::s_defaultBlockSize) << endl;
290 return EXIT_SUCCESS;
291 }
292 catch (const std::exception& e) {
293 cerr << "Error while hashing the supplied password: " << e.what() << endl;
294 return EXIT_FAILURE;
295 }
296 }
297
2157aec4
OM
298 string command;
299 int fd = -1;
51e90474
O
300 unsigned int i = 0;
301 while (i < commands.size()) {
302 if (i > 0) {
303 command += " ";
304 }
305 command += commands[i];
42ae54e3
OM
306
307 // special case: trace-regex with no arguments is clear regex
308 auto traceregexClear = command == "trace-regex" && commands.size() == 1;
309
310 if (fileCommands.count(commands[i]) > 0 && !traceregexClear) {
0e4bbf5d
O
311 if (i + 1 < commands.size()) {
312 // dump-rpz is different, it also has a zonename as argument
42ae54e3
OM
313 // trace-regex is different, it also has a regexp as argument
314 if (commands[i] == "dump-rpz" || commands[i] == "trace-regex") {
0e4bbf5d
O
315 if (i + 2 < commands.size()) {
316 ++i;
42ae54e3
OM
317 command += " ";
318 command += commands[i]; // add rpzname/regex and continue with filename
a7f98e34
O
319 }
320 else {
42ae54e3 321 throw PDNSException("Command needs two arguments");
0e4bbf5d
O
322 }
323 }
51e90474 324 ++i;
0e4bbf5d
O
325 if (commands[i] == "-") {
326 fd = STDOUT_FILENO;
a7f98e34
O
327 }
328 else {
0e4bbf5d
O
329 fd = open(commands[i].c_str(), O_CREAT | O_EXCL | O_WRONLY, 0660);
330 }
331 if (fd == -1) {
332 int err = errno;
333 throw PDNSException("Error opening dump file for writing: " + stringerror(err));
334 }
a7f98e34
O
335 }
336 else {
0e4bbf5d 337 throw PDNSException("Command needs a file argument");
2157aec4 338 }
a8f0a123 339 }
51e90474 340 ++i;
a8f0a123 341 }
2157aec4
OM
342
343 auto timeout = arg().asNum("timeout");
5b6b0dae
RG
344 RecursorControlChannel rccS;
345 rccS.connect(arg()["socket-dir"], sockname);
6c593a38 346 rccS.send(rccS.d_fd, {0, command}, timeout, fd);
2157aec4 347
6c593a38 348 auto receive = rccS.recv(rccS.d_fd, timeout);
adf0f368
O
349 if (receive.d_ret != 0) {
350 cerr << receive.d_str;
a7f98e34
O
351 }
352 else {
adf0f368 353 cout << receive.d_str;
2157aec4 354 }
adf0f368 355 return receive.d_ret;
1d5b3ce6 356 }
a7f98e34
O
357 catch (PDNSException& ae) {
358 cerr << "Fatal: " << ae.reason << "\n";
be10c6b7
RK
359 return 1;
360 }
f6f016d1 361}