]>
Commit | Line | Data |
---|---|---|
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 | 36 | ArgvMap& arg() |
aaacf7f2 BH |
37 | { |
38 | static ArgvMap arg; | |
39 | return arg; | |
40 | } | |
41 | ||
42 | static 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 |
111 | static 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 | ||
141 | static 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 | ||
166 | static 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 | ||
191 | static 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 |
238 | int 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 | } |