]>
Commit | Line | Data |
---|---|---|
bc7a7b24 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 | */ | |
eeaa1cb6 | 22 | #include <sys/stat.h> |
bc7a7b24 OM |
23 | |
24 | #include "rec-main.hh" | |
25 | ||
eeaa1cb6 OM |
26 | #include "aggressive_nsec.hh" |
27 | #include "capabilities.hh" | |
28 | #include "arguments.hh" | |
29 | #include "dns_random.hh" | |
30 | #include "rec_channel.hh" | |
31 | #include "rec-tcpout.hh" | |
32 | #include "version.hh" | |
33 | #include "query-local-address.hh" | |
34 | #include "validate-recursor.hh" | |
35 | #include "pubsuffix.hh" | |
36 | #include "opensslsigners.hh" | |
eeaa1cb6 OM |
37 | #include "ws-recursor.hh" |
38 | #include "rec-taskqueue.hh" | |
39 | #include "secpoll-recursor.hh" | |
36e3f42a | 40 | #include "logging.hh" |
eeaa1cb6 OM |
41 | |
42 | #ifdef NOD_ENABLED | |
43 | #include "nod.hh" | |
eeaa1cb6 OM |
44 | #endif /* NOD_ENABLED */ |
45 | ||
46 | #ifdef HAVE_LIBSODIUM | |
47 | #include <sodium.h> | |
48 | #endif | |
49 | ||
50 | #ifdef HAVE_SYSTEMD | |
0112056b OM |
51 | // All calls are coming form the same function, so no use for CODE_LINE, CODE_FUNC etc |
52 | #define SD_JOURNAL_SUPPRESS_LOCATION | |
eeaa1cb6 | 53 | #include <systemd/sd-daemon.h> |
0112056b | 54 | #include <systemd/sd-journal.h> |
eeaa1cb6 OM |
55 | #endif |
56 | ||
eeaa1cb6 | 57 | #ifdef HAVE_FSTRM |
afaf1b5d | 58 | thread_local FrameStreamServersInfo t_frameStreamServersInfo; |
9489e2b5 | 59 | thread_local FrameStreamServersInfo t_nodFrameStreamServersInfo; |
eeaa1cb6 OM |
60 | #endif /* HAVE_FSTRM */ |
61 | ||
b35191d7 OM |
62 | string g_programname = "pdns_recursor"; |
63 | string g_pidfname; | |
64 | RecursorControlChannel g_rcc; // only active in the handler thread | |
eeaa1cb6 OM |
65 | |
66 | #ifdef NOD_ENABLED | |
67 | bool g_nodEnabled; | |
68 | DNSName g_nodLookupDomain; | |
69 | bool g_nodLog; | |
70 | SuffixMatchNode g_nodDomainWL; | |
71 | std::string g_nod_pbtag; | |
72 | bool g_udrEnabled; | |
73 | bool g_udrLog; | |
74 | std::string g_udr_pbtag; | |
75 | thread_local std::shared_ptr<nod::NODDB> t_nodDBp; | |
76 | thread_local std::shared_ptr<nod::UniqueResponseDB> t_udrDBp; | |
77 | #endif /* NOD_ENABLED */ | |
78 | ||
79 | std::atomic<bool> statsWanted; | |
eeaa1cb6 | 80 | uint32_t g_disthashseed; |
eeaa1cb6 | 81 | bool g_useIncomingECS; |
eeaa1cb6 OM |
82 | NetmaskGroup g_proxyProtocolACL; |
83 | boost::optional<ComboAddress> g_dns64Prefix{boost::none}; | |
84 | DNSName g_dns64PrefixReverse; | |
85 | std::shared_ptr<SyncRes::domainmap_t> g_initialDomainMap; // new threads needs this to be setup | |
86 | std::shared_ptr<NetmaskGroup> g_initialAllowFrom; // new thread needs to be setup with this | |
87 | std::shared_ptr<NetmaskGroup> g_initialAllowNotifyFrom; // new threads need this to be setup | |
88 | std::shared_ptr<notifyset_t> g_initialAllowNotifyFor; // new threads need this to be setup | |
89 | bool g_logRPZChanges{false}; | |
b35191d7 OM |
90 | static time_t s_statisticsInterval; |
91 | bool g_addExtendedResolutionDNSErrors; | |
92 | static std::atomic<uint32_t> s_counter; | |
eeaa1cb6 OM |
93 | int g_argc; |
94 | char** g_argv; | |
2281ecb1 | 95 | static string s_structured_logger_backend; |
1e5eef5e | 96 | static Logger::Urgency s_logUrgency; |
eeaa1cb6 | 97 | |
91092a9f OM |
98 | std::shared_ptr<Logr::Logger> g_slogtcpin; |
99 | std::shared_ptr<Logr::Logger> g_slogudpin; | |
100 | std::shared_ptr<Logr::Logger> g_slogudpout; | |
ab26d8d5 | 101 | |
eeaa1cb6 OM |
102 | /* without reuseport, all listeners share the same sockets */ |
103 | deferredAdd_t g_deferredAdds; | |
104 | ||
105 | /* first we have the handler thread, t_id == 0 (some other | |
106 | helper threads like SNMP might have t_id == 0 as well) | |
107 | then the distributor threads if any | |
108 | and finally the workers */ | |
4f6e00dc | 109 | std::vector<RecThreadInfo> RecThreadInfo::s_threadInfos; |
eeaa1cb6 | 110 | |
c375521b OM |
111 | std::unique_ptr<ProxyMapping> g_proxyMapping; // new threads needs this to be setup |
112 | thread_local std::unique_ptr<ProxyMapping> t_proxyMapping; | |
e81063e5 | 113 | |
69b39198 OM |
114 | bool RecThreadInfo::s_weDistributeQueries; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers |
115 | unsigned int RecThreadInfo::s_numDistributorThreads; | |
116 | unsigned int RecThreadInfo::s_numWorkerThreads; | |
117 | thread_local unsigned int RecThreadInfo::t_id; | |
118 | ||
d61b8a01 | 119 | static std::map<unsigned int, std::set<int>> parseCPUMap(Logr::log_t log) |
3487974d OM |
120 | { |
121 | std::map<unsigned int, std::set<int>> result; | |
122 | ||
123 | const std::string value = ::arg()["cpu-map"]; | |
124 | ||
125 | if (!value.empty() && !isSettingThreadCPUAffinitySupported()) { | |
e4565646 OM |
126 | SLOG(g_log << Logger::Warning << "CPU mapping requested but not supported, skipping" << endl, |
127 | log->info(Logr::Warning, "CPU mapping requested but not supported, skipping")); | |
3487974d OM |
128 | return result; |
129 | } | |
130 | ||
131 | std::vector<std::string> parts; | |
132 | ||
133 | stringtok(parts, value, " \t"); | |
134 | ||
135 | for (const auto& part : parts) { | |
136 | if (part.find('=') == string::npos) | |
137 | continue; | |
138 | ||
139 | try { | |
140 | auto headers = splitField(part, '='); | |
141 | boost::trim(headers.first); | |
142 | boost::trim(headers.second); | |
143 | ||
a0383aad | 144 | auto threadId = pdns::checked_stoi<unsigned int>(headers.first); |
3487974d OM |
145 | std::vector<std::string> cpus; |
146 | ||
147 | stringtok(cpus, headers.second, ","); | |
148 | ||
149 | for (const auto& cpu : cpus) { | |
150 | int cpuId = std::stoi(cpu); | |
151 | ||
152 | result[threadId].insert(cpuId); | |
153 | } | |
154 | } | |
155 | catch (const std::exception& e) { | |
e4565646 OM |
156 | SLOG(g_log << Logger::Error << "Error parsing cpu-map entry '" << part << "': " << e.what() << endl, |
157 | log->error(Logr::Error, e.what(), "Error parsing cpu-map entry", "entry", Logging::Loggable(part))); | |
3487974d OM |
158 | } |
159 | } | |
160 | ||
161 | return result; | |
162 | } | |
163 | ||
d61b8a01 | 164 | static void setCPUMap(const std::map<unsigned int, std::set<int>>& cpusMap, unsigned int n, pthread_t tid, Logr::log_t log) |
3487974d OM |
165 | { |
166 | const auto& cpuMapping = cpusMap.find(n); | |
167 | if (cpuMapping == cpusMap.cend()) { | |
168 | return; | |
169 | } | |
170 | int rc = mapThreadToCPUList(tid, cpuMapping->second); | |
171 | if (rc == 0) { | |
e4565646 OM |
172 | if (!g_slogStructured) { |
173 | g_log << Logger::Info << "CPU affinity for thread " << n << " has been set to CPU map:"; | |
174 | for (const auto cpu : cpuMapping->second) { | |
175 | g_log << Logger::Info << " " << cpu; | |
176 | } | |
177 | g_log << Logger::Info << endl; | |
178 | } | |
179 | else { | |
dfd0f3c9 | 180 | log->info(Logr::Info, "CPU affinity has been set", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end())); |
3487974d | 181 | } |
3487974d OM |
182 | } |
183 | else { | |
e4565646 OM |
184 | if (!g_slogStructured) { |
185 | g_log << Logger::Warning << "Error setting CPU affinity for thread " << n << " to CPU map:"; | |
186 | for (const auto cpu : cpuMapping->second) { | |
187 | g_log << Logger::Info << " " << cpu; | |
188 | } | |
189 | g_log << Logger::Info << ' ' << strerror(rc) << endl; | |
190 | } | |
191 | else { | |
88ddfdf4 | 192 | log->error(Logr::Warning, rc, "Error setting CPU affinity", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end())); |
3487974d | 193 | } |
3487974d OM |
194 | } |
195 | } | |
196 | ||
a0a3a61a OM |
197 | static void recursorThread(); |
198 | ||
d61b8a01 | 199 | void RecThreadInfo::start(unsigned int id, const string& tname, const std::map<unsigned int, std::set<int>>& cpusMap, Logr::log_t log) |
3487974d | 200 | { |
aee3ab9f OM |
201 | name = tname; |
202 | thread = std::thread([id, tname] { | |
3487974d OM |
203 | t_id = id; |
204 | const string threadPrefix = "rec/"; | |
aee3ab9f | 205 | setThreadName(threadPrefix + tname); |
3487974d OM |
206 | recursorThread(); |
207 | }); | |
e4565646 | 208 | setCPUMap(cpusMap, id, thread.native_handle(), log); |
3487974d OM |
209 | } |
210 | ||
d61b8a01 | 211 | int RecThreadInfo::runThreads(Logr::log_t log) |
3487974d OM |
212 | { |
213 | int ret = EXIT_SUCCESS; | |
214 | unsigned int currentThreadId = 1; | |
e4565646 | 215 | const auto cpusMap = parseCPUMap(log); |
3487974d OM |
216 | |
217 | if (RecThreadInfo::numDistributors() + RecThreadInfo::numWorkers() == 1) { | |
d8d1d955 | 218 | SLOG(g_log << Logger::Warning << "Operating with single distributor/worker thread" << endl, |
d197c31c | 219 | log->info(Logr::Notice, "Operating with single distributor/worker thread")); |
3487974d | 220 | |
3487974d OM |
221 | /* This thread handles the web server, carbon, statistics and the control channel */ |
222 | auto& handlerInfo = RecThreadInfo::info(0); | |
223 | handlerInfo.setHandler(); | |
e4565646 | 224 | handlerInfo.start(0, "web+stat", cpusMap, log); |
3487974d OM |
225 | auto& taskInfo = RecThreadInfo::info(2); |
226 | taskInfo.setTaskThread(); | |
e4565646 | 227 | taskInfo.start(2, "taskThread", cpusMap, log); |
3487974d OM |
228 | |
229 | auto& info = RecThreadInfo::info(currentThreadId); | |
230 | info.setListener(); | |
231 | info.setWorker(); | |
232 | info.setThreadId(currentThreadId++); | |
233 | recursorThread(); | |
234 | ||
235 | handlerInfo.thread.join(); | |
236 | if (handlerInfo.exitCode != 0) { | |
237 | ret = handlerInfo.exitCode; | |
238 | } | |
239 | taskInfo.thread.join(); | |
240 | if (taskInfo.exitCode != 0) { | |
241 | ret = taskInfo.exitCode; | |
242 | } | |
243 | } | |
244 | else { | |
245 | // Setup RecThreadInfo objects | |
246 | unsigned int tmp = currentThreadId; | |
247 | if (RecThreadInfo::weDistributeQueries()) { | |
248 | for (unsigned int n = 0; n < RecThreadInfo::numDistributors(); ++n) { | |
249 | RecThreadInfo::info(tmp++).setListener(); | |
250 | } | |
251 | } | |
252 | for (unsigned int n = 0; n < RecThreadInfo::numWorkers(); ++n) { | |
253 | auto& info = RecThreadInfo::info(tmp++); | |
254 | info.setListener(!RecThreadInfo::weDistributeQueries()); | |
255 | info.setWorker(); | |
256 | } | |
257 | for (unsigned int n = 0; n < RecThreadInfo::numTaskThreads(); ++n) { | |
258 | auto& info = RecThreadInfo::info(tmp++); | |
259 | info.setTaskThread(); | |
260 | } | |
261 | ||
262 | // And now start the actual threads | |
263 | if (RecThreadInfo::weDistributeQueries()) { | |
d8d1d955 | 264 | SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numDistributors() << " distributor threads" << endl, |
d197c31c | 265 | log->info(Logr::Notice, "Launching distributor threads", "count", Logging::Loggable(RecThreadInfo::numDistributors()))); |
3487974d OM |
266 | for (unsigned int n = 0; n < RecThreadInfo::numDistributors(); ++n) { |
267 | auto& info = RecThreadInfo::info(currentThreadId); | |
e4565646 | 268 | info.start(currentThreadId++, "distr", cpusMap, log); |
3487974d OM |
269 | } |
270 | } | |
d8d1d955 | 271 | SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numWorkers() << " worker threads" << endl, |
d197c31c | 272 | log->info(Logr::Notice, "Launching worker threads", "count", Logging::Loggable(RecThreadInfo::numWorkers()))); |
3487974d OM |
273 | |
274 | for (unsigned int n = 0; n < RecThreadInfo::numWorkers(); ++n) { | |
275 | auto& info = RecThreadInfo::info(currentThreadId); | |
e4565646 | 276 | info.start(currentThreadId++, "worker", cpusMap, log); |
3487974d OM |
277 | } |
278 | ||
279 | for (unsigned int n = 0; n < RecThreadInfo::numTaskThreads(); ++n) { | |
280 | auto& info = RecThreadInfo::info(currentThreadId); | |
e4565646 | 281 | info.start(currentThreadId++, "taskThread", cpusMap, log); |
3487974d OM |
282 | } |
283 | ||
3487974d OM |
284 | /* This thread handles the web server, carbon, statistics and the control channel */ |
285 | auto& info = RecThreadInfo::info(0); | |
286 | info.setHandler(); | |
e4565646 | 287 | info.start(0, "web+stat", cpusMap, log); |
3487974d OM |
288 | |
289 | for (auto& ti : RecThreadInfo::infos()) { | |
290 | ti.thread.join(); | |
291 | if (ti.exitCode != 0) { | |
292 | ret = ti.exitCode; | |
293 | } | |
294 | } | |
295 | } | |
296 | return ret; | |
297 | } | |
298 | ||
d61b8a01 | 299 | void RecThreadInfo::makeThreadPipes(Logr::log_t log) |
3487974d OM |
300 | { |
301 | auto pipeBufferSize = ::arg().asNum("distribution-pipe-buffer-size"); | |
302 | if (pipeBufferSize > 0) { | |
e4565646 OM |
303 | SLOG(g_log << Logger::Info << "Resizing the buffer of the distribution pipe to " << pipeBufferSize << endl, |
304 | log->info(Logr::Info, "Resizing the buffer of the distribution pipe", "size", Logging::Loggable(pipeBufferSize))); | |
3487974d OM |
305 | } |
306 | ||
307 | /* thread 0 is the handler / SNMP, worker threads start at 1 */ | |
308 | for (unsigned int n = 0; n < numRecursorThreads(); ++n) { | |
309 | auto& threadInfo = info(n); | |
310 | ||
311 | int fd[2]; | |
312 | if (pipe(fd) < 0) | |
313 | unixDie("Creating pipe for inter-thread communications"); | |
314 | ||
315 | threadInfo.pipes.readToThread = fd[0]; | |
316 | threadInfo.pipes.writeToThread = fd[1]; | |
317 | ||
318 | // handler thread only gets first pipe, not the others | |
319 | if (n == 0) { | |
320 | continue; | |
321 | } | |
322 | ||
323 | if (pipe(fd) < 0) | |
324 | unixDie("Creating pipe for inter-thread communications"); | |
325 | ||
326 | threadInfo.pipes.readFromThread = fd[0]; | |
327 | threadInfo.pipes.writeFromThread = fd[1]; | |
328 | ||
329 | if (pipe(fd) < 0) | |
330 | unixDie("Creating pipe for inter-thread communications"); | |
331 | ||
332 | threadInfo.pipes.readQueriesToThread = fd[0]; | |
333 | threadInfo.pipes.writeQueriesToThread = fd[1]; | |
334 | ||
335 | if (pipeBufferSize > 0) { | |
336 | if (!setPipeBufferSize(threadInfo.pipes.writeQueriesToThread, pipeBufferSize)) { | |
337 | int err = errno; | |
e4565646 OM |
338 | SLOG(g_log << Logger::Warning << "Error resizing the buffer of the distribution pipe for thread " << n << " to " << pipeBufferSize << ": " << strerror(err) << endl, |
339 | log->error(Logr::Warning, err, "Error resizing the buffer of the distribution pipe for thread", "thread", Logging::Loggable(n), "size", Logging::Loggable(pipeBufferSize))); | |
3487974d OM |
340 | auto existingSize = getPipeBufferSize(threadInfo.pipes.writeQueriesToThread); |
341 | if (existingSize > 0) { | |
e4565646 OM |
342 | SLOG(g_log << Logger::Warning << "The current size of the distribution pipe's buffer for thread " << n << " is " << existingSize << endl, |
343 | log->info(Logr::Warning, "The current size of the distribution pipe's buffer for thread", "thread", Logging::Loggable(n), "size", Logging::Loggable(existingSize))); | |
3487974d OM |
344 | } |
345 | } | |
346 | } | |
347 | ||
348 | if (!setNonBlocking(threadInfo.pipes.writeQueriesToThread)) { | |
349 | unixDie("Making pipe for inter-thread communications non-blocking"); | |
350 | } | |
351 | } | |
352 | } | |
353 | ||
8c1cb8aa | 354 | ArgvMap& arg() |
eeaa1cb6 OM |
355 | { |
356 | static ArgvMap theArg; | |
357 | return theArg; | |
358 | } | |
359 | ||
d61b8a01 | 360 | static FDMultiplexer* getMultiplexer(Logr::log_t log) |
eeaa1cb6 OM |
361 | { |
362 | FDMultiplexer* ret; | |
8c1cb8aa | 363 | for (const auto& i : FDMultiplexer::getMultiplexerMap()) { |
eeaa1cb6 | 364 | try { |
7c58a81f | 365 | ret = i.second(FDMultiplexer::s_maxevents); |
eeaa1cb6 OM |
366 | return ret; |
367 | } | |
8c1cb8aa | 368 | catch (FDMultiplexerException& fe) { |
37c3c693 OM |
369 | SLOG(g_log << Logger::Error << "Non-fatal error initializing possible multiplexer (" << fe.what() << "), falling back" << endl, |
370 | log->error(Logr::Error, fe.what(), "Non-fatal error initializing possible multiplexer, falling back")); | |
eeaa1cb6 | 371 | } |
8c1cb8aa | 372 | catch (...) { |
37c3c693 OM |
373 | SLOG(g_log << Logger::Error << "Non-fatal error initializing possible multiplexer" << endl, |
374 | log->info(Logr::Error, "Non-fatal error initializing possible multiplexer")); | |
eeaa1cb6 OM |
375 | } |
376 | } | |
37c3c693 OM |
377 | SLOG(g_log << Logger::Error << "No working multiplexer found!" << endl, |
378 | log->info(Logr::Error, "No working multiplexer found!")); | |
eeaa1cb6 OM |
379 | _exit(1); |
380 | } | |
381 | ||
62b191dc | 382 | static std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>> startProtobufServers(const ProtobufExportConfig& config, Logr::log_t log) |
eeaa1cb6 OM |
383 | { |
384 | auto result = std::make_shared<std::vector<std::unique_ptr<RemoteLogger>>>(); | |
385 | ||
386 | for (const auto& server : config.servers) { | |
387 | try { | |
8c1cb8aa | 388 | auto logger = make_unique<RemoteLogger>(server, config.timeout, 100 * config.maxQueuedEntries, config.reconnectWaitTime, config.asyncConnect); |
eeaa1cb6 OM |
389 | logger->setLogQueries(config.logQueries); |
390 | logger->setLogResponses(config.logResponses); | |
391 | result->emplace_back(std::move(logger)); | |
392 | } | |
8c1cb8aa | 393 | catch (const std::exception& e) { |
62b191dc OM |
394 | SLOG(g_log << Logger::Error << "Error while starting protobuf logger to '" << server << ": " << e.what() << endl, |
395 | log->error(Logr::Error, e.what(), "Exception while starting protobuf logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server))); | |
eeaa1cb6 | 396 | } |
8c1cb8aa | 397 | catch (const PDNSException& e) { |
62b191dc OM |
398 | SLOG(g_log << Logger::Error << "Error while starting protobuf logger to '" << server << ": " << e.reason << endl, |
399 | log->error(Logr::Error, e.reason, "Exception while starting protobuf logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server))); | |
eeaa1cb6 OM |
400 | } |
401 | } | |
402 | ||
403 | return result; | |
404 | } | |
405 | ||
406 | bool checkProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal) | |
407 | { | |
408 | if (!luaconfsLocal->protobufExportConfig.enabled) { | |
babe9430 OM |
409 | if (t_protobufServers.servers) { |
410 | t_protobufServers.servers.reset(); | |
411 | t_protobufServers.config = luaconfsLocal->protobufExportConfig; | |
eeaa1cb6 OM |
412 | } |
413 | ||
414 | return false; | |
415 | } | |
416 | ||
417 | /* if the server was not running, or if it was running according to a | |
418 | previous configuration */ | |
babe9430 | 419 | if (t_protobufServers.generation < luaconfsLocal->generation && t_protobufServers.config != luaconfsLocal->protobufExportConfig) { |
eeaa1cb6 | 420 | |
babe9430 OM |
421 | if (t_protobufServers.servers) { |
422 | t_protobufServers.servers.reset(); | |
eeaa1cb6 | 423 | } |
62b191dc | 424 | auto log = g_slog->withName("protobuf"); |
babe9430 OM |
425 | t_protobufServers.servers = startProtobufServers(luaconfsLocal->protobufExportConfig, log); |
426 | t_protobufServers.config = luaconfsLocal->protobufExportConfig; | |
427 | t_protobufServers.generation = luaconfsLocal->generation; | |
eeaa1cb6 OM |
428 | } |
429 | ||
430 | return true; | |
431 | } | |
432 | ||
433 | bool checkOutgoingProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal) | |
434 | { | |
435 | if (!luaconfsLocal->outgoingProtobufExportConfig.enabled) { | |
babe9430 OM |
436 | if (t_outgoingProtobufServers.servers) { |
437 | t_outgoingProtobufServers.servers.reset(); | |
438 | t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig; | |
eeaa1cb6 | 439 | } |
eeaa1cb6 OM |
440 | |
441 | return false; | |
442 | } | |
443 | ||
444 | /* if the server was not running, or if it was running according to a | |
445 | previous configuration */ | |
babe9430 | 446 | if (t_outgoingProtobufServers.generation < luaconfsLocal->generation && t_outgoingProtobufServers.config != luaconfsLocal->outgoingProtobufExportConfig) { |
eeaa1cb6 | 447 | |
babe9430 OM |
448 | if (t_outgoingProtobufServers.servers) { |
449 | t_outgoingProtobufServers.servers.reset(); | |
eeaa1cb6 | 450 | } |
62b191dc | 451 | auto log = g_slog->withName("protobuf"); |
babe9430 OM |
452 | t_outgoingProtobufServers.servers = startProtobufServers(luaconfsLocal->outgoingProtobufExportConfig, log); |
453 | t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig; | |
454 | t_outgoingProtobufServers.generation = luaconfsLocal->generation; | |
eeaa1cb6 OM |
455 | } |
456 | ||
457 | return true; | |
458 | } | |
459 | ||
e81063e5 | 460 | void protobufLogQuery(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const ComboAddress& mappedRemote, const Netmask& ednssubnet, bool tcp, uint16_t id, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::unordered_set<std::string>& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName, const std::map<std::string, RecursorLua4::MetaValue>& meta) |
eeaa1cb6 | 461 | { |
babe9430 OM |
462 | auto log = g_slog->withName("pblq"); |
463 | ||
464 | if (!t_protobufServers.servers) { | |
eeaa1cb6 OM |
465 | return; |
466 | } | |
467 | ||
e81063e5 OM |
468 | ComboAddress requestor; |
469 | if (!luaconfsLocal->protobufExportConfig.logMappedFrom) { | |
470 | Netmask requestorNM(remote, remote.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); | |
471 | requestor = requestorNM.getMaskedNetwork(); | |
472 | requestor.setPort(remote.getPort()); | |
473 | } | |
474 | else { | |
475 | Netmask requestorNM(mappedRemote, mappedRemote.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); | |
476 | requestor = requestorNM.getMaskedNetwork(); | |
477 | requestor.setPort(mappedRemote.getPort()); | |
478 | } | |
eeaa1cb6 OM |
479 | |
480 | pdns::ProtoZero::RecMessage m{128, std::string::size_type(policyTags.empty() ? 0 : 64)}; // It's a guess | |
481 | m.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType); | |
482 | m.setRequest(uniqueId, requestor, local, qname, qtype, qclass, id, tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP, len); | |
483 | m.setServerIdentity(SyncRes::s_serverID); | |
484 | m.setEDNSSubnet(ednssubnet, ednssubnet.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); | |
485 | m.setRequestorId(requestorId); | |
486 | m.setDeviceId(deviceId); | |
487 | m.setDeviceName(deviceName); | |
488 | ||
489 | if (!policyTags.empty()) { | |
490 | m.addPolicyTags(policyTags); | |
491 | } | |
492 | for (const auto& mit : meta) { | |
493 | m.setMeta(mit.first, mit.second.stringVal, mit.second.intVal); | |
494 | } | |
495 | ||
496 | std::string msg(m.finishAndMoveBuf()); | |
babe9430 | 497 | for (auto& server : *t_protobufServers.servers) { |
4d7db3d7 | 498 | remoteLoggerQueueData(*server, msg); |
eeaa1cb6 OM |
499 | } |
500 | } | |
501 | ||
502 | void protobufLogResponse(pdns::ProtoZero::RecMessage& message) | |
503 | { | |
babe9430 | 504 | if (!t_protobufServers.servers) { |
eeaa1cb6 OM |
505 | return; |
506 | } | |
507 | ||
508 | std::string msg(message.finishAndMoveBuf()); | |
babe9430 | 509 | for (auto& server : *t_protobufServers.servers) { |
4d7db3d7 | 510 | remoteLoggerQueueData(*server, msg); |
eeaa1cb6 OM |
511 | } |
512 | } | |
513 | ||
514 | void protobufLogResponse(const struct dnsheader* dh, LocalStateHolder<LuaConfigItems>& luaconfsLocal, | |
8c1cb8aa OM |
515 | const RecursorPacketCache::OptPBData& pbData, const struct timeval& tv, |
516 | bool tcp, const ComboAddress& source, const ComboAddress& destination, | |
e81063e5 | 517 | const ComboAddress& mappedSource, |
8c1cb8aa OM |
518 | const EDNSSubnetOpts& ednssubnet, |
519 | const boost::uuids::uuid& uniqueId, const string& requestorId, const string& deviceId, | |
520 | const string& deviceName, const std::map<std::string, RecursorLua4::MetaValue>& meta, | |
521 | const RecEventTrace& eventTrace) | |
eeaa1cb6 OM |
522 | { |
523 | pdns::ProtoZero::RecMessage pbMessage(pbData ? pbData->d_message : "", pbData ? pbData->d_response : "", 64, 10); // The extra bytes we are going to add | |
524 | // Normally we take the immutable string from the cache and append a few values, but if it's not there (can this happen?) | |
525 | // we start with an empty string and append the minimal | |
526 | if (!pbData) { | |
527 | pbMessage.setType(pdns::ProtoZero::Message::MessageType::DNSResponseType); | |
528 | pbMessage.setServerIdentity(SyncRes::s_serverID); | |
529 | } | |
530 | ||
531 | // In response part | |
532 | if (g_useKernelTimestamp && tv.tv_sec) { | |
533 | pbMessage.setQueryTime(tv.tv_sec, tv.tv_usec); | |
534 | } | |
535 | else { | |
536 | pbMessage.setQueryTime(g_now.tv_sec, g_now.tv_usec); | |
537 | } | |
538 | ||
539 | // In message part | |
e81063e5 OM |
540 | if (!luaconfsLocal->protobufExportConfig.logMappedFrom) { |
541 | Netmask requestorNM(source, source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); | |
542 | auto requestor = requestorNM.getMaskedNetwork(); | |
543 | pbMessage.setFrom(requestor); | |
544 | pbMessage.setFromPort(source.getPort()); | |
545 | } | |
546 | else { | |
547 | Netmask requestorNM(mappedSource, mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); | |
548 | auto requestor = requestorNM.getMaskedNetwork(); | |
549 | pbMessage.setFrom(requestor); | |
550 | pbMessage.setFromPort(mappedSource.getPort()); | |
551 | } | |
eeaa1cb6 | 552 | pbMessage.setMessageIdentity(uniqueId); |
eeaa1cb6 OM |
553 | pbMessage.setTo(destination); |
554 | pbMessage.setSocketProtocol(tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP); | |
555 | pbMessage.setId(dh->id); | |
556 | ||
557 | pbMessage.setTime(); | |
558 | pbMessage.setEDNSSubnet(ednssubnet.source, ednssubnet.source.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); | |
559 | pbMessage.setRequestorId(requestorId); | |
560 | pbMessage.setDeviceId(deviceId); | |
561 | pbMessage.setDeviceName(deviceName); | |
eeaa1cb6 OM |
562 | pbMessage.setToPort(destination.getPort()); |
563 | for (const auto& m : meta) { | |
564 | pbMessage.setMeta(m.first, m.second.stringVal, m.second.intVal); | |
565 | } | |
566 | #ifdef NOD_ENABLED | |
567 | if (g_nodEnabled) { | |
568 | pbMessage.setNewlyObservedDomain(false); | |
569 | } | |
570 | #endif | |
571 | if (eventTrace.enabled() && SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_pb) { | |
572 | pbMessage.addEvents(eventTrace); | |
573 | } | |
574 | protobufLogResponse(pbMessage); | |
575 | } | |
576 | ||
577 | #ifdef HAVE_FSTRM | |
578 | ||
62b191dc | 579 | static std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>> startFrameStreamServers(const FrameStreamExportConfig& config, Logr::log_t log) |
eeaa1cb6 OM |
580 | { |
581 | auto result = std::make_shared<std::vector<std::unique_ptr<FrameStreamLogger>>>(); | |
582 | ||
583 | for (const auto& server : config.servers) { | |
584 | try { | |
8c1cb8aa | 585 | std::unordered_map<string, unsigned> options; |
eeaa1cb6 OM |
586 | options["bufferHint"] = config.bufferHint; |
587 | options["flushTimeout"] = config.flushTimeout; | |
588 | options["inputQueueSize"] = config.inputQueueSize; | |
589 | options["outputQueueSize"] = config.outputQueueSize; | |
590 | options["queueNotifyThreshold"] = config.queueNotifyThreshold; | |
591 | options["reopenInterval"] = config.reopenInterval; | |
8c1cb8aa | 592 | FrameStreamLogger* fsl = nullptr; |
eeaa1cb6 OM |
593 | try { |
594 | ComboAddress address(server); | |
595 | fsl = new FrameStreamLogger(address.sin4.sin_family, address.toStringWithPort(), true, options); | |
596 | } | |
597 | catch (const PDNSException& e) { | |
598 | fsl = new FrameStreamLogger(AF_UNIX, server, true, options); | |
599 | } | |
600 | fsl->setLogQueries(config.logQueries); | |
601 | fsl->setLogResponses(config.logResponses); | |
9489e2b5 CHB |
602 | fsl->setLogNODs(config.logNODs); |
603 | fsl->setLogUDRs(config.logUDRs); | |
eeaa1cb6 OM |
604 | result->emplace_back(fsl); |
605 | } | |
8c1cb8aa | 606 | catch (const std::exception& e) { |
62b191dc OM |
607 | SLOG(g_log << Logger::Error << "Error while starting dnstap framestream logger to '" << server << ": " << e.what() << endl, |
608 | log->error(Logr::Error, e.what(), "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server))); | |
eeaa1cb6 | 609 | } |
8c1cb8aa | 610 | catch (const PDNSException& e) { |
62b191dc OM |
611 | SLOG(g_log << Logger::Error << "Error while starting dnstap framestream logger to '" << server << ": " << e.reason << endl, |
612 | log->error(Logr::Error, e.reason, "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server))); | |
eeaa1cb6 OM |
613 | } |
614 | } | |
615 | ||
616 | return result; | |
617 | } | |
618 | ||
2e0757d5 FM |
619 | static void asyncFrameStreamLoggersCleanup(std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>&& servers) |
620 | { | |
621 | auto thread = std::thread([&] { | |
622 | servers.reset(); | |
623 | }); | |
624 | thread.detach(); | |
625 | } | |
626 | ||
9489e2b5 | 627 | bool checkFrameStreamExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const FrameStreamExportConfig& config, FrameStreamServersInfo& serverInfos) |
eeaa1cb6 | 628 | { |
9489e2b5 CHB |
629 | if (!config.enabled) { |
630 | if (serverInfos.servers) { | |
eeaa1cb6 | 631 | // dt's take care of cleanup |
9489e2b5 CHB |
632 | asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers)); |
633 | serverInfos.config = config; | |
eeaa1cb6 OM |
634 | } |
635 | ||
636 | return false; | |
637 | } | |
638 | ||
afaf1b5d FM |
639 | /* if the server was not running, or if it was running according to a previous |
640 | * configuration | |
641 | */ | |
9489e2b5 CHB |
642 | if (serverInfos.generation < luaconfsLocal->generation && serverInfos.config != config) { |
643 | if (serverInfos.servers) { | |
eeaa1cb6 | 644 | // dt's take care of cleanup |
9489e2b5 | 645 | asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers)); |
eeaa1cb6 OM |
646 | } |
647 | ||
afaf1b5d | 648 | auto dnsTapLog = g_slog->withName("dnstap"); |
9489e2b5 CHB |
649 | serverInfos.servers = startFrameStreamServers(config, dnsTapLog); |
650 | serverInfos.config = config; | |
651 | serverInfos.generation = luaconfsLocal->generation; | |
eeaa1cb6 OM |
652 | } |
653 | ||
654 | return true; | |
655 | } | |
9489e2b5 | 656 | |
eeaa1cb6 OM |
657 | #endif /* HAVE_FSTRM */ |
658 | ||
8c1cb8aa | 659 | static void makeControlChannelSocket(int processNum = -1) |
eeaa1cb6 | 660 | { |
b35191d7 | 661 | string sockname = ::arg()["socket-dir"] + "/" + g_programname; |
8c1cb8aa OM |
662 | if (processNum >= 0) |
663 | sockname += "." + std::to_string(processNum); | |
664 | sockname += ".controlsocket"; | |
b35191d7 | 665 | g_rcc.listen(sockname); |
eeaa1cb6 OM |
666 | |
667 | int sockowner = -1; | |
668 | int sockgroup = -1; | |
669 | ||
670 | if (!::arg().isEmpty("socket-group")) | |
8c1cb8aa | 671 | sockgroup = ::arg().asGid("socket-group"); |
eeaa1cb6 | 672 | if (!::arg().isEmpty("socket-owner")) |
8c1cb8aa | 673 | sockowner = ::arg().asUid("socket-owner"); |
eeaa1cb6 OM |
674 | |
675 | if (sockgroup > -1 || sockowner > -1) { | |
8c1cb8aa | 676 | if (chown(sockname.c_str(), sockowner, sockgroup) < 0) { |
eeaa1cb6 OM |
677 | unixDie("Failed to chown control socket"); |
678 | } | |
679 | } | |
680 | ||
681 | // do mode change if socket-mode is given | |
8c1cb8aa OM |
682 | if (!::arg().isEmpty("socket-mode")) { |
683 | mode_t sockmode = ::arg().asMode("socket-mode"); | |
684 | if (chmod(sockname.c_str(), sockmode) < 0) { | |
eeaa1cb6 OM |
685 | unixDie("Failed to chmod control socket"); |
686 | } | |
687 | } | |
688 | } | |
689 | ||
d61b8a01 | 690 | static void writePid(Logr::log_t log) |
eeaa1cb6 | 691 | { |
8c1cb8aa | 692 | if (!::arg().mustDo("write-pid")) |
eeaa1cb6 | 693 | return; |
b35191d7 | 694 | ofstream of(g_pidfname.c_str(), std::ios_base::app); |
8c1cb8aa OM |
695 | if (of) |
696 | of << Utility::getpid() << endl; | |
eeaa1cb6 OM |
697 | else { |
698 | int err = errno; | |
37c3c693 OM |
699 | SLOG(g_log << Logger::Error << "Writing pid for " << Utility::getpid() << " to " << g_pidfname << " failed: " << stringerror(err) << endl, |
700 | log->error(Logr::Error, err, "Writing pid failed", "pid", Logging::Loggable(Utility::getpid()), "file", Logging::Loggable(g_pidfname))); | |
eeaa1cb6 OM |
701 | } |
702 | } | |
703 | ||
d61b8a01 | 704 | static void checkSocketDir(Logr::log_t log) |
eeaa1cb6 | 705 | { |
eeaa1cb6 OM |
706 | string dir(::arg()["socket-dir"]); |
707 | string msg; | |
708 | ||
7d7a0749 FM |
709 | struct stat dirStat = {}; |
710 | if (stat(dir.c_str(), &dirStat) == -1) { | |
eeaa1cb6 OM |
711 | msg = "it does not exist or cannot access"; |
712 | } | |
7d7a0749 | 713 | else if (!S_ISDIR(dirStat.st_mode)) { |
eeaa1cb6 OM |
714 | msg = "it is not a directory"; |
715 | } | |
8c1cb8aa | 716 | else if (access(dir.c_str(), R_OK | W_OK | X_OK) != 0) { |
eeaa1cb6 | 717 | msg = "cannot read, write or search"; |
8c1cb8aa OM |
718 | } |
719 | else { | |
eeaa1cb6 OM |
720 | return; |
721 | } | |
6a6f29c9 | 722 | dir = ::arg()["chroot"] + dir; |
37c3c693 | 723 | SLOG(g_log << Logger::Error << "Problem with socket directory " << dir << ": " << msg << "; see https://docs.powerdns.com/recursor/upgrade.html#x-to-4-3-0" << endl, |
7d7a0749 | 724 | log->error(Logr::Error, msg, "Problem with socket directory, see https://docs.powerdns.com/recursor/upgrade.html#x-to-4-3-0", "dir", Logging::Loggable(dir))); |
eeaa1cb6 OM |
725 | _exit(1); |
726 | } | |
727 | ||
728 | #ifdef NOD_ENABLED | |
d61b8a01 | 729 | static void setupNODThread(Logr::log_t log) |
eeaa1cb6 OM |
730 | { |
731 | if (g_nodEnabled) { | |
732 | uint32_t num_cells = ::arg().asNum("new-domain-db-size"); | |
733 | t_nodDBp = std::make_shared<nod::NODDB>(num_cells); | |
734 | try { | |
735 | t_nodDBp->setCacheDir(::arg()["new-domain-history-dir"]); | |
736 | } | |
737 | catch (const PDNSException& e) { | |
37c3c693 OM |
738 | SLOG(g_log << Logger::Error << "new-domain-history-dir (" << ::arg()["new-domain-history-dir"] << ") is not readable or does not exist" << endl, |
739 | log->error(Logr::Error, e.reason, "new-domain-history-dir is not readbale or does not exists", "dir", Logging::Loggable(::arg()["new-domain-history-dir"]))); | |
eeaa1cb6 OM |
740 | _exit(1); |
741 | } | |
742 | if (!t_nodDBp->init()) { | |
37c3c693 OM |
743 | SLOG(g_log << Logger::Error << "Could not initialize domain tracking" << endl, |
744 | log->info(Logr::Error, "Could not initialize domain tracking")); | |
eeaa1cb6 OM |
745 | _exit(1); |
746 | } | |
747 | std::thread t(nod::NODDB::startHousekeepingThread, t_nodDBp, std::this_thread::get_id()); | |
748 | t.detach(); | |
749 | g_nod_pbtag = ::arg()["new-domain-pb-tag"]; | |
750 | } | |
751 | if (g_udrEnabled) { | |
752 | uint32_t num_cells = ::arg().asNum("unique-response-db-size"); | |
753 | t_udrDBp = std::make_shared<nod::UniqueResponseDB>(num_cells); | |
754 | try { | |
755 | t_udrDBp->setCacheDir(::arg()["unique-response-history-dir"]); | |
756 | } | |
757 | catch (const PDNSException& e) { | |
37c3c693 OM |
758 | SLOG(g_log << Logger::Error << "unique-response-history-dir (" << ::arg()["unique-response-history-dir"] << ") is not readable or does not exist" << endl, |
759 | log->info(Logr::Error, "unique-response-history-dir is not readable or does not exist", "dir", Logging::Loggable(::arg()["unique-response-history-dir"]))); | |
eeaa1cb6 OM |
760 | _exit(1); |
761 | } | |
762 | if (!t_udrDBp->init()) { | |
37c3c693 OM |
763 | SLOG(g_log << Logger::Error << "Could not initialize unique response tracking" << endl, |
764 | log->info(Logr::Error, "Could not initialize unique response tracking")); | |
eeaa1cb6 OM |
765 | _exit(1); |
766 | } | |
767 | std::thread t(nod::UniqueResponseDB::startHousekeepingThread, t_udrDBp, std::this_thread::get_id()); | |
768 | t.detach(); | |
769 | g_udr_pbtag = ::arg()["unique-response-pb-tag"]; | |
770 | } | |
771 | } | |
772 | ||
773 | static void parseNODIgnorelist(const std::string& wlist) | |
774 | { | |
775 | vector<string> parts; | |
776 | stringtok(parts, wlist, ",; "); | |
8c1cb8aa | 777 | for (const auto& a : parts) { |
eeaa1cb6 OM |
778 | g_nodDomainWL.add(DNSName(a)); |
779 | } | |
780 | } | |
781 | ||
782 | static void setupNODGlobal() | |
783 | { | |
784 | // Setup NOD subsystem | |
785 | g_nodEnabled = ::arg().mustDo("new-domain-tracking"); | |
786 | g_nodLookupDomain = DNSName(::arg()["new-domain-lookup"]); | |
787 | g_nodLog = ::arg().mustDo("new-domain-log"); | |
788 | parseNODIgnorelist(::arg()["new-domain-whitelist"]); | |
789 | parseNODIgnorelist(::arg()["new-domain-ignore-list"]); | |
790 | ||
791 | // Setup Unique DNS Response subsystem | |
792 | g_udrEnabled = ::arg().mustDo("unique-response-tracking"); | |
793 | g_udrLog = ::arg().mustDo("unique-response-log"); | |
794 | } | |
795 | #endif /* NOD_ENABLED */ | |
796 | ||
d61b8a01 | 797 | static void daemonize(Logr::log_t log) |
eeaa1cb6 | 798 | { |
8c1cb8aa | 799 | if (fork()) |
eeaa1cb6 OM |
800 | exit(0); // bye bye |
801 | ||
802 | setsid(); | |
803 | ||
8c1cb8aa | 804 | int i = open("/dev/null", O_RDWR); /* open stdin */ |
37c3c693 OM |
805 | if (i < 0) { |
806 | int err = errno; | |
807 | SLOG(g_log << Logger::Critical << "Unable to open /dev/null: " << stringerror(err) << endl, | |
808 | log->error(Logr::Critical, err, "Unable to open /dev/null")); | |
809 | } | |
eeaa1cb6 | 810 | else { |
8c1cb8aa OM |
811 | dup2(i, 0); /* stdin */ |
812 | dup2(i, 1); /* stderr */ | |
813 | dup2(i, 2); /* stderr */ | |
eeaa1cb6 OM |
814 | close(i); |
815 | } | |
816 | } | |
817 | ||
818 | static void termIntHandler(int) | |
819 | { | |
820 | doExit(); | |
821 | } | |
822 | ||
823 | static void usr1Handler(int) | |
824 | { | |
8c1cb8aa | 825 | statsWanted = true; |
eeaa1cb6 OM |
826 | } |
827 | ||
828 | static void usr2Handler(int) | |
829 | { | |
8c1cb8aa | 830 | g_quiet = !g_quiet; |
eeaa1cb6 | 831 | SyncRes::setDefaultLogMode(g_quiet ? SyncRes::LogNone : SyncRes::Log); |
8c1cb8aa | 832 | ::arg().set("quiet") = g_quiet ? "" : "no"; |
eeaa1cb6 OM |
833 | } |
834 | ||
d61b8a01 | 835 | static void checkLinuxIPv6Limits(Logr::log_t log) |
eeaa1cb6 OM |
836 | { |
837 | #ifdef __linux__ | |
838 | string line; | |
8c1cb8aa OM |
839 | if (readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line)) { |
840 | int lim = std::stoi(line); | |
841 | if (lim < 16384) { | |
37c3c693 OM |
842 | SLOG(g_log << Logger::Error << "If using IPv6, please raise sysctl net.ipv6.route.max_size, currently set to " << lim << " which is < 16384" << endl, |
843 | log->info(Logr::Error, "If using IPv6, please raise sysctl net.ipv6.route.max_size to a size >= 16384", "current", Logging::Loggable(lim))); | |
eeaa1cb6 OM |
844 | } |
845 | } | |
846 | #endif | |
847 | } | |
b35191d7 | 848 | |
d61b8a01 | 849 | static void checkOrFixFDS(Logr::log_t log) |
eeaa1cb6 | 850 | { |
8c1cb8aa | 851 | unsigned int availFDs = getFilenumLimit(); |
048607b4 OM |
852 | unsigned int wantFDs = g_maxMThreads * RecThreadInfo::numWorkers() + 25; // even healthier margin then before |
853 | wantFDs += RecThreadInfo::numWorkers() * TCPOutConnectionManager::s_maxIdlePerThread; | |
eeaa1cb6 | 854 | |
8c1cb8aa OM |
855 | if (wantFDs > availFDs) { |
856 | unsigned int hardlimit = getFilenumLimit(true); | |
857 | if (hardlimit >= wantFDs) { | |
eeaa1cb6 | 858 | setFilenumLimit(wantFDs); |
37c3c693 OM |
859 | SLOG(g_log << Logger::Warning << "Raised soft limit on number of filedescriptors to " << wantFDs << " to match max-mthreads and threads settings" << endl, |
860 | log->info(Logr::Warning, "Raised soft limit on number of filedescriptors to match max-mthreads and threads settings", "limit", Logging::Loggable(wantFDs))); | |
eeaa1cb6 OM |
861 | } |
862 | else { | |
048607b4 | 863 | int newval = (hardlimit - 25 - TCPOutConnectionManager::s_maxIdlePerThread) / RecThreadInfo::numWorkers(); |
37c3c693 OM |
864 | SLOG(g_log << Logger::Warning << "Insufficient number of filedescriptors available for max-mthreads*threads setting! (" << hardlimit << " < " << wantFDs << "), reducing max-mthreads to " << newval << endl, |
865 | log->info(Logr::Warning, "Insufficient number of filedescriptors available for max-mthreads*threads setting! Reducing max-mthreads", "hardlimit", Logging::Loggable(hardlimit), "want", Logging::Loggable(wantFDs), "max-mthreads", Logging::Loggable(newval))); | |
eeaa1cb6 OM |
866 | g_maxMThreads = newval; |
867 | setFilenumLimit(hardlimit); | |
868 | } | |
869 | } | |
870 | } | |
b35191d7 | 871 | |
8c1cb8aa | 872 | // static std::string s_timestampFormat = "%m-%dT%H:%M:%S"; |
eeaa1cb6 OM |
873 | static std::string s_timestampFormat = "%s"; |
874 | ||
8c1cb8aa | 875 | static const char* toTimestampStringMilli(const struct timeval& tv, char* buf, size_t sz) |
eeaa1cb6 | 876 | { |
68b1e632 OM |
877 | size_t len = 0; |
878 | if (s_timestampFormat != "%s") { | |
879 | // strftime is not thread safe, it can access locale information | |
880 | static std::mutex m; | |
881 | auto lock = std::lock_guard(m); | |
882 | struct tm tm; | |
883 | len = strftime(buf, sz, s_timestampFormat.c_str(), localtime_r(&tv.tv_sec, &tm)); | |
884 | } | |
eeaa1cb6 OM |
885 | if (len == 0) { |
886 | len = snprintf(buf, sz, "%lld", static_cast<long long>(tv.tv_sec)); | |
887 | } | |
888 | ||
889 | snprintf(buf + len, sz - len, ".%03ld", static_cast<long>(tv.tv_usec) / 1000); | |
890 | return buf; | |
891 | } | |
892 | ||
0112056b OM |
893 | #ifdef HAVE_SYSTEMD |
894 | static void loggerSDBackend(const Logging::Entry& entry) | |
895 | { | |
2cff1a09 | 896 | // First map SL priority to syslog's Urgency |
1e5eef5e OM |
897 | Logger::Urgency u = entry.d_priority ? Logger::Urgency(entry.d_priority) : Logger::Info; |
898 | if (u > s_logUrgency) { | |
2cff1a09 OM |
899 | // We do not log anything if the Urgency of the message is lower than the requested loglevel. |
900 | // Not that lower Urgency means higher number. | |
1e5eef5e OM |
901 | return; |
902 | } | |
0112056b OM |
903 | // We need to keep the string in mem until sd_journal_sendv has ben called |
904 | vector<string> strings; | |
a2b3f98b | 905 | auto appendKeyAndVal = [&strings](const string& k, const string& v) { |
0112056b OM |
906 | strings.emplace_back(k + "=" + v); |
907 | }; | |
a2b3f98b | 908 | appendKeyAndVal("MESSAGE", entry.message); |
0112056b | 909 | if (entry.error) { |
a2b3f98b | 910 | appendKeyAndVal("ERROR", entry.error.get()); |
0112056b | 911 | } |
a2b3f98b OM |
912 | appendKeyAndVal("LEVEL", std::to_string(entry.level)); |
913 | appendKeyAndVal("PRIORITY", std::to_string(entry.d_priority)); | |
0112056b | 914 | if (entry.name) { |
a2b3f98b | 915 | appendKeyAndVal("SUBSYSTEM", entry.name.get()); |
0112056b OM |
916 | } |
917 | char timebuf[64]; | |
a2b3f98b | 918 | appendKeyAndVal("TIMESTAMP", toTimestampStringMilli(entry.d_timestamp, timebuf, sizeof(timebuf))); |
0112056b | 919 | for (auto const& v : entry.values) { |
a2b3f98b | 920 | appendKeyAndVal(toUpper(v.first), v.second); |
0112056b OM |
921 | } |
922 | // Thread id filled in by backend, since the SL code does not know about RecursorThreads | |
923 | // We use the Recursor thread, other threads get id 0. May need to revisit. | |
a2b3f98b | 924 | appendKeyAndVal("TID", std::to_string(RecThreadInfo::id())); |
0112056b OM |
925 | |
926 | vector<iovec> iov; | |
927 | iov.reserve(strings.size()); | |
928 | for (const auto& s : strings) { | |
929 | // iovec has no 2 arg constructor, so make it explicit | |
930 | iov.emplace_back(iovec{const_cast<void*>(reinterpret_cast<const void*>(s.data())), s.size()}); | |
931 | } | |
932 | sd_journal_sendv(iov.data(), static_cast<int>(iov.size())); | |
933 | } | |
934 | #endif | |
935 | ||
8c1cb8aa OM |
936 | static void loggerBackend(const Logging::Entry& entry) |
937 | { | |
eeaa1cb6 OM |
938 | static thread_local std::stringstream buf; |
939 | ||
2cff1a09 | 940 | // First map SL priority to syslog's Urgency |
1e5eef5e OM |
941 | Logger::Urgency u = entry.d_priority ? Logger::Urgency(entry.d_priority) : Logger::Info; |
942 | if (u > s_logUrgency) { | |
2cff1a09 OM |
943 | // We do not log anything if the Urgency of the message is lower than the requested loglevel. |
944 | // Not that lower Urgency means higher number. | |
1e5eef5e OM |
945 | return; |
946 | } | |
eeaa1cb6 OM |
947 | buf.str(""); |
948 | buf << "msg=" << std::quoted(entry.message); | |
949 | if (entry.error) { | |
d197c31c | 950 | buf << " error=" << std::quoted(entry.error.get()); |
eeaa1cb6 OM |
951 | } |
952 | ||
953 | if (entry.name) { | |
954 | buf << " subsystem=" << std::quoted(entry.name.get()); | |
955 | } | |
d197c31c | 956 | buf << " level=" << std::quoted(std::to_string(entry.level)); |
eeaa1cb6 | 957 | if (entry.d_priority) { |
d197c31c | 958 | buf << " prio=" << std::quoted(Logr::Logger::toString(entry.d_priority)); |
eeaa1cb6 | 959 | } |
0112056b OM |
960 | // Thread id filled in by backend, since the SL code does not know about RecursorThreads |
961 | // We use the Recursor thread, other threads get id 0. May need to revisit. | |
962 | buf << " tid=" << std::quoted(std::to_string(RecThreadInfo::id())); | |
eeaa1cb6 OM |
963 | char timebuf[64]; |
964 | buf << " ts=" << std::quoted(toTimestampStringMilli(entry.d_timestamp, timebuf, sizeof(timebuf))); | |
8c1cb8aa | 965 | for (auto const& v : entry.values) { |
eeaa1cb6 OM |
966 | buf << " "; |
967 | buf << v.first << "=" << std::quoted(v.second); | |
968 | } | |
1e5eef5e | 969 | |
eeaa1cb6 OM |
970 | g_log << u << buf.str() << endl; |
971 | } | |
b35191d7 | 972 | |
eeaa1cb6 OM |
973 | static int ratePercentage(uint64_t nom, uint64_t denom) |
974 | { | |
975 | if (denom == 0) { | |
976 | return 0; | |
977 | } | |
978 | return round(100.0 * nom / denom); | |
979 | } | |
980 | ||
981 | static void doStats(void) | |
982 | { | |
983 | static time_t lastOutputTime; | |
984 | static uint64_t lastQueryCount; | |
985 | ||
986 | uint64_t cacheHits = g_recCache->cacheHits; | |
987 | uint64_t cacheMisses = g_recCache->cacheMisses; | |
988 | uint64_t cacheSize = g_recCache->size(); | |
989 | auto rc_stats = g_recCache->stats(); | |
990 | double r = rc_stats.second == 0 ? 0.0 : (100.0 * rc_stats.first / rc_stats.second); | |
991 | uint64_t negCacheSize = g_negCache->size(); | |
992 | auto taskPushes = getTaskPushes(); | |
993 | auto taskExpired = getTaskExpired(); | |
994 | auto taskSize = getTaskSize(); | |
3ddcf687 OM |
995 | uint64_t pcSize = broadcastAccFunction<uint64_t>(pleaseGetPacketCacheSize); |
996 | uint64_t pcHits = broadcastAccFunction<uint64_t>(pleaseGetPacketCacheHits); | |
997 | ||
998 | auto log = g_slog->withName("stats"); | |
eeaa1cb6 | 999 | |
8c1cb8aa | 1000 | if (g_stats.qcounter && (cacheHits + cacheMisses) && SyncRes::s_queries && SyncRes::s_outqueries) { |
3ddcf687 OM |
1001 | if (!g_slogStructured) { |
1002 | g_log << Logger::Notice << "stats: " << g_stats.qcounter << " questions, " << cacheSize << " cache entries, " << negCacheSize << " negative entries, " << ratePercentage(cacheHits, cacheHits + cacheMisses) << "% cache hits" << endl; | |
1003 | g_log << Logger::Notice << "stats: cache contended/acquired " << rc_stats.first << '/' << rc_stats.second << " = " << r << '%' << endl; | |
1004 | ||
1005 | g_log << Logger::Notice << "stats: throttle map: " | |
1006 | << SyncRes::getThrottledServersSize() << ", ns speeds: " | |
1007 | << SyncRes::getNSSpeedsSize() << ", failed ns: " | |
1008 | << SyncRes::getFailedServersSize() << ", ednsmap: " | |
f6001854 | 1009 | << SyncRes::getEDNSStatusesSize() << ", non-resolving: " |
3ddcf687 OM |
1010 | << SyncRes::getNonResolvingNSSize() << ", saved-parentsets: " |
1011 | << SyncRes::getSaveParentsNSSetsSize() | |
1012 | << endl; | |
1013 | g_log << Logger::Notice << "stats: outpacket/query ratio " << ratePercentage(SyncRes::s_outqueries, SyncRes::s_queries) << "%"; | |
1014 | g_log << Logger::Notice << ", " << ratePercentage(SyncRes::s_throttledqueries, SyncRes::s_outqueries + SyncRes::s_throttledqueries) << "% throttled" << endl; | |
1015 | g_log << Logger::Notice << "stats: " << SyncRes::s_tcpoutqueries << "/" << SyncRes::s_dotoutqueries << "/" << getCurrentIdleTCPConnections() << " outgoing tcp/dot/idle connections, " << broadcastAccFunction<uint64_t>(pleaseGetConcurrentQueries) << " queries running, " << SyncRes::s_outgoingtimeouts << " outgoing timeouts " << endl; | |
eeaa1cb6 | 1016 | |
3ddcf687 | 1017 | g_log << Logger::Notice << "stats: " << pcSize << " packet cache entries, " << ratePercentage(pcHits, g_stats.qcounter) << "% packet cache hits" << endl; |
eeaa1cb6 | 1018 | |
3ddcf687 OM |
1019 | g_log << Logger::Notice << "stats: tasks pushed/expired/queuesize: " << taskPushes << '/' << taskExpired << '/' << taskSize << endl; |
1020 | } | |
1021 | else { | |
1022 | const string m = "Periodic statistics report"; | |
cbcd2a69 | 1023 | log->info(Logr::Info, m, |
3ddcf687 OM |
1024 | "questions", Logging::Loggable(g_stats.qcounter), |
1025 | "cache-entries", Logging::Loggable(cacheSize), | |
1026 | "negcache-entries", Logging::Loggable(negCacheSize), | |
1027 | "record-cache-hitratio-perc", Logging::Loggable(ratePercentage(cacheHits, cacheHits + cacheMisses)), | |
1028 | "record-cache-contended", Logging::Loggable(rc_stats.first), | |
1029 | "record-cache-acquired", Logging::Loggable(rc_stats.second), | |
1030 | "record-cache-contended-perc", Logging::Loggable(r)); | |
cbcd2a69 | 1031 | log->info(Logr::Info, m, |
ed164eb2 | 1032 | "throttle-entries", Logging::Loggable(SyncRes::getThrottledServersSize()), |
3ddcf687 OM |
1033 | "nsspeed-entries", Logging::Loggable(SyncRes::getNSSpeedsSize()), |
1034 | "failed-host-entries", Logging::Loggable(SyncRes::getFailedServersSize()), | |
f6001854 | 1035 | "edns-entries", Logging::Loggable(SyncRes::getEDNSStatusesSize()), |
3ddcf687 OM |
1036 | "non-resolving-nameserver-entries", Logging::Loggable(SyncRes::getNonResolvingNSSize()), |
1037 | "saved-parent-ns-sets-entries", Logging::Loggable(SyncRes::getSaveParentsNSSetsSize()), | |
ed164eb2 | 1038 | "outqueries-per-query", Logging::Loggable(ratePercentage(SyncRes::s_outqueries, SyncRes::s_queries))); |
cbcd2a69 | 1039 | log->info(Logr::Info, m, |
3ddcf687 OM |
1040 | "throttled-queries-perc", Logging::Loggable(ratePercentage(SyncRes::s_throttledqueries, SyncRes::s_outqueries + SyncRes::s_throttledqueries)), |
1041 | "tcp-outqueries", Logging::Loggable(SyncRes::s_tcpoutqueries), | |
1042 | "dot-outqueries", Logging::Loggable(SyncRes::s_dotoutqueries), | |
1043 | "idle-tcpout-connections", Logging::Loggable(getCurrentIdleTCPConnections()), | |
1044 | "concurrent-queries", Logging::Loggable(broadcastAccFunction<uint64_t>(pleaseGetConcurrentQueries)), | |
fb702e74 | 1045 | "outgoing-timeouts", Logging::Loggable(SyncRes::s_outgoingtimeouts)); |
cbcd2a69 | 1046 | log->info(Logr::Info, m, |
3ddcf687 OM |
1047 | "packetcache-entries", Logging::Loggable(pcSize), |
1048 | "packetcache-hitratio-perc", Logging::Loggable(ratePercentage(pcHits, g_stats.qcounter)), | |
1049 | "taskqueue-pushed", Logging::Loggable(taskPushes), | |
1050 | "taskqueue-expired", Logging::Loggable(taskExpired), | |
1051 | "taskqueue-size", Logging::Loggable(taskSize)); | |
1052 | } | |
eeaa1cb6 | 1053 | size_t idx = 0; |
4f6e00dc | 1054 | for (const auto& threadInfo : RecThreadInfo::infos()) { |
69b39198 | 1055 | if (threadInfo.isWorker()) { |
3ddcf687 | 1056 | SLOG(g_log << Logger::Notice << "stats: thread " << idx << " has been distributed " << threadInfo.numberOfDistributedQueries << " queries" << endl, |
cbcd2a69 | 1057 | log->info(Logr::Info, "Queries handled by thread", "thread", Logging::Loggable(idx), "count", Logging::Loggable(threadInfo.numberOfDistributedQueries))); |
eeaa1cb6 OM |
1058 | ++idx; |
1059 | } | |
1060 | } | |
eeaa1cb6 | 1061 | time_t now = time(0); |
8c1cb8aa | 1062 | if (lastOutputTime && lastQueryCount && now != lastOutputTime) { |
3ddcf687 | 1063 | SLOG(g_log << Logger::Notice << "stats: " << (g_stats.qcounter - lastQueryCount) / (now - lastOutputTime) << " qps (average over " << (now - lastOutputTime) << " seconds)" << endl, |
cbcd2a69 | 1064 | log->info(Logr::Info, "Periodic QPS report", "qps", Logging::Loggable((g_stats.qcounter - lastQueryCount) / (now - lastOutputTime)), |
3ddcf687 | 1065 | "averagedOver", Logging::Loggable(now - lastOutputTime))); |
eeaa1cb6 OM |
1066 | } |
1067 | lastOutputTime = now; | |
ed7dd19b | 1068 | lastQueryCount = g_stats.qcounter; |
eeaa1cb6 | 1069 | } |
cbcd2a69 OM |
1070 | else if (statsWanted) { |
1071 | SLOG(g_log << Logger::Notice << "stats: no stats yet!" << endl, | |
1072 | log->info(Logr::Notice, "No stats yet")); | |
1073 | } | |
eeaa1cb6 | 1074 | |
8c1cb8aa | 1075 | statsWanted = false; |
eeaa1cb6 OM |
1076 | } |
1077 | ||
d61b8a01 | 1078 | static std::shared_ptr<NetmaskGroup> parseACL(const std::string& aclFile, const std::string& aclSetting, Logr::log_t log) |
eeaa1cb6 OM |
1079 | { |
1080 | auto result = std::make_shared<NetmaskGroup>(); | |
1081 | ||
8c1cb8aa | 1082 | if (!::arg()[aclFile].empty()) { |
eeaa1cb6 OM |
1083 | string line; |
1084 | ifstream ifs(::arg()[aclFile].c_str()); | |
8c1cb8aa OM |
1085 | if (!ifs) { |
1086 | throw runtime_error("Could not open '" + ::arg()[aclFile] + "': " + stringerror()); | |
eeaa1cb6 OM |
1087 | } |
1088 | ||
1089 | string::size_type pos; | |
8c1cb8aa OM |
1090 | while (getline(ifs, line)) { |
1091 | pos = line.find('#'); | |
1092 | if (pos != string::npos) | |
eeaa1cb6 OM |
1093 | line.resize(pos); |
1094 | boost::trim(line); | |
8c1cb8aa | 1095 | if (line.empty()) |
eeaa1cb6 OM |
1096 | continue; |
1097 | ||
1098 | result->addMask(line); | |
1099 | } | |
d8d1d955 OM |
1100 | SLOG(g_log << Logger::Info << "Done parsing " << result->size() << " " << aclSetting << " ranges from file '" << ::arg()[aclFile] << "' - overriding '" << aclSetting << "' setting" << endl, |
1101 | log->info(Logr::Info, "Done parsing ranges from file, will override setting", "setting", Logging::Loggable(aclSetting), | |
6cb5db59 OM |
1102 | "number", Logging::Loggable(result->size()), "file", Logging::Loggable(::arg()[aclFile]))); |
1103 | } | |
8c1cb8aa | 1104 | else if (!::arg()[aclSetting].empty()) { |
eeaa1cb6 OM |
1105 | vector<string> ips; |
1106 | stringtok(ips, ::arg()[aclSetting], ", "); | |
1107 | ||
d8d1d955 OM |
1108 | for (const auto& i : ips) { |
1109 | result->addMask(i); | |
1110 | } | |
1111 | if (!g_slogStructured) { | |
1112 | g_log << Logger::Info << aclSetting << ": "; | |
1113 | for (vector<string>::const_iterator i = ips.begin(); i != ips.end(); ++i) { | |
1114 | if (i != ips.begin()) | |
1115 | g_log << Logger::Info << ", "; | |
1116 | g_log << Logger::Info << *i; | |
1117 | } | |
1118 | g_log << Logger::Info << endl; | |
1119 | } | |
1120 | else { | |
88ddfdf4 | 1121 | log->info(Logr::Info, "Setting access control", "acl", Logging::Loggable(aclSetting), "addresses", Logging::IterLoggable(ips.begin(), ips.end())); |
eeaa1cb6 | 1122 | } |
eeaa1cb6 OM |
1123 | } |
1124 | ||
bc79df66 | 1125 | return result; |
eeaa1cb6 OM |
1126 | } |
1127 | ||
1128 | static void* pleaseSupplantAllowFrom(std::shared_ptr<NetmaskGroup> ng) | |
1129 | { | |
1130 | t_allowFrom = ng; | |
1131 | return nullptr; | |
1132 | } | |
1133 | ||
1134 | static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr<NetmaskGroup> ng) | |
1135 | { | |
1136 | t_allowNotifyFrom = ng; | |
1137 | return nullptr; | |
1138 | } | |
1139 | ||
1140 | void* pleaseSupplantAllowNotifyFor(std::shared_ptr<notifyset_t> ns) | |
1141 | { | |
1142 | t_allowNotifyFor = ns; | |
1143 | return nullptr; | |
1144 | } | |
1145 | ||
1146 | void parseACLs() | |
1147 | { | |
d8d1d955 OM |
1148 | auto log = g_slog->withName("config"); |
1149 | ||
eeaa1cb6 OM |
1150 | static bool l_initialized; |
1151 | ||
8c1cb8aa | 1152 | if (l_initialized) { // only reload configuration file on second call |
7102c209 FM |
1153 | string configName = ::arg()["config-dir"] + "/recursor.conf"; |
1154 | if (!::arg()["config-name"].empty()) { | |
1155 | configName = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"] + ".conf"; | |
eeaa1cb6 | 1156 | } |
7102c209 | 1157 | cleanSlashes(configName); |
eeaa1cb6 | 1158 | |
7102c209 FM |
1159 | if (!::arg().preParseFile(configName.c_str(), "allow-from-file")) { |
1160 | throw runtime_error("Unable to re-parse configuration file '" + configName + "'"); | |
1161 | } | |
1162 | ::arg().preParseFile(configName.c_str(), "allow-from", LOCAL_NETS); | |
eeaa1cb6 | 1163 | |
7102c209 FM |
1164 | if (!::arg().preParseFile(configName.c_str(), "allow-notify-from-file")) { |
1165 | throw runtime_error("Unable to re-parse configuration file '" + configName + "'"); | |
1166 | } | |
1167 | ::arg().preParseFile(configName.c_str(), "allow-notify-from"); | |
eeaa1cb6 | 1168 | |
7102c209 | 1169 | ::arg().preParseFile(configName.c_str(), "include-dir"); |
eeaa1cb6 OM |
1170 | ::arg().preParse(g_argc, g_argv, "include-dir"); |
1171 | ||
1172 | // then process includes | |
1173 | std::vector<std::string> extraConfigs; | |
1174 | ::arg().gatherIncludes(extraConfigs); | |
1175 | ||
7102c209 FM |
1176 | for (const std::string& fileName : extraConfigs) { |
1177 | if (!::arg().preParseFile(fileName.c_str(), "allow-from-file", ::arg()["allow-from-file"])) { | |
1178 | throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'"); | |
1179 | } | |
1180 | if (!::arg().preParseFile(fileName.c_str(), "allow-from", ::arg()["allow-from"])) { | |
1181 | throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'"); | |
1182 | } | |
eeaa1cb6 | 1183 | |
7102c209 FM |
1184 | if (!::arg().preParseFile(fileName.c_str(), "allow-notify-from-file", ::arg()["allow-notify-from-file"])) { |
1185 | throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'"); | |
1186 | } | |
1187 | if (!::arg().preParseFile(fileName.c_str(), "allow-notify-from", ::arg()["allow-notify-from"])) { | |
1188 | throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'"); | |
1189 | } | |
eeaa1cb6 OM |
1190 | } |
1191 | ||
1192 | ::arg().preParse(g_argc, g_argv, "allow-from-file"); | |
1193 | ::arg().preParse(g_argc, g_argv, "allow-from"); | |
1194 | ||
1195 | ::arg().preParse(g_argc, g_argv, "allow-notify-from-file"); | |
1196 | ::arg().preParse(g_argc, g_argv, "allow-notify-from"); | |
1197 | } | |
1198 | ||
d8d1d955 | 1199 | auto allowFrom = parseACL("allow-from-file", "allow-from", log); |
eeaa1cb6 | 1200 | |
7102c209 | 1201 | if (allowFrom->empty()) { |
d8d1d955 OM |
1202 | if (::arg()["local-address"] != "127.0.0.1" && ::arg().asNum("local-port") == 53) { |
1203 | SLOG(g_log << Logger::Warning << "WARNING: Allowing queries from all IP addresses - this can be a security risk!" << endl, | |
1204 | log->info(Logr::Warning, "WARNING: Allowing queries from all IP addresses - this can be a security risk!")); | |
1205 | } | |
eeaa1cb6 OM |
1206 | allowFrom = nullptr; |
1207 | } | |
1208 | ||
1209 | g_initialAllowFrom = allowFrom; | |
8c1cb8aa | 1210 | broadcastFunction([=] { return pleaseSupplantAllowFrom(allowFrom); }); |
eeaa1cb6 | 1211 | |
d8d1d955 | 1212 | auto allowNotifyFrom = parseACL("allow-notify-from-file", "allow-notify-from", log); |
eeaa1cb6 OM |
1213 | |
1214 | g_initialAllowNotifyFrom = allowNotifyFrom; | |
8c1cb8aa | 1215 | broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom); }); |
eeaa1cb6 OM |
1216 | |
1217 | l_initialized = true; | |
1218 | } | |
1219 | ||
eeaa1cb6 OM |
1220 | void broadcastFunction(const pipefunc_t& func) |
1221 | { | |
1222 | /* This function might be called by the worker with t_id 0 during startup | |
1223 | for the initialization of ACLs and domain maps. After that it should only | |
1224 | be called by the handler. */ | |
1225 | ||
4f6e00dc | 1226 | if (RecThreadInfo::infos().empty() && RecThreadInfo::id() == 0) { |
eeaa1cb6 | 1227 | /* the handler and distributors will call themselves below, but |
b35191d7 | 1228 | during startup we get called while g_threadInfos has not been |
eeaa1cb6 OM |
1229 | populated yet to update the ACL or domain maps, so we need to |
1230 | handle that case. | |
1231 | */ | |
1232 | func(); | |
1233 | } | |
1234 | ||
1235 | unsigned int n = 0; | |
4f6e00dc | 1236 | for (const auto& threadInfo : RecThreadInfo::infos()) { |
9abbcebb | 1237 | if (n++ == RecThreadInfo::id()) { |
eeaa1cb6 OM |
1238 | func(); // don't write to ourselves! |
1239 | continue; | |
1240 | } | |
1241 | ||
1242 | ThreadMSG* tmsg = new ThreadMSG(); | |
1243 | tmsg->func = func; | |
1244 | tmsg->wantAnswer = true; | |
8c1cb8aa | 1245 | if (write(threadInfo.pipes.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { |
eeaa1cb6 OM |
1246 | delete tmsg; |
1247 | ||
1248 | unixDie("write to thread pipe returned wrong size or error"); | |
1249 | } | |
1250 | ||
1251 | string* resp = nullptr; | |
8c1cb8aa | 1252 | if (read(threadInfo.pipes.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) |
eeaa1cb6 OM |
1253 | unixDie("read from thread pipe returned wrong size or error"); |
1254 | ||
8c1cb8aa | 1255 | if (resp) { |
eeaa1cb6 OM |
1256 | delete resp; |
1257 | resp = nullptr; | |
1258 | } | |
7db35c6e | 1259 | // coverity[leaked_storage] |
eeaa1cb6 OM |
1260 | } |
1261 | } | |
1262 | ||
8c1cb8aa | 1263 | template <class T> |
f4319769 | 1264 | void* voider(const std::function<T*()>& func) |
eeaa1cb6 OM |
1265 | { |
1266 | return func(); | |
1267 | } | |
1268 | ||
8c1cb8aa | 1269 | static vector<ComboAddress>& operator+=(vector<ComboAddress>& a, const vector<ComboAddress>& b) |
eeaa1cb6 OM |
1270 | { |
1271 | a.insert(a.end(), b.begin(), b.end()); | |
1272 | return a; | |
1273 | } | |
1274 | ||
8c1cb8aa | 1275 | static vector<pair<DNSName, uint16_t>>& operator+=(vector<pair<DNSName, uint16_t>>& a, const vector<pair<DNSName, uint16_t>>& b) |
eeaa1cb6 OM |
1276 | { |
1277 | a.insert(a.end(), b.begin(), b.end()); | |
1278 | return a; | |
1279 | } | |
1280 | ||
c375521b OM |
1281 | static ProxyMappingStats_t& operator+=(ProxyMappingStats_t& a, const ProxyMappingStats_t& b) |
1282 | { | |
1283 | for (const auto& [key, entry] : b) { | |
1284 | a[key].netmaskMatches += entry.netmaskMatches; | |
1285 | a[key].suffixMatches += entry.suffixMatches; | |
1286 | } | |
1287 | return a; | |
1288 | } | |
1289 | ||
b98b553a | 1290 | static RemoteLoggerStats_t& operator+=(RemoteLoggerStats_t& a, const RemoteLoggerStats_t& b) |
9472436f OM |
1291 | { |
1292 | for (const auto& [key, entry] : b) { | |
1293 | a[key] += entry; | |
1294 | } | |
1295 | return a; | |
1296 | } | |
1297 | ||
a0a3a61a OM |
1298 | // This function should only be called by the handler to gather |
1299 | // metrics, wipe the cache, reload the Lua script (not the Lua config) | |
1300 | // or change the current trace regex, and by the SNMP thread to gather | |
1301 | // metrics. | |
1302 | // Note that this currently skips the handler, but includes the taskThread(s). | |
8c1cb8aa | 1303 | template <class T> |
94f63b50 | 1304 | T broadcastAccFunction(const std::function<T*()>& func) |
eeaa1cb6 | 1305 | { |
69b39198 | 1306 | if (!RecThreadInfo::self().isHandler()) { |
62b191dc OM |
1307 | SLOG(g_log << Logger::Error << "broadcastAccFunction has been called by a worker (" << RecThreadInfo::id() << ")" << endl, |
1308 | g_slog->withName("runtime")->info(Logr::Critical, "broadcastAccFunction has been called by a worker")); // tid will be added | |
eeaa1cb6 OM |
1309 | _exit(1); |
1310 | } | |
1311 | ||
1312 | unsigned int n = 0; | |
8c1cb8aa | 1313 | T ret = T(); |
4f6e00dc | 1314 | for (const auto& threadInfo : RecThreadInfo::infos()) { |
69b39198 | 1315 | if (n++ == RecThreadInfo::id()) { |
eeaa1cb6 OM |
1316 | continue; |
1317 | } | |
1318 | ||
1319 | const auto& tps = threadInfo.pipes; | |
1320 | ThreadMSG* tmsg = new ThreadMSG(); | |
8c1cb8aa | 1321 | tmsg->func = [func] { return voider<T>(func); }; |
eeaa1cb6 OM |
1322 | tmsg->wantAnswer = true; |
1323 | ||
8c1cb8aa | 1324 | if (write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { |
eeaa1cb6 OM |
1325 | delete tmsg; |
1326 | unixDie("write to thread pipe returned wrong size or error"); | |
1327 | } | |
1328 | ||
1329 | T* resp = nullptr; | |
8c1cb8aa | 1330 | if (read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) |
eeaa1cb6 OM |
1331 | unixDie("read from thread pipe returned wrong size or error"); |
1332 | ||
8c1cb8aa | 1333 | if (resp) { |
eeaa1cb6 OM |
1334 | ret += *resp; |
1335 | delete resp; | |
1336 | resp = nullptr; | |
1337 | } | |
7db35c6e | 1338 | // coverity[leaked_storage] |
eeaa1cb6 OM |
1339 | } |
1340 | return ret; | |
1341 | } | |
1342 | ||
94f63b50 OM |
1343 | template string broadcastAccFunction(const std::function<string*()>& fun); // explicit instantiation |
1344 | template RecursorControlChannel::Answer broadcastAccFunction(const std::function<RecursorControlChannel::Answer*()>& fun); // explicit instantiation | |
1345 | template uint64_t broadcastAccFunction(const std::function<uint64_t*()>& fun); // explicit instantiation | |
1346 | template vector<ComboAddress> broadcastAccFunction(const std::function<vector<ComboAddress>*()>& fun); // explicit instantiation | |
1347 | template vector<pair<DNSName, uint16_t>> broadcastAccFunction(const std::function<vector<pair<DNSName, uint16_t>>*()>& fun); // explicit instantiation | |
1348 | template ThreadTimes broadcastAccFunction(const std::function<ThreadTimes*()>& fun); | |
c375521b | 1349 | template ProxyMappingStats_t broadcastAccFunction(const std::function<ProxyMappingStats_t*()>& fun); |
9472436f | 1350 | template RemoteLoggerStats_t broadcastAccFunction(const std::function<RemoteLoggerStats_t*()>& fun); |
eeaa1cb6 | 1351 | |
d61b8a01 | 1352 | static int serviceMain(int argc, char* argv[], Logr::log_t log) |
eeaa1cb6 | 1353 | { |
b35191d7 | 1354 | g_log.setName(g_programname); |
eeaa1cb6 OM |
1355 | g_log.disableSyslog(::arg().mustDo("disable-syslog")); |
1356 | g_log.setTimestamps(::arg().mustDo("log-timestamp")); | |
1357 | ||
8c1cb8aa OM |
1358 | if (!::arg()["logging-facility"].empty()) { |
1359 | int val = logFacilityToLOG(::arg().asNum("logging-facility")); | |
1360 | if (val >= 0) | |
eeaa1cb6 | 1361 | g_log.setFacility(val); |
d8d1d955 OM |
1362 | else { |
1363 | SLOG(g_log << Logger::Error << "Unknown logging facility " << ::arg().asNum("logging-facility") << endl, | |
1364 | log->info(Logr::Error, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility")))); | |
1365 | } | |
eeaa1cb6 OM |
1366 | } |
1367 | ||
1368 | showProductVersion(); | |
1369 | ||
8c1cb8aa | 1370 | g_disthashseed = dns_random(0xffffffff); |
eeaa1cb6 | 1371 | |
37c3c693 | 1372 | checkLinuxIPv6Limits(log); |
eeaa1cb6 OM |
1373 | try { |
1374 | pdns::parseQueryLocalAddress(::arg()["query-local-address"]); | |
1375 | } | |
8c1cb8aa | 1376 | catch (std::exception& e) { |
d8d1d955 | 1377 | SLOG(g_log << Logger::Error << "Assigning local query addresses: " << e.what(), |
ed3ec74d | 1378 | log->error(Logr::Error, e.what(), "Unable to assign local query address")); |
eeaa1cb6 OM |
1379 | exit(99); |
1380 | } | |
1381 | ||
8c1cb8aa OM |
1382 | if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) { |
1383 | SyncRes::s_doIPv4 = true; | |
d8d1d955 | 1384 | SLOG(g_log << Logger::Warning << "Enabling IPv4 transport for outgoing queries" << endl, |
d197c31c | 1385 | log->info(Logr::Notice, "Enabling IPv4 transport for outgoing queries")); |
eeaa1cb6 OM |
1386 | } |
1387 | else { | |
d8d1d955 OM |
1388 | SLOG(g_log << Logger::Warning << "NOT using IPv4 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable" << endl, |
1389 | log->info(Logr::Warning, "NOT using IPv4 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable")); | |
eeaa1cb6 OM |
1390 | } |
1391 | ||
8c1cb8aa OM |
1392 | if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) { |
1393 | SyncRes::s_doIPv6 = true; | |
d8d1d955 | 1394 | SLOG(g_log << Logger::Warning << "Enabling IPv6 transport for outgoing queries" << endl, |
d197c31c | 1395 | log->info(Logr::Notice, "Enabling IPv6 transport for outgoing queries")); |
eeaa1cb6 OM |
1396 | } |
1397 | else { | |
d8d1d955 OM |
1398 | SLOG(g_log << Logger::Warning << "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable" << endl, |
1399 | log->info(Logr::Warning, "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable")); | |
eeaa1cb6 OM |
1400 | } |
1401 | ||
1402 | if (!SyncRes::s_doIPv6 && !SyncRes::s_doIPv4) { | |
d8d1d955 OM |
1403 | SLOG(g_log << Logger::Error << "No outgoing addresses configured! Can not continue" << endl, |
1404 | log->info(Logr::Error, "No outgoing addresses configured! Can not continue")); | |
eeaa1cb6 OM |
1405 | exit(99); |
1406 | } | |
1407 | ||
1408 | // keep this ABOVE loadRecursorLuaConfig! | |
8c1cb8aa OM |
1409 | if (::arg()["dnssec"] == "off") |
1410 | g_dnssecmode = DNSSECMode::Off; | |
1411 | else if (::arg()["dnssec"] == "process-no-validate") | |
1412 | g_dnssecmode = DNSSECMode::ProcessNoValidate; | |
1413 | else if (::arg()["dnssec"] == "process") | |
1414 | g_dnssecmode = DNSSECMode::Process; | |
1415 | else if (::arg()["dnssec"] == "validate") | |
1416 | g_dnssecmode = DNSSECMode::ValidateAll; | |
1417 | else if (::arg()["dnssec"] == "log-fail") | |
1418 | g_dnssecmode = DNSSECMode::ValidateForLog; | |
eeaa1cb6 | 1419 | else { |
d8d1d955 OM |
1420 | SLOG(g_log << Logger::Error << "Unknown DNSSEC mode " << ::arg()["dnssec"] << endl, |
1421 | log->info(Logr::Error, "Unknown DNSSEC mode", "dnssec", Logging::Loggable(::arg()["dnssec"]))); | |
eeaa1cb6 OM |
1422 | exit(1); |
1423 | } | |
1424 | ||
1425 | g_signatureInceptionSkew = ::arg().asNum("signature-inception-skew"); | |
1426 | if (g_signatureInceptionSkew < 0) { | |
d8d1d955 OM |
1427 | SLOG(g_log << Logger::Error << "A negative value for 'signature-inception-skew' is not allowed" << endl, |
1428 | log->info(Logr::Error, "A negative value for 'signature-inception-skew' is not allowed")); | |
eeaa1cb6 OM |
1429 | exit(1); |
1430 | } | |
1431 | ||
1432 | g_dnssecLogBogus = ::arg().mustDo("dnssec-log-bogus"); | |
1433 | g_maxNSEC3Iterations = ::arg().asNum("nsec3-max-iterations"); | |
1434 | ||
1435 | g_maxCacheEntries = ::arg().asNum("max-cache-entries"); | |
1436 | g_maxPacketCacheEntries = ::arg().asNum("max-packetcache-entries"); | |
1437 | ||
1438 | luaConfigDelayedThreads delayedLuaThreads; | |
1439 | try { | |
e81063e5 OM |
1440 | ProxyMapping proxyMapping; |
1441 | loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads, proxyMapping); | |
1442 | // Initial proxy mapping | |
c375521b | 1443 | g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_unique<ProxyMapping>(proxyMapping); |
eeaa1cb6 | 1444 | } |
8c1cb8aa | 1445 | catch (PDNSException& e) { |
d8d1d955 OM |
1446 | SLOG(g_log << Logger::Error << "Cannot load Lua configuration: " << e.reason << endl, |
1447 | log->error(Logr::Error, e.reason, "Cannot load Lua configuration")); | |
eeaa1cb6 OM |
1448 | exit(1); |
1449 | } | |
1450 | ||
1451 | parseACLs(); | |
1452 | initPublicSuffixList(::arg()["public-suffix-list-file"]); | |
1453 | ||
8c1cb8aa | 1454 | if (!::arg()["dont-query"].empty()) { |
eeaa1cb6 OM |
1455 | vector<string> ips; |
1456 | stringtok(ips, ::arg()["dont-query"], ", "); | |
1457 | ips.push_back("0.0.0.0"); | |
1458 | ips.push_back("::"); | |
1459 | ||
d8d1d955 OM |
1460 | for (const auto& ip : ips) { |
1461 | SyncRes::addDontQuery(ip); | |
1462 | } | |
1463 | if (!g_slogStructured) { | |
1464 | g_log << Logger::Warning << "Will not send queries to: "; | |
1465 | for (vector<string>::const_iterator i = ips.begin(); i != ips.end(); ++i) { | |
1466 | if (i != ips.begin()) | |
1467 | g_log << Logger::Warning << ", "; | |
1468 | g_log << Logger::Warning << *i; | |
1469 | } | |
1470 | g_log << Logger::Warning << endl; | |
1471 | } | |
1472 | else { | |
88ddfdf4 | 1473 | log->info(Logr::Notice, "Will not send queries to", "addresses", Logging::IterLoggable(ips.begin(), ips.end())); |
eeaa1cb6 | 1474 | } |
eeaa1cb6 OM |
1475 | } |
1476 | ||
1477 | /* this needs to be done before parseACLs(), which call broadcastFunction() */ | |
048607b4 OM |
1478 | RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries")); |
1479 | if (RecThreadInfo::weDistributeQueries()) { | |
d8d1d955 | 1480 | SLOG(g_log << Logger::Warning << "PowerDNS Recursor itself will distribute queries over threads" << endl, |
d197c31c | 1481 | log->info(Logr::Notice, "PowerDNS Recursor itself will distribute queries over threads")); |
eeaa1cb6 OM |
1482 | } |
1483 | ||
8c1cb8aa | 1484 | g_outgoingEDNSBufsize = ::arg().asNum("edns-outgoing-bufsize"); |
eeaa1cb6 | 1485 | |
8c1cb8aa | 1486 | if (::arg()["trace"] == "fail") { |
eeaa1cb6 OM |
1487 | SyncRes::setDefaultLogMode(SyncRes::Store); |
1488 | } | |
8c1cb8aa | 1489 | else if (::arg().mustDo("trace")) { |
eeaa1cb6 | 1490 | SyncRes::setDefaultLogMode(SyncRes::Log); |
8c1cb8aa OM |
1491 | ::arg().set("quiet") = "no"; |
1492 | g_quiet = false; | |
1493 | g_dnssecLOG = true; | |
eeaa1cb6 | 1494 | } |
ff32c991 FM |
1495 | auto myHostname = getHostname(); |
1496 | if (!myHostname.has_value()) { | |
d8d1d955 OM |
1497 | SLOG(g_log << Logger::Warning << "Unable to get the hostname, NSID and id.server values will be empty" << endl, |
1498 | log->info(Logr::Warning, "Unable to get the hostname, NSID and id.server values will be empty")); | |
eeaa1cb6 OM |
1499 | } |
1500 | ||
1501 | SyncRes::s_minimumTTL = ::arg().asNum("minimum-ttl-override"); | |
1502 | SyncRes::s_minimumECSTTL = ::arg().asNum("ecs-minimum-ttl-override"); | |
8c1cb8aa OM |
1503 | SyncRes::s_maxnegttl = ::arg().asNum("max-negative-ttl"); |
1504 | SyncRes::s_maxbogusttl = ::arg().asNum("max-cache-bogus-ttl"); | |
1505 | SyncRes::s_maxcachettl = max(::arg().asNum("max-cache-ttl"), 15); | |
1506 | SyncRes::s_packetcachettl = ::arg().asNum("packetcache-ttl"); | |
eeaa1cb6 OM |
1507 | // Cap the packetcache-servfail-ttl to the packetcache-ttl |
1508 | uint32_t packetCacheServFailTTL = ::arg().asNum("packetcache-servfail-ttl"); | |
8c1cb8aa OM |
1509 | SyncRes::s_packetcacheservfailttl = (packetCacheServFailTTL > SyncRes::s_packetcachettl) ? SyncRes::s_packetcachettl : packetCacheServFailTTL; |
1510 | SyncRes::s_serverdownmaxfails = ::arg().asNum("server-down-max-fails"); | |
1511 | SyncRes::s_serverdownthrottletime = ::arg().asNum("server-down-throttle-time"); | |
1512 | SyncRes::s_nonresolvingnsmaxfails = ::arg().asNum("non-resolving-ns-max-fails"); | |
1513 | SyncRes::s_nonresolvingnsthrottletime = ::arg().asNum("non-resolving-ns-throttle-time"); | |
1514 | SyncRes::s_serverID = ::arg()["server-id"]; | |
1515 | SyncRes::s_maxqperq = ::arg().asNum("max-qperq"); | |
a49b0b40 | 1516 | SyncRes::s_maxnsperresolve = ::arg().asNum("max-ns-per-resolve"); |
8c1cb8aa OM |
1517 | SyncRes::s_maxnsaddressqperq = ::arg().asNum("max-ns-address-qperq"); |
1518 | SyncRes::s_maxtotusec = 1000 * ::arg().asNum("max-total-msec"); | |
1519 | SyncRes::s_maxdepth = ::arg().asNum("max-recursion-depth"); | |
1520 | SyncRes::s_rootNXTrust = ::arg().mustDo("root-nx-trust"); | |
eeaa1cb6 | 1521 | SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc"); |
55badaf8 | 1522 | SyncRes::s_locked_ttlperc = ::arg().asNum("record-cache-locked-ttl-perc"); |
eeaa1cb6 OM |
1523 | RecursorPacketCache::s_refresh_ttlperc = SyncRes::s_refresh_ttlperc; |
1524 | SyncRes::s_tcp_fast_open = ::arg().asNum("tcp-fast-open"); | |
1525 | SyncRes::s_tcp_fast_open_connect = ::arg().mustDo("tcp-fast-open-connect"); | |
1526 | ||
1527 | SyncRes::s_dot_to_port_853 = ::arg().mustDo("dot-to-port-853"); | |
1528 | SyncRes::s_event_trace_enabled = ::arg().asNum("event-trace-enabled"); | |
1bb24087 | 1529 | SyncRes::s_save_parent_ns_set = ::arg().mustDo("save-parent-ns-set"); |
78deedda | 1530 | SyncRes::s_max_busy_dot_probes = ::arg().asNum("max-busy-dot-probes"); |
8c08b4ec OM |
1531 | { |
1532 | uint64_t sse = ::arg().asNum("serve-stale-extensions"); | |
1533 | if (sse > std::numeric_limits<uint16_t>::max()) { | |
1534 | SLOG(g_log << Logger::Error << "Illegal serve-stale-extensions value: " << sse << "; range = 0..65536" << endl, | |
1535 | log->info(Logr::Error, "Illegal serve-stale-extensions value; range = 0..65536", "value", Logging::Loggable(sse))); | |
1536 | exit(1); | |
1537 | } | |
1538 | MemRecursorCache::s_maxServedStaleExtensions = sse; | |
1539 | NegCache::s_maxServedStaleExtensions = sse; | |
1540 | } | |
eeaa1cb6 OM |
1541 | |
1542 | if (SyncRes::s_tcp_fast_open_connect) { | |
62b191dc OM |
1543 | checkFastOpenSysctl(true, log); |
1544 | checkTFOconnect(log); | |
eeaa1cb6 OM |
1545 | } |
1546 | ||
8c1cb8aa | 1547 | if (SyncRes::s_serverID.empty()) { |
ff32c991 | 1548 | SyncRes::s_serverID = myHostname.has_value() ? *myHostname : ""; |
eeaa1cb6 OM |
1549 | } |
1550 | ||
1551 | SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits"); | |
1552 | SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits"); | |
1553 | SyncRes::clearECSStats(); | |
1554 | SyncRes::s_ecsipv4cachelimit = ::arg().asNum("ecs-ipv4-cache-bits"); | |
1555 | SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits"); | |
1556 | SyncRes::s_ecsipv4nevercache = ::arg().mustDo("ecs-ipv4-never-cache"); | |
1557 | SyncRes::s_ecsipv6nevercache = ::arg().mustDo("ecs-ipv6-never-cache"); | |
1558 | SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl"); | |
1559 | ||
1560 | SyncRes::s_qnameminimization = ::arg().mustDo("qname-minimization"); | |
1561 | ||
1562 | if (SyncRes::s_qnameminimization) { | |
1563 | // With an empty cache, a rev ipv6 query with dnssec enabled takes | |
1564 | // almost 100 queries. Default maxqperq is 60. | |
1565 | SyncRes::s_maxqperq = std::max(SyncRes::s_maxqperq, static_cast<unsigned int>(100)); | |
1566 | } | |
1567 | ||
1568 | SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC; | |
1569 | string value = ::arg()["nothing-below-nxdomain"]; | |
1570 | if (value == "yes") { | |
1571 | SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes; | |
8c1cb8aa OM |
1572 | } |
1573 | else if (value == "no") { | |
eeaa1cb6 | 1574 | SyncRes::s_hardenNXD = SyncRes::HardenNXD::No; |
8c1cb8aa OM |
1575 | } |
1576 | else if (value != "dnssec") { | |
d8d1d955 OM |
1577 | SLOG(g_log << Logger::Error << "Unknown nothing-below-nxdomain mode: " << value << endl, |
1578 | log->info(Logr::Error, "Unknown nothing-below-nxdomain mode", "mode", Logging::Loggable(value))); | |
eeaa1cb6 OM |
1579 | exit(1); |
1580 | } | |
1581 | ||
1582 | if (!::arg().isEmpty("ecs-scope-zero-address")) { | |
1583 | ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]); | |
1584 | SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128)); | |
1585 | } | |
1586 | else { | |
1587 | Netmask nm; | |
1588 | bool done = false; | |
1589 | ||
1590 | auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET); | |
1591 | if (addr.sin4.sin_family != 0) { | |
1592 | nm = Netmask(addr, 32); | |
1593 | done = true; | |
1594 | } | |
1595 | if (!done) { | |
1596 | addr = pdns::getNonAnyQueryLocalAddress(AF_INET6); | |
1597 | if (addr.sin4.sin_family != 0) { | |
1598 | nm = Netmask(addr, 128); | |
1599 | done = true; | |
1600 | } | |
1601 | } | |
1602 | if (!done) { | |
1603 | nm = Netmask(ComboAddress("127.0.0.1"), 32); | |
1604 | } | |
1605 | SyncRes::setECSScopeZeroAddress(nm); | |
1606 | } | |
1607 | ||
1608 | SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]); | |
1609 | SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]); | |
1610 | SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]); | |
1611 | g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet"); | |
1612 | ||
eeaa1cb6 OM |
1613 | g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]); |
1614 | g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size"); | |
1615 | ||
1616 | if (!::arg()["dns64-prefix"].empty()) { | |
1617 | try { | |
1618 | auto dns64Prefix = Netmask(::arg()["dns64-prefix"]); | |
1619 | if (dns64Prefix.getBits() != 96) { | |
d8d1d955 OM |
1620 | SLOG(g_log << Logger::Error << "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl, |
1621 | log->info(Logr::Error, "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes", "prefix", Logging::Loggable(::arg()["dns64-prefix"]))); | |
eeaa1cb6 OM |
1622 | exit(1); |
1623 | } | |
1624 | g_dns64Prefix = dns64Prefix.getNetwork(); | |
1625 | g_dns64PrefixReverse = reverseNameFromIP(*g_dns64Prefix); | |
1626 | /* /96 is 24 nibbles + 2 for "ip6.arpa." */ | |
1627 | while (g_dns64PrefixReverse.countLabels() > 26) { | |
1628 | g_dns64PrefixReverse.chopOff(); | |
1629 | } | |
1630 | } | |
1631 | catch (const NetmaskException& ne) { | |
d8d1d955 OM |
1632 | SLOG(g_log << Logger::Error << "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne.reason << endl, |
1633 | log->info(Logr::Error, "Invalid prefix", "dns64-prefix", Logging::Loggable(::arg()["dns64-prefix"]))); | |
eeaa1cb6 OM |
1634 | exit(1); |
1635 | } | |
1636 | } | |
1637 | ||
1638 | g_networkTimeoutMsec = ::arg().asNum("network-timeout"); | |
1639 | ||
1640 | std::tie(g_initialDomainMap, g_initialAllowNotifyFor) = parseZoneConfiguration(); | |
1641 | ||
8c1cb8aa | 1642 | g_latencyStatSize = ::arg().asNum("latency-statistic-size"); |
eeaa1cb6 | 1643 | |
8c1cb8aa | 1644 | g_logCommonErrors = ::arg().mustDo("log-common-errors"); |
eeaa1cb6 OM |
1645 | g_logRPZChanges = ::arg().mustDo("log-rpz-changes"); |
1646 | ||
1647 | g_anyToTcp = ::arg().mustDo("any-to-tcp"); | |
1648 | g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold"); | |
1649 | ||
1650 | g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing"); | |
1651 | ||
1652 | g_paddingFrom.toMasks(::arg()["edns-padding-from"]); | |
1653 | if (::arg()["edns-padding-mode"] == "always") { | |
1654 | g_paddingMode = PaddingMode::Always; | |
1655 | } | |
1656 | else if (::arg()["edns-padding-mode"] == "padded-queries-only") { | |
1657 | g_paddingMode = PaddingMode::PaddedQueries; | |
1658 | } | |
1659 | else { | |
d8d1d955 OM |
1660 | SLOG(g_log << Logger::Error << "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl, |
1661 | log->info(Logr::Error, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"]))); | |
eeaa1cb6 OM |
1662 | exit(1); |
1663 | } | |
1664 | g_paddingTag = ::arg().asNum("edns-padding-tag"); | |
fcf47f49 | 1665 | g_paddingOutgoing = ::arg().mustDo("edns-padding-out"); |
eeaa1cb6 | 1666 | |
048607b4 OM |
1667 | RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads")); |
1668 | RecThreadInfo::setNumWorkerThreads(::arg().asNum("threads")); | |
1669 | if (RecThreadInfo::numWorkers() < 1) { | |
d8d1d955 OM |
1670 | SLOG(g_log << Logger::Warning << "Asked to run with 0 threads, raising to 1 instead" << endl, |
1671 | log->info(Logr::Warning, "Asked to run with 0 threads, raising to 1 instead")); | |
048607b4 | 1672 | RecThreadInfo::setNumWorkerThreads(1); |
eeaa1cb6 OM |
1673 | } |
1674 | ||
eeaa1cb6 OM |
1675 | g_maxMThreads = ::arg().asNum("max-mthreads"); |
1676 | ||
eeaa1cb6 OM |
1677 | int64_t maxInFlight = ::arg().asNum("max-concurrent-requests-per-tcp-connection"); |
1678 | if (maxInFlight < 1 || maxInFlight > USHRT_MAX || maxInFlight >= g_maxMThreads) { | |
d8d1d955 OM |
1679 | SLOG(g_log << Logger::Warning << "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)" << endl, |
1680 | log->info(Logr::Warning, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)")); | |
eeaa1cb6 | 1681 | TCPConnection::s_maxInFlight = 10; |
8c1cb8aa OM |
1682 | } |
1683 | else { | |
eeaa1cb6 OM |
1684 | TCPConnection::s_maxInFlight = maxInFlight; |
1685 | } | |
1686 | ||
1687 | int64_t millis = ::arg().asNum("tcp-out-max-idle-ms"); | |
8c1cb8aa | 1688 | TCPOutConnectionManager::s_maxIdleTime = timeval{millis / 1000, (static_cast<suseconds_t>(millis) % 1000) * 1000}; |
eeaa1cb6 OM |
1689 | TCPOutConnectionManager::s_maxIdlePerAuth = ::arg().asNum("tcp-out-max-idle-per-auth"); |
1690 | TCPOutConnectionManager::s_maxQueries = ::arg().asNum("tcp-out-max-queries"); | |
1691 | TCPOutConnectionManager::s_maxIdlePerThread = ::arg().asNum("tcp-out-max-idle-per-thread"); | |
1692 | ||
1693 | g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options"); | |
1694 | ||
b35191d7 | 1695 | s_statisticsInterval = ::arg().asNum("statistics-interval"); |
eeaa1cb6 | 1696 | |
b35191d7 | 1697 | g_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors"); |
eeaa1cb6 OM |
1698 | |
1699 | if (::arg().asNum("aggressive-nsec-cache-size") > 0) { | |
1700 | if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) { | |
1701 | g_aggressiveNSECCache = make_unique<AggressiveNSECCache>(::arg().asNum("aggressive-nsec-cache-size")); | |
1702 | } | |
1703 | else { | |
d8d1d955 OM |
1704 | SLOG(g_log << Logger::Warning << "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring" << endl, |
1705 | log->info(Logr::Warning, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring")); | |
eeaa1cb6 OM |
1706 | } |
1707 | } | |
1708 | ||
1709 | { | |
1710 | SuffixMatchNode dontThrottleNames; | |
1711 | vector<string> parts; | |
1712 | stringtok(parts, ::arg()["dont-throttle-names"], " ,"); | |
8c1cb8aa | 1713 | for (const auto& p : parts) { |
eeaa1cb6 OM |
1714 | dontThrottleNames.add(DNSName(p)); |
1715 | } | |
1716 | g_dontThrottleNames.setState(std::move(dontThrottleNames)); | |
1717 | ||
1718 | parts.clear(); | |
1719 | NetmaskGroup dontThrottleNetmasks; | |
1720 | stringtok(parts, ::arg()["dont-throttle-netmasks"], " ,"); | |
8c1cb8aa | 1721 | for (const auto& p : parts) { |
eeaa1cb6 OM |
1722 | dontThrottleNetmasks.addMask(Netmask(p)); |
1723 | } | |
1724 | g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks)); | |
1725 | } | |
1726 | ||
1727 | { | |
1728 | SuffixMatchNode xdnssecNames; | |
1729 | vector<string> parts; | |
1730 | stringtok(parts, ::arg()["x-dnssec-names"], " ,"); | |
8c1cb8aa | 1731 | for (const auto& p : parts) { |
eeaa1cb6 OM |
1732 | xdnssecNames.add(DNSName(p)); |
1733 | } | |
1734 | g_xdnssec.setState(std::move(xdnssecNames)); | |
1735 | } | |
1736 | ||
1737 | { | |
1738 | SuffixMatchNode dotauthNames; | |
1739 | vector<string> parts; | |
1740 | stringtok(parts, ::arg()["dot-to-auth-names"], " ,"); | |
1741 | #ifndef HAVE_DNS_OVER_TLS | |
1742 | if (parts.size()) { | |
d8d1d955 | 1743 | SLOG(g_log << Logger::Error << "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored." << endl, |
6cb5db59 | 1744 | log->info(Logr::Error, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored")); |
eeaa1cb6 OM |
1745 | } |
1746 | #endif | |
8c1cb8aa | 1747 | for (const auto& p : parts) { |
eeaa1cb6 OM |
1748 | dotauthNames.add(DNSName(p)); |
1749 | } | |
1750 | g_DoTToAuthNames.setState(std::move(dotauthNames)); | |
1751 | } | |
1752 | ||
1753 | { | |
1754 | CarbonConfig config; | |
1755 | stringtok(config.servers, arg()["carbon-server"], ", "); | |
1756 | config.hostname = arg()["carbon-ourname"]; | |
1757 | config.instance_name = arg()["carbon-instance"]; | |
1758 | config.namespace_name = arg()["carbon-namespace"]; | |
1759 | g_carbonConfig.setState(std::move(config)); | |
1760 | } | |
1761 | ||
b35191d7 OM |
1762 | g_balancingFactor = ::arg().asDouble("distribution-load-factor"); |
1763 | if (g_balancingFactor != 0.0 && g_balancingFactor < 1.0) { | |
1764 | g_balancingFactor = 0.0; | |
d8d1d955 OM |
1765 | SLOG(g_log << Logger::Warning << "Asked to run with a distribution-load-factor below 1.0, disabling it instead" << endl, |
1766 | log->info(Logr::Warning, "Asked to run with a distribution-load-factor below 1.0, disabling it instead")); | |
eeaa1cb6 OM |
1767 | } |
1768 | ||
1769 | #ifdef SO_REUSEPORT | |
1770 | g_reusePort = ::arg().mustDo("reuseport"); | |
1771 | #endif | |
1772 | ||
048607b4 | 1773 | RecThreadInfo::infos().resize(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + RecThreadInfo::numWorkers() + RecThreadInfo::numTaskThreads()); |
eeaa1cb6 OM |
1774 | |
1775 | if (g_reusePort) { | |
048607b4 | 1776 | if (RecThreadInfo::weDistributeQueries()) { |
eeaa1cb6 | 1777 | /* first thread is the handler, then distributors */ |
048607b4 | 1778 | for (unsigned int threadId = 1; threadId <= RecThreadInfo::numDistributors(); threadId++) { |
9abbcebb | 1779 | auto& info = RecThreadInfo::info(threadId); |
4f6e00dc OM |
1780 | auto& deferredAdds = info.deferredAdds; |
1781 | auto& tcpSockets = info.tcpSockets; | |
d8d1d955 OM |
1782 | makeUDPServerSockets(deferredAdds, log); |
1783 | makeTCPServerSockets(deferredAdds, tcpSockets, log); | |
eeaa1cb6 OM |
1784 | } |
1785 | } | |
1786 | else { | |
1787 | /* first thread is the handler, there is no distributor here and workers are accepting queries */ | |
048607b4 | 1788 | for (unsigned int threadId = 1; threadId <= RecThreadInfo::numWorkers(); threadId++) { |
9abbcebb | 1789 | auto& info = RecThreadInfo::info(threadId); |
4f6e00dc OM |
1790 | auto& deferredAdds = info.deferredAdds; |
1791 | auto& tcpSockets = info.tcpSockets; | |
d8d1d955 OM |
1792 | makeUDPServerSockets(deferredAdds, log); |
1793 | makeTCPServerSockets(deferredAdds, tcpSockets, log); | |
eeaa1cb6 OM |
1794 | } |
1795 | } | |
1796 | } | |
1797 | else { | |
1798 | std::set<int> tcpSockets; | |
1799 | /* we don't have reuseport so we can only open one socket per | |
1800 | listening addr:port and everyone will listen on it */ | |
d8d1d955 OM |
1801 | makeUDPServerSockets(g_deferredAdds, log); |
1802 | makeTCPServerSockets(g_deferredAdds, tcpSockets, log); | |
eeaa1cb6 OM |
1803 | |
1804 | /* every listener (so distributor if g_weDistributeQueries, workers otherwise) | |
1805 | needs to listen to the shared sockets */ | |
048607b4 | 1806 | if (RecThreadInfo::weDistributeQueries()) { |
eeaa1cb6 | 1807 | /* first thread is the handler, then distributors */ |
048607b4 | 1808 | for (unsigned int threadId = 1; threadId <= RecThreadInfo::numDistributors(); threadId++) { |
4f6e00dc | 1809 | RecThreadInfo::info(threadId).tcpSockets = tcpSockets; |
eeaa1cb6 OM |
1810 | } |
1811 | } | |
1812 | else { | |
1813 | /* first thread is the handler, there is no distributor here and workers are accepting queries */ | |
048607b4 | 1814 | for (unsigned int threadId = 1; threadId <= RecThreadInfo::numWorkers(); threadId++) { |
4f6e00dc | 1815 | RecThreadInfo::info(threadId).tcpSockets = tcpSockets; |
eeaa1cb6 OM |
1816 | } |
1817 | } | |
1818 | } | |
1819 | ||
1820 | #ifdef NOD_ENABLED | |
1821 | // Setup newly observed domain globals | |
1822 | setupNODGlobal(); | |
1823 | #endif /* NOD_ENABLED */ | |
8c1cb8aa | 1824 | |
eeaa1cb6 | 1825 | int forks; |
8c1cb8aa OM |
1826 | for (forks = 0; forks < ::arg().asNum("processes") - 1; ++forks) { |
1827 | if (!fork()) // we are child | |
eeaa1cb6 OM |
1828 | break; |
1829 | } | |
1830 | ||
8c1cb8aa | 1831 | if (::arg().mustDo("daemon")) { |
d8d1d955 OM |
1832 | SLOG(g_log << Logger::Warning << "Calling daemonize, going to background" << endl, |
1833 | log->info(Logr::Warning, "Calling daemonize, going to background")); | |
eeaa1cb6 | 1834 | g_log.toConsole(Logger::Critical); |
37c3c693 | 1835 | daemonize(log); |
eeaa1cb6 | 1836 | } |
8c1cb8aa | 1837 | if (Utility::getpid() == 1) { |
eeaa1cb6 | 1838 | /* We are running as pid 1, register sigterm and sigint handler |
8c1cb8aa | 1839 | |
eeaa1cb6 OM |
1840 | The Linux kernel will handle SIGTERM and SIGINT for all processes, except PID 1. |
1841 | It assumes that the processes running as pid 1 is an "init" like system. | |
1842 | For years, this was a safe assumption, but containers change that: in | |
1843 | most (all?) container implementations, the application itself is running | |
1844 | as pid 1. This means that sending signals to those applications, will not | |
1845 | be handled by default. Results might be "your container not responding | |
1846 | when asking it to stop", or "ctrl-c not working even when the app is | |
1847 | running in the foreground inside a container". | |
1848 | ||
1849 | So TL;DR: If we're running pid 1 (container), we should handle SIGTERM and SIGINT ourselves */ | |
1850 | ||
8c1cb8aa OM |
1851 | signal(SIGTERM, termIntHandler); |
1852 | signal(SIGINT, termIntHandler); | |
1853 | } | |
eeaa1cb6 | 1854 | |
8c1cb8aa OM |
1855 | signal(SIGUSR1, usr1Handler); |
1856 | signal(SIGUSR2, usr2Handler); | |
1857 | signal(SIGPIPE, SIG_IGN); | |
eeaa1cb6 | 1858 | |
37c3c693 | 1859 | checkOrFixFDS(log); |
eeaa1cb6 OM |
1860 | |
1861 | #ifdef HAVE_LIBSODIUM | |
1862 | if (sodium_init() == -1) { | |
d8d1d955 OM |
1863 | SLOG(g_log << Logger::Error << "Unable to initialize sodium crypto library" << endl, |
1864 | log->info(Logr::Error, "Unable to initialize sodium crypto library")); | |
eeaa1cb6 OM |
1865 | exit(99); |
1866 | } | |
1867 | #endif | |
1868 | ||
1869 | openssl_thread_setup(); | |
1870 | openssl_seed(); | |
1871 | /* setup rng before chroot */ | |
1872 | dns_random_init(); | |
1873 | ||
8c1cb8aa | 1874 | if (::arg()["server-id"].empty()) { |
ff32c991 | 1875 | ::arg().set("server-id") = myHostname.has_value() ? *myHostname : ""; |
eeaa1cb6 OM |
1876 | } |
1877 | ||
8c1cb8aa OM |
1878 | int newgid = 0; |
1879 | if (!::arg()["setgid"].empty()) | |
eeaa1cb6 | 1880 | newgid = strToGID(::arg()["setgid"]); |
8c1cb8aa OM |
1881 | int newuid = 0; |
1882 | if (!::arg()["setuid"].empty()) | |
eeaa1cb6 OM |
1883 | newuid = strToUID(::arg()["setuid"]); |
1884 | ||
1885 | Utility::dropGroupPrivs(newuid, newgid); | |
1886 | ||
1887 | if (!::arg()["chroot"].empty()) { | |
1888 | #ifdef HAVE_SYSTEMD | |
8c1cb8aa OM |
1889 | char* ns; |
1890 | ns = getenv("NOTIFY_SOCKET"); | |
1891 | if (ns != nullptr) { | |
d8d1d955 OM |
1892 | SLOG(g_log << Logger::Error << "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'" << endl, |
1893 | log->info(Logr::Error, "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'")); | |
8c1cb8aa OM |
1894 | exit(1); |
1895 | } | |
eeaa1cb6 | 1896 | #endif |
8c1cb8aa OM |
1897 | if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) { |
1898 | int err = errno; | |
d8d1d955 | 1899 | SLOG(g_log << Logger::Error << "Unable to chroot to '" + ::arg()["chroot"] + "': " << strerror(err) << ", exiting" << endl, |
6cb5db59 | 1900 | log->error(Logr::Error, err, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"]))); |
8c1cb8aa | 1901 | exit(1); |
eeaa1cb6 | 1902 | } |
d8d1d955 OM |
1903 | else { |
1904 | SLOG(g_log << Logger::Info << "Chrooted to '" << ::arg()["chroot"] << "'" << endl, | |
6cb5db59 | 1905 | log->info(Logr::Info, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"]))); |
d8d1d955 | 1906 | } |
eeaa1cb6 OM |
1907 | } |
1908 | ||
37c3c693 | 1909 | checkSocketDir(log); |
eeaa1cb6 | 1910 | |
b35191d7 OM |
1911 | g_pidfname = ::arg()["socket-dir"] + "/" + g_programname + ".pid"; |
1912 | if (!g_pidfname.empty()) | |
1913 | unlink(g_pidfname.c_str()); // remove possible old pid file | |
37c3c693 | 1914 | writePid(log); |
eeaa1cb6 | 1915 | |
8c1cb8aa | 1916 | makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks : -1); |
eeaa1cb6 OM |
1917 | |
1918 | Utility::dropUserPrivs(newuid); | |
1919 | try { | |
1920 | /* we might still have capabilities remaining, for example if we have been started as root | |
1921 | without --setuid (please don't do that) or as an unprivileged user with ambient capabilities | |
1922 | like CAP_NET_BIND_SERVICE. | |
1923 | */ | |
1924 | dropCapabilities(); | |
1925 | } | |
8c1cb8aa | 1926 | catch (const std::exception& e) { |
d8d1d955 OM |
1927 | SLOG(g_log << Logger::Warning << e.what() << endl, |
1928 | log->error(Logr::Warning, e.what(), "Could not drop capabilities")); | |
eeaa1cb6 OM |
1929 | } |
1930 | ||
1931 | startLuaConfigDelayedThreads(delayedLuaThreads, g_luaconfs.getCopy().generation); | |
1932 | delayedLuaThreads.rpzPrimaryThreads.clear(); // no longer needed | |
eeaa1cb6 | 1933 | |
e4565646 | 1934 | RecThreadInfo::makeThreadPipes(log); |
eeaa1cb6 | 1935 | |
8c1cb8aa OM |
1936 | g_tcpTimeout = ::arg().asNum("client-tcp-timeout"); |
1937 | g_maxTCPPerClient = ::arg().asNum("max-tcp-per-client"); | |
1938 | g_tcpMaxQueriesPerConn = ::arg().asNum("max-tcp-queries-per-connection"); | |
b35191d7 | 1939 | g_maxUDPQueriesPerRound = ::arg().asNum("max-udp-queries-per-round"); |
eeaa1cb6 OM |
1940 | |
1941 | g_useKernelTimestamp = ::arg().mustDo("protobuf-use-kernel-timestamp"); | |
1942 | ||
1943 | disableStats(StatComponent::API, ::arg()["stats-api-blacklist"]); | |
1944 | disableStats(StatComponent::Carbon, ::arg()["stats-carbon-blacklist"]); | |
1945 | disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-blacklist"]); | |
1946 | disableStats(StatComponent::SNMP, ::arg()["stats-snmp-blacklist"]); | |
1947 | ||
1948 | disableStats(StatComponent::API, ::arg()["stats-api-disabled-list"]); | |
1949 | disableStats(StatComponent::Carbon, ::arg()["stats-carbon-disabled-list"]); | |
1950 | disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-disabled-list"]); | |
1951 | disableStats(StatComponent::SNMP, ::arg()["stats-snmp-disabled-list"]); | |
1952 | ||
1953 | if (::arg().mustDo("snmp-agent")) { | |
de4ee0ee | 1954 | #ifdef HAVE_NET_SNMP |
8c1cb8aa | 1955 | string setting = ::arg()["snmp-daemon-socket"]; |
eeaa1cb6 OM |
1956 | if (setting.empty()) { |
1957 | setting = ::arg()["snmp-master-socket"]; | |
1958 | } | |
1959 | g_snmpAgent = std::make_shared<RecursorSNMPAgent>("recursor", setting); | |
1960 | g_snmpAgent->run(); | |
de4ee0ee OM |
1961 | #else |
1962 | const std::string msg = "snmp-agent set but SNMP support not compiled in"; | |
1963 | SLOG(g_log << Logger::Error << msg << endl, | |
1964 | log->info(Logr::Error, msg)); | |
1965 | #endif // HAVE_NET_SNMP | |
eeaa1cb6 OM |
1966 | } |
1967 | ||
1968 | int port = ::arg().asNum("udp-source-port-min"); | |
8c1cb8aa | 1969 | if (port < 1024 || port > 65535) { |
d8d1d955 OM |
1970 | SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-min is not a valid port number" << endl, |
1971 | log->info(Logr::Error, "Unable to launch, udp-source-port-min is not a valid port number")); | |
eeaa1cb6 OM |
1972 | exit(99); // this isn't going to fix itself either |
1973 | } | |
b35191d7 | 1974 | g_minUdpSourcePort = port; |
eeaa1cb6 | 1975 | port = ::arg().asNum("udp-source-port-max"); |
b35191d7 | 1976 | if (port < 1024 || port > 65535 || port < g_minUdpSourcePort) { |
d8d1d955 OM |
1977 | SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-max is not a valid port number or is smaller than udp-source-port-min" << endl, |
1978 | log->info(Logr::Error, "Unable to launch, udp-source-port-max is not a valid port number or is smaller than udp-source-port-min")); | |
eeaa1cb6 OM |
1979 | exit(99); // this isn't going to fix itself either |
1980 | } | |
b35191d7 | 1981 | g_maxUdpSourcePort = port; |
8c1cb8aa | 1982 | std::vector<string> parts{}; |
eeaa1cb6 | 1983 | stringtok(parts, ::arg()["udp-source-port-avoid"], ", "); |
8c1cb8aa | 1984 | for (const auto& part : parts) { |
eeaa1cb6 | 1985 | port = std::stoi(part); |
8c1cb8aa | 1986 | if (port < 1024 || port > 65535) { |
d8d1d955 | 1987 | SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part << endl, |
6cb5db59 | 1988 | log->info(Logr::Error, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part))); |
eeaa1cb6 OM |
1989 | exit(99); // this isn't going to fix itself either |
1990 | } | |
b35191d7 | 1991 | g_avoidUdpSourcePorts.insert(port); |
eeaa1cb6 OM |
1992 | } |
1993 | ||
d8d1d955 | 1994 | return RecThreadInfo::runThreads(log); |
eeaa1cb6 OM |
1995 | } |
1996 | ||
1997 | static void handlePipeRequest(int fd, FDMultiplexer::funcparam_t& var) | |
1998 | { | |
1999 | ThreadMSG* tmsg = nullptr; | |
2000 | ||
8c1cb8aa | 2001 | if (read(fd, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread || fd == readQueriesToThread |
eeaa1cb6 OM |
2002 | unixDie("read from thread pipe returned wrong size or error"); |
2003 | } | |
2004 | ||
8c1cb8aa | 2005 | void* resp = 0; |
eeaa1cb6 OM |
2006 | try { |
2007 | resp = tmsg->func(); | |
2008 | } | |
8c1cb8aa | 2009 | catch (std::exception& e) { |
62b191dc OM |
2010 | if (g_logCommonErrors) { |
2011 | SLOG(g_log << Logger::Error << "PIPE function we executed created exception: " << e.what() << endl, // but what if they wanted an answer.. we send 0 | |
2012 | g_slog->withName("runtime")->error(Logr::Error, e.what(), "PIPE function we executed created exception", "exception", Logging::Loggable("std::exception"))); | |
2013 | } | |
eeaa1cb6 | 2014 | } |
8c1cb8aa | 2015 | catch (PDNSException& e) { |
62b191dc OM |
2016 | if (g_logCommonErrors) { |
2017 | SLOG(g_log << Logger::Error << "PIPE function we executed created PDNS exception: " << e.reason << endl, // but what if they wanted an answer.. we send 0 | |
2018 | g_slog->withName("runtime")->error(Logr::Error, e.reason, "PIPE function we executed created exception", "exception", Logging::Loggable("PDNSException"))); | |
2019 | } | |
eeaa1cb6 | 2020 | } |
8c1cb8aa | 2021 | if (tmsg->wantAnswer) { |
69b39198 | 2022 | if (write(RecThreadInfo::self().pipes.writeFromThread, &resp, sizeof(resp)) != sizeof(resp)) { |
eeaa1cb6 OM |
2023 | delete tmsg; |
2024 | unixDie("write to thread pipe returned wrong size or error"); | |
2025 | } | |
2026 | } | |
2027 | ||
2028 | delete tmsg; | |
2029 | } | |
2030 | ||
2031 | static void handleRCC(int fd, FDMultiplexer::funcparam_t& var) | |
2032 | { | |
43f91cad | 2033 | auto log = g_slog->withName("control"); |
eeaa1cb6 OM |
2034 | try { |
2035 | FDWrapper clientfd = accept(fd, nullptr, nullptr); | |
2036 | if (clientfd == -1) { | |
2037 | throw PDNSException("accept failed"); | |
2038 | } | |
b35191d7 | 2039 | string msg = g_rcc.recv(clientfd).d_str; |
43f91cad | 2040 | SLOG(g_log << Logger::Info << "Received rec_control command '" << msg << "' via controlsocket" << endl, |
70099a45 | 2041 | log->info(Logr::Info, "Received rec_control command via control socket", "command", Logging::Loggable(msg))); |
eeaa1cb6 OM |
2042 | |
2043 | RecursorControlParser rcp; | |
2044 | RecursorControlParser::func_t* command; | |
2045 | auto answer = rcp.getAnswer(clientfd, msg, &command); | |
2046 | ||
b35191d7 | 2047 | g_rcc.send(clientfd, answer); |
eeaa1cb6 OM |
2048 | command(); |
2049 | } | |
8c1cb8aa | 2050 | catch (const std::exception& e) { |
43f91cad | 2051 | SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << e.what() << endl, |
70099a45 | 2052 | log->error(Logr::Error, e.what(), "Exception while dealing with control socket request", "exception", Logging::Loggable("std::exception"))); |
eeaa1cb6 | 2053 | } |
8c1cb8aa | 2054 | catch (const PDNSException& ae) { |
43f91cad | 2055 | SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << ae.reason << endl, |
70099a45 | 2056 | log->error(Logr::Error, ae.reason, "Exception while dealing with control socket request", "exception", Logging::Loggable("PDNSException"))); |
eeaa1cb6 OM |
2057 | } |
2058 | } | |
b35191d7 | 2059 | |
3e34e2ee | 2060 | class PeriodicTask |
eeaa1cb6 | 2061 | { |
3e34e2ee OM |
2062 | public: |
2063 | PeriodicTask(const string& n, time_t p) : | |
2064 | period{p, 0}, name(n) | |
2065 | { | |
2066 | if (p <= 0) { | |
2067 | throw PDNSException("Invalid period of periodic task " + n); | |
2068 | } | |
2069 | } | |
dc9b4f6e | 2070 | |
3e34e2ee OM |
2071 | void runIfDue(struct timeval& now, const std::function<void()>& f) |
2072 | { | |
2073 | if (last_run < now - period) { | |
2074 | // cerr << RecThreadInfo::id() << ' ' << name << ' ' << now.tv_sec << '.' << now.tv_usec << " running" << endl; | |
2075 | f(); | |
2076 | Utility::gettimeofday(&last_run); | |
2077 | now = last_run; | |
2078 | } | |
2079 | } | |
dc9b4f6e OM |
2080 | |
2081 | time_t getPeriod() const | |
2082 | { | |
2083 | return period.tv_sec; | |
2084 | } | |
2085 | ||
3e34e2ee OM |
2086 | void setPeriod(time_t p) |
2087 | { | |
2088 | period.tv_sec = p; | |
2089 | } | |
2090 | ||
2091 | void updateLastRun() | |
2092 | { | |
2093 | Utility::gettimeofday(&last_run); | |
2094 | } | |
2095 | ||
2096 | bool hasRun() const | |
2097 | { | |
2098 | return last_run.tv_sec != 0 || last_run.tv_usec != 0; | |
2099 | } | |
2100 | ||
2101 | private: | |
2102 | struct timeval last_run | |
aee3ab9f OM |
2103 | { |
2104 | 0, 0 | |
3e34e2ee OM |
2105 | }; |
2106 | struct timeval period; | |
2107 | const string name; | |
2108 | }; | |
2109 | ||
2110 | static void houseKeeping(void*) | |
2111 | { | |
2ed8065e | 2112 | auto log = g_slog->withName("housekeeping"); |
3e34e2ee | 2113 | static thread_local bool t_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work |
eeaa1cb6 | 2114 | |
eeaa1cb6 | 2115 | try { |
b35191d7 | 2116 | if (t_running) { |
eeaa1cb6 OM |
2117 | return; |
2118 | } | |
b35191d7 | 2119 | t_running = true; |
eeaa1cb6 | 2120 | |
3e34e2ee OM |
2121 | struct timeval now; |
2122 | Utility::gettimeofday(&now); | |
f4991c19 | 2123 | |
eeab0dc2 | 2124 | // Below are the tasks that run for every recursorThread, including handler and taskThread |
5abc5e10 | 2125 | if (t_packetCache) { |
5abc5e10 OM |
2126 | static thread_local PeriodicTask packetCacheTask{"packetCacheTask", 5}; |
2127 | packetCacheTask.runIfDue(now, []() { | |
eeab0dc2 | 2128 | size_t sz = g_maxPacketCacheEntries / (RecThreadInfo::numWorkers() + RecThreadInfo::numDistributors()); |
ed7dd19b | 2129 | t_packetCache->setMaxSize(sz); // g_maxPacketCacheEntries might have changed by rec_control |
eeab0dc2 | 2130 | t_packetCache->doPruneTo(sz); |
5abc5e10 OM |
2131 | }); |
2132 | } | |
eeaa1cb6 | 2133 | |
0868ab91 | 2134 | static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5}; |
5da69fb5 | 2135 | pruneTCPTask.runIfDue(now, [now]() { |
0868ab91 OM |
2136 | t_tcp_manager.cleanup(now); |
2137 | }); | |
2138 | ||
f14f56f4 OM |
2139 | const auto& info = RecThreadInfo::self(); |
2140 | ||
babe9430 OM |
2141 | // Threads handling packets process config changes in the input path, but not all threads process input packets |
2142 | // distr threads only process TCP, so that may not happenn very often. So do all periodically. | |
2143 | static thread_local PeriodicTask exportConfigTask{"exportConfigTask", 30}; | |
2144 | auto luaconfsLocal = g_luaconfs.getLocal(); | |
2145 | exportConfigTask.runIfDue(now, [&luaconfsLocal]() { | |
2146 | checkProtobufExport(luaconfsLocal); | |
2147 | checkOutgoingProtobufExport(luaconfsLocal); | |
2148 | #ifdef HAVE_FSTRM | |
2149 | checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo); | |
2150 | checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo); | |
2151 | #endif | |
2152 | }); | |
2153 | ||
3e34e2ee OM |
2154 | // Below are the thread specific tasks for the handler and the taskThread |
2155 | // Likley a few handler tasks could be moved to the taskThread | |
2156 | if (info.isTaskThread()) { | |
2157 | // TaskQueue is run always | |
c42b6632 | 2158 | runTasks(10, g_logCommonErrors); |
3e34e2ee OM |
2159 | |
2160 | static PeriodicTask ztcTask{"ZTC", 60}; | |
2161 | static map<DNSName, RecZoneToCache::State> ztcStates; | |
3e34e2ee | 2162 | ztcTask.runIfDue(now, [&luaconfsLocal]() { |
11871d6c OM |
2163 | RecZoneToCache::maintainStates(luaconfsLocal->ztcConfigs, ztcStates, luaconfsLocal->generation); |
2164 | for (auto& ztc : luaconfsLocal->ztcConfigs) { | |
2165 | RecZoneToCache::ZoneToCache(ztc.second, ztcStates.at(ztc.first)); | |
2166 | } | |
3e34e2ee | 2167 | }); |
f14f56f4 | 2168 | } |
3e34e2ee OM |
2169 | else if (info.isHandler()) { |
2170 | static PeriodicTask recordCachePruneTask{"RecordCachePruneTask", 5}; | |
2171 | recordCachePruneTask.runIfDue(now, []() { | |
eeaa1cb6 | 2172 | g_recCache->doPrune(g_maxCacheEntries); |
3e34e2ee OM |
2173 | }); |
2174 | ||
2175 | static PeriodicTask negCachePruneTask{"NegCachePrunteTask", 5}; | |
2176 | negCachePruneTask.runIfDue(now, []() { | |
eeaa1cb6 | 2177 | g_negCache->prune(g_maxCacheEntries / 10); |
3e34e2ee OM |
2178 | }); |
2179 | ||
2180 | static PeriodicTask aggrNSECPruneTask{"AggrNSECPruneTask", 5}; | |
2181 | aggrNSECPruneTask.runIfDue(now, [now]() { | |
eeaa1cb6 OM |
2182 | if (g_aggressiveNSECCache) { |
2183 | g_aggressiveNSECCache->prune(now.tv_sec); | |
2184 | } | |
3e34e2ee OM |
2185 | }); |
2186 | ||
03d26a97 OM |
2187 | static PeriodicTask pruneNSpeedTask{"pruneNSSpeedTask", 30}; |
2188 | pruneNSpeedTask.runIfDue(now, [now]() { | |
2189 | SyncRes::pruneNSSpeeds(now.tv_sec - 300); | |
2190 | }); | |
2191 | ||
d1241b41 | 2192 | static PeriodicTask pruneEDNSTask{"pruneEDNSTask", 60}; |
f6001854 | 2193 | pruneEDNSTask.runIfDue(now, [now]() { |
3a016e92 | 2194 | SyncRes::pruneEDNSStatuses(now.tv_sec); |
f6001854 OM |
2195 | }); |
2196 | ||
a66fd34f OM |
2197 | if (SyncRes::s_max_busy_dot_probes > 0) { |
2198 | static PeriodicTask pruneDoTProbeMap{"pruneDoTProbeMapTask", 60}; | |
2199 | pruneDoTProbeMap.runIfDue(now, [now]() { | |
2200 | SyncRes::pruneDoTProbeMap(now.tv_sec); | |
2201 | }); | |
2202 | } | |
2203 | ||
e0d0cce7 OM |
2204 | static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5}; |
2205 | pruneThrottledTask.runIfDue(now, [now]() { | |
2206 | SyncRes::pruneThrottledServers(now.tv_sec); | |
2207 | }); | |
2208 | ||
3e34e2ee OM |
2209 | static PeriodicTask pruneFailedServersTask{"pruneFailedServerTask", 5}; |
2210 | pruneFailedServersTask.runIfDue(now, [now]() { | |
472cba76 | 2211 | SyncRes::pruneFailedServers(now.tv_sec - SyncRes::s_serverdownthrottletime * 10); |
3e34e2ee OM |
2212 | }); |
2213 | ||
2214 | static PeriodicTask pruneNonResolvingTask{"pruneNonResolvingTask", 5}; | |
2215 | pruneNonResolvingTask.runIfDue(now, [now]() { | |
472cba76 | 2216 | SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime); |
3e34e2ee OM |
2217 | }); |
2218 | ||
4e45e5bc O |
2219 | static PeriodicTask pruneSaveParentSetTask{"pruneSaveParentSetTask", 60}; |
2220 | pruneSaveParentSetTask.runIfDue(now, [now]() { | |
2221 | SyncRes::pruneSaveParentsNSSets(now.tv_sec); | |
2222 | }); | |
2223 | ||
dc9b4f6e OM |
2224 | // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s |
2225 | const unsigned int minRootRefreshInterval = 10; | |
2226 | static PeriodicTask rootUpdateTask{"rootUpdateTask", std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval)}; | |
2ed8065e OM |
2227 | rootUpdateTask.runIfDue(now, [now, &log, minRootRefreshInterval]() { |
2228 | int res = SyncRes::getRootNS(now, nullptr, 0, log); | |
e46b0f2f | 2229 | if (res == 0) { |
dc9b4f6e OM |
2230 | // Success, go back to the defaut period |
2231 | rootUpdateTask.setPeriod(std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval)); | |
9ddda9ed | 2232 | const string msg = "Exception while priming the root NS zones"; |
eeaa1cb6 | 2233 | try { |
e46b0f2f | 2234 | primeRootNSZones(g_dnssecmode, 0); |
eeaa1cb6 OM |
2235 | } |
2236 | catch (const std::exception& e) { | |
2ed8065e | 2237 | SLOG(g_log << Logger::Error << "Exception while priming the root NS zones: " << e.what() << endl, |
9ddda9ed | 2238 | log->error(Logr::Error, e.what(), msg, "exception", Logging::Loggable("std::exception"))); |
eeaa1cb6 OM |
2239 | } |
2240 | catch (const PDNSException& e) { | |
2ed8065e | 2241 | SLOG(g_log << Logger::Error << "Exception while priming the root NS zones: " << e.reason << endl, |
9ddda9ed | 2242 | log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("PDNSException"))); |
eeaa1cb6 OM |
2243 | } |
2244 | catch (const ImmediateServFailException& e) { | |
2ed8065e | 2245 | SLOG(g_log << Logger::Error << "Exception while priming the root NS zones: " << e.reason << endl, |
9ddda9ed | 2246 | log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException"))); |
eeaa1cb6 OM |
2247 | } |
2248 | catch (const PolicyHitException& e) { | |
2ed8065e | 2249 | SLOG(g_log << Logger::Error << "Policy hit while priming the root NS zones" << endl, |
9ddda9ed | 2250 | log->info(Logr::Error, msg, "exception", Logging::Loggable("PolicyHitException"))); |
eeaa1cb6 | 2251 | } |
8c1cb8aa | 2252 | catch (...) { |
2ed8065e OM |
2253 | SLOG(g_log << Logger::Error << "Exception while priming the root NS zones" << endl, |
2254 | log->info(Logr::Error, "Exception while priming the root NS zones")); | |
eeaa1cb6 OM |
2255 | } |
2256 | } | |
dc9b4f6e OM |
2257 | else { |
2258 | // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each | |
2259 | // failure by dividing the existing interval by 8, keeping the minimum interval at 10s. | |
2260 | // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ... | |
2261 | rootUpdateTask.setPeriod(std::max<time_t>(rootUpdateTask.getPeriod() / 8, minRootRefreshInterval)); | |
2262 | } | |
3e34e2ee | 2263 | }); |
eeaa1cb6 | 2264 | |
3e34e2ee OM |
2265 | static PeriodicTask secpollTask{"secpollTask", 3600}; |
2266 | static time_t t_last_secpoll; | |
2ed8065e | 2267 | secpollTask.runIfDue(now, [&log]() { |
8c1cb8aa | 2268 | try { |
2ed8065e | 2269 | doSecPoll(&t_last_secpoll, log); |
eeaa1cb6 | 2270 | } |
8c1cb8aa | 2271 | catch (const std::exception& e) { |
2ed8065e OM |
2272 | SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.what() << endl, |
2273 | log->error(Logr::Error, e.what(), "Exception while performing security poll")); | |
eeaa1cb6 | 2274 | } |
8c1cb8aa | 2275 | catch (const PDNSException& e) { |
2ed8065e OM |
2276 | SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl, |
2277 | log->error(Logr::Error, e.reason, "Exception while performing security poll")); | |
8c1cb8aa OM |
2278 | } |
2279 | catch (const ImmediateServFailException& e) { | |
2ed8065e OM |
2280 | SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl, |
2281 | log->error(Logr::Error, e.reason, "Exception while performing security poll")); | |
eeaa1cb6 OM |
2282 | } |
2283 | catch (const PolicyHitException& e) { | |
2ed8065e OM |
2284 | SLOG(g_log << Logger::Error << "Policy hit while performing security poll" << endl, |
2285 | log->info(Logr::Error, "Policy hit while performing security poll")); | |
eeaa1cb6 | 2286 | } |
8c1cb8aa | 2287 | catch (...) { |
2ed8065e OM |
2288 | SLOG(g_log << Logger::Error << "Exception while performing security poll" << endl, |
2289 | log->info(Logr::Error, "Exception while performing security poll")); | |
eeaa1cb6 | 2290 | } |
3e34e2ee | 2291 | }); |
eeaa1cb6 | 2292 | |
3e34e2ee OM |
2293 | static PeriodicTask trustAnchorTask{"trustAnchorTask", std::max(1U, luaconfsLocal->trustAnchorFileInfo.interval) * 3600}; |
2294 | if (!trustAnchorTask.hasRun()) { | |
2295 | // Loading the Lua config file already "refreshed" the TAs | |
2296 | trustAnchorTask.updateLastRun(); | |
2297 | } | |
2298 | // interval might have ben updated | |
2299 | trustAnchorTask.setPeriod(std::max(1U, luaconfsLocal->trustAnchorFileInfo.interval) * 3600); | |
2ed8065e | 2300 | trustAnchorTask.runIfDue(now, [&luaconfsLocal, &log]() { |
3e34e2ee | 2301 | if (!luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) { |
2ed8065e OM |
2302 | SLOG(g_log << Logger::Debug << "Refreshing Trust Anchors from file" << endl, |
2303 | log->info(Logr::Debug, "Refreshing Trust Anchors from file")); | |
3e34e2ee OM |
2304 | try { |
2305 | map<DNSName, dsmap_t> dsAnchors; | |
2ed8065e | 2306 | if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors, log)) { |
3e34e2ee OM |
2307 | g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) { |
2308 | lci.dsAnchors = dsAnchors; | |
2309 | }); | |
2310 | } | |
2311 | } | |
2312 | catch (const PDNSException& pe) { | |
2ed8065e OM |
2313 | SLOG(g_log << Logger::Error << "Unable to update Trust Anchors: " << pe.reason << endl, |
2314 | log->error(Logr::Error, pe.reason, "Unable to update Trust Anchors")); | |
eeaa1cb6 | 2315 | } |
eeaa1cb6 | 2316 | } |
3e34e2ee | 2317 | }); |
eeaa1cb6 | 2318 | } |
b35191d7 | 2319 | t_running = false; |
eeaa1cb6 | 2320 | } |
8c1cb8aa | 2321 | catch (const PDNSException& ae) { |
b35191d7 | 2322 | t_running = false; |
2ed8065e OM |
2323 | SLOG(g_log << Logger::Error << "Fatal error in housekeeping thread: " << ae.reason << endl, |
2324 | log->error(Logr::Error, ae.reason, "Fatal error in housekeeping thread")); | |
eeaa1cb6 OM |
2325 | throw; |
2326 | } | |
8c1cb8aa | 2327 | catch (...) { |
b35191d7 | 2328 | t_running = false; |
2ed8065e OM |
2329 | SLOG(g_log << Logger::Error << "Uncaught exception in housekeeping thread" << endl, |
2330 | log->info(Logr::Error, "Uncaught exception in housekeeping thread")); | |
eeaa1cb6 OM |
2331 | throw; |
2332 | } | |
2333 | } | |
2334 | ||
a0a3a61a | 2335 | static void recursorThread() |
69b39198 | 2336 | { |
37c3c693 | 2337 | auto log = g_slog->withName("runtime"); |
69b39198 OM |
2338 | try { |
2339 | auto& threadInfo = RecThreadInfo::self(); | |
2340 | { | |
2341 | SyncRes tmp(g_now); // make sure it allocates tsstorage before we do anything, like primeHints or so.. | |
2342 | SyncRes::setDomainMap(g_initialDomainMap); | |
2343 | t_allowFrom = g_initialAllowFrom; | |
2344 | t_allowNotifyFrom = g_initialAllowNotifyFrom; | |
2345 | t_allowNotifyFor = g_initialAllowNotifyFor; | |
2346 | t_udpclientsocks = std::make_unique<UDPClientSocks>(); | |
2347 | t_tcpClientCounts = std::make_unique<tcpClientCounts_t>(); | |
c375521b OM |
2348 | if (g_proxyMapping) { |
2349 | t_proxyMapping = make_unique<ProxyMapping>(*g_proxyMapping); | |
2350 | } | |
2351 | else { | |
2352 | t_proxyMapping = nullptr; | |
2353 | } | |
69b39198 OM |
2354 | |
2355 | if (threadInfo.isHandler()) { | |
2356 | if (!primeHints()) { | |
3487974d | 2357 | threadInfo.setExitCode(EXIT_FAILURE); |
69b39198 | 2358 | RecursorControlChannel::stop = 1; |
2ed8065e OM |
2359 | SLOG(g_log << Logger::Critical << "Priming cache failed, stopping" << endl, |
2360 | log->info(Logr::Critical, "Priming cache failed, stopping")); | |
69b39198 | 2361 | } |
2ed8065e OM |
2362 | SLOG(g_log << Logger::Debug << "Done priming cache with root hints" << endl, |
2363 | log->info(Logr::Debug, "Done priming cache with root hints")); | |
69b39198 | 2364 | } |
eeaa1cb6 | 2365 | } |
eeaa1cb6 | 2366 | |
5abc5e10 | 2367 | if (!::arg().mustDo("disable-packetcache") && (threadInfo.isDistributor() || threadInfo.isWorker())) { |
ed7dd19b | 2368 | // Only enable packet cache for thread processing queries from the outside world |
eeab0dc2 | 2369 | t_packetCache = std::make_unique<RecursorPacketCache>(g_maxPacketCacheEntries / (RecThreadInfo::numWorkers() + RecThreadInfo::numDistributors())); |
5abc5e10 | 2370 | } |
eeaa1cb6 OM |
2371 | |
2372 | #ifdef NOD_ENABLED | |
69b39198 | 2373 | if (threadInfo.isWorker()) |
37c3c693 | 2374 | setupNODThread(log); |
eeaa1cb6 OM |
2375 | #endif /* NOD_ENABLED */ |
2376 | ||
69b39198 OM |
2377 | /* the listener threads handle TCP queries */ |
2378 | if (threadInfo.isWorker() || threadInfo.isListener()) { | |
2379 | try { | |
2380 | if (!::arg()["lua-dns-script"].empty()) { | |
2381 | t_pdl = std::make_shared<RecursorLua4>(); | |
cd2d95cd | 2382 | t_pdl->loadFile(::arg()["lua-dns-script"]); |
2ed8065e OM |
2383 | SLOG(g_log << Logger::Warning << "Loaded 'lua' script from '" << ::arg()["lua-dns-script"] << "'" << endl, |
2384 | log->info(Logr::Warning, "Loading Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"]))); | |
69b39198 OM |
2385 | } |
2386 | } | |
2387 | catch (std::exception& e) { | |
2ed8065e OM |
2388 | SLOG(g_log << Logger::Error << "Failed to load 'lua' script from '" << ::arg()["lua-dns-script"] << "': " << e.what() << endl, |
2389 | log->error(Logr::Error, e.what(), "Failed to load Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"]))); | |
69b39198 | 2390 | _exit(99); |
eeaa1cb6 OM |
2391 | } |
2392 | } | |
eeaa1cb6 | 2393 | |
048607b4 | 2394 | unsigned int ringsize = ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numWorkers(); |
69b39198 OM |
2395 | if (ringsize) { |
2396 | t_remotes = std::make_unique<addrringbuf_t>(); | |
048607b4 OM |
2397 | if (RecThreadInfo::weDistributeQueries()) |
2398 | t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors()); | |
69b39198 OM |
2399 | else |
2400 | t_remotes->set_capacity(ringsize); | |
2401 | t_servfailremotes = std::make_unique<addrringbuf_t>(); | |
2402 | t_servfailremotes->set_capacity(ringsize); | |
2403 | t_bogusremotes = std::make_unique<addrringbuf_t>(); | |
2404 | t_bogusremotes->set_capacity(ringsize); | |
2405 | t_largeanswerremotes = std::make_unique<addrringbuf_t>(); | |
2406 | t_largeanswerremotes->set_capacity(ringsize); | |
2407 | t_timeouts = std::make_unique<addrringbuf_t>(); | |
2408 | t_timeouts->set_capacity(ringsize); | |
2409 | ||
2410 | t_queryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>(); | |
2411 | t_queryring->set_capacity(ringsize); | |
2412 | t_servfailqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>(); | |
2413 | t_servfailqueryring->set_capacity(ringsize); | |
2414 | t_bogusqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>(); | |
2415 | t_bogusqueryring->set_capacity(ringsize); | |
2416 | } | |
2417 | MT = std::make_unique<MT_t>(::arg().asNum("stack-size")); | |
2418 | threadInfo.mt = MT.get(); | |
2419 | ||
2420 | /* start protobuf export threads if needed */ | |
2421 | auto luaconfsLocal = g_luaconfs.getLocal(); | |
2422 | checkProtobufExport(luaconfsLocal); | |
2423 | checkOutgoingProtobufExport(luaconfsLocal); | |
eeaa1cb6 | 2424 | #ifdef HAVE_FSTRM |
9489e2b5 CHB |
2425 | checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo); |
2426 | checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo); | |
eeaa1cb6 OM |
2427 | #endif |
2428 | ||
37c3c693 | 2429 | t_fdm = unique_ptr<FDMultiplexer>(getMultiplexer(log)); |
eeaa1cb6 | 2430 | |
a5040968 | 2431 | std::unique_ptr<RecursorWebServer> rws; |
eeaa1cb6 | 2432 | |
69b39198 | 2433 | t_fdm->addReadFD(threadInfo.pipes.readToThread, handlePipeRequest); |
eeaa1cb6 | 2434 | |
69b39198 OM |
2435 | if (threadInfo.isHandler()) { |
2436 | if (::arg().mustDo("webserver")) { | |
62b191dc OM |
2437 | SLOG(g_log << Logger::Warning << "Enabling web server" << endl, |
2438 | log->info(Logr::Info, "Enabling web server")) | |
69b39198 | 2439 | try { |
a5040968 | 2440 | rws = make_unique<RecursorWebServer>(t_fdm.get()); |
69b39198 OM |
2441 | } |
2442 | catch (const PDNSException& e) { | |
62b191dc OM |
2443 | SLOG(g_log << Logger::Error << "Unable to start the internal web server: " << e.reason << endl, |
2444 | log->error(Logr::Critical, e.reason, "Exception while starting internal web server")); | |
69b39198 OM |
2445 | _exit(99); |
2446 | } | |
eeaa1cb6 | 2447 | } |
62b191dc OM |
2448 | SLOG(g_log << Logger::Info << "Enabled '" << t_fdm->getName() << "' multiplexer" << endl, |
2449 | log->info(Logr::Info, "Enabled multiplexer", "name", Logging::Loggable(t_fdm->getName()))); | |
eeaa1cb6 | 2450 | } |
69b39198 OM |
2451 | else { |
2452 | t_fdm->addReadFD(threadInfo.pipes.readQueriesToThread, handlePipeRequest); | |
eeaa1cb6 | 2453 | |
69b39198 OM |
2454 | if (threadInfo.isListener()) { |
2455 | if (g_reusePort) { | |
2456 | /* then every listener has its own FDs */ | |
2457 | for (const auto& deferred : threadInfo.deferredAdds) { | |
2458 | t_fdm->addReadFD(deferred.first, deferred.second); | |
2459 | } | |
eeaa1cb6 | 2460 | } |
69b39198 OM |
2461 | else { |
2462 | /* otherwise all listeners are listening on the same ones */ | |
2463 | for (const auto& deferred : g_deferredAdds) { | |
2464 | t_fdm->addReadFD(deferred.first, deferred.second); | |
2465 | } | |
eeaa1cb6 OM |
2466 | } |
2467 | } | |
2468 | } | |
eeaa1cb6 | 2469 | |
69b39198 | 2470 | registerAllStats(); |
eeaa1cb6 | 2471 | |
69b39198 OM |
2472 | if (threadInfo.isHandler()) { |
2473 | t_fdm->addReadFD(g_rcc.d_fd, handleRCC); // control channel | |
2474 | } | |
eeaa1cb6 | 2475 | |
69b39198 | 2476 | unsigned int maxTcpClients = ::arg().asNum("max-tcp-clients"); |
eeaa1cb6 | 2477 | |
a5040968 | 2478 | bool listenOnTCP{true}; |
eeaa1cb6 | 2479 | |
69b39198 OM |
2480 | time_t last_stat = 0; |
2481 | time_t last_carbon = 0, last_lua_maintenance = 0; | |
2482 | time_t carbonInterval = ::arg().asNum("carbon-interval"); | |
2483 | time_t luaMaintenanceInterval = ::arg().asNum("lua-maintenance-interval"); | |
2484 | s_counter.store(0); // used to periodically execute certain tasks | |
eeaa1cb6 | 2485 | |
b441c2fd OM |
2486 | #ifdef HAVE_SYSTEMD |
2487 | if (threadInfo.isHandler()) { | |
2488 | // There is a race, as some threads might not be ready yet to do work. | |
2489 | // To solve that, threads should notify RecThreadInfo they are done initializing. | |
2490 | // But we lack a mechanism for that at this point in time. | |
2491 | sd_notify(0, "READY=1"); | |
2492 | } | |
2493 | #endif | |
2494 | ||
69b39198 OM |
2495 | while (!RecursorControlChannel::stop) { |
2496 | while (MT->schedule(&g_now)) | |
2497 | ; // MTasker letting the mthreads do their thing | |
eeaa1cb6 | 2498 | |
69b39198 OM |
2499 | // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern. |
2500 | // We want to call handler thread often, it gets scheduled about 2 times per second | |
f14f56f4 | 2501 | if (((threadInfo.isHandler() || threadInfo.isTaskThread()) && s_counter % 11 == 0) || s_counter % 499 == 0) { |
67daf77f OM |
2502 | struct timeval start; |
2503 | Utility::gettimeofday(&start); | |
2ed8065e | 2504 | MT->makeThread(houseKeeping, nullptr); |
67daf77f OM |
2505 | if (!threadInfo.isTaskThread()) { |
2506 | struct timeval stop; | |
2507 | Utility::gettimeofday(&stop); | |
2508 | g_stats.maintenanceUsec += uSec(stop - start); | |
2509 | ++g_stats.maintenanceCalls; | |
2510 | } | |
69b39198 | 2511 | } |
eeaa1cb6 | 2512 | |
69b39198 OM |
2513 | if (!(s_counter % 55)) { |
2514 | typedef vector<pair<int, FDMultiplexer::funcparam_t>> expired_t; | |
2515 | expired_t expired = t_fdm->getTimeouts(g_now); | |
eeaa1cb6 | 2516 | |
69b39198 OM |
2517 | for (expired_t::iterator i = expired.begin(); i != expired.end(); ++i) { |
2518 | shared_ptr<TCPConnection> conn = boost::any_cast<shared_ptr<TCPConnection>>(i->second); | |
2519 | if (g_logCommonErrors) | |
ab26d8d5 | 2520 | SLOG(g_log << Logger::Warning << "Timeout from remote TCP client " << conn->d_remote.toStringWithPort() << endl, |
91092a9f | 2521 | g_slogtcpin->info(Logr::Warning, "Timeout from remote TCP client", "remote", Logging::Loggable(conn->d_remote))); |
69b39198 OM |
2522 | t_fdm->removeReadFD(i->first); |
2523 | } | |
eeaa1cb6 | 2524 | } |
eeaa1cb6 | 2525 | |
69b39198 | 2526 | s_counter++; |
eeaa1cb6 | 2527 | |
69b39198 OM |
2528 | if (threadInfo.isHandler()) { |
2529 | if (statsWanted || (s_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= s_statisticsInterval)) { | |
2530 | doStats(); | |
2531 | last_stat = g_now.tv_sec; | |
2532 | } | |
eeaa1cb6 | 2533 | |
69b39198 | 2534 | Utility::gettimeofday(&g_now, nullptr); |
eeaa1cb6 | 2535 | |
69b39198 OM |
2536 | if ((g_now.tv_sec - last_carbon) >= carbonInterval) { |
2537 | MT->makeThread(doCarbonDump, 0); | |
2538 | last_carbon = g_now.tv_sec; | |
2539 | } | |
eeaa1cb6 | 2540 | } |
69b39198 OM |
2541 | if (t_pdl != nullptr) { |
2542 | // lua-dns-script directive is present, call the maintenance callback if needed | |
2543 | /* remember that the listener threads handle TCP queries */ | |
2544 | if (threadInfo.isWorker() || threadInfo.isListener()) { | |
2545 | // Only on threads processing queries | |
2546 | if (g_now.tv_sec - last_lua_maintenance >= luaMaintenanceInterval) { | |
67daf77f OM |
2547 | struct timeval start; |
2548 | Utility::gettimeofday(&start); | |
69b39198 OM |
2549 | t_pdl->maintenance(); |
2550 | last_lua_maintenance = g_now.tv_sec; | |
67daf77f OM |
2551 | struct timeval stop; |
2552 | Utility::gettimeofday(&stop); | |
2553 | g_stats.maintenanceUsec += uSec(stop - start); | |
2554 | ++g_stats.maintenanceCalls; | |
69b39198 | 2555 | } |
eeaa1cb6 OM |
2556 | } |
2557 | } | |
eeaa1cb6 | 2558 | |
69b39198 OM |
2559 | t_fdm->run(&g_now); |
2560 | // 'run' updates g_now for us | |
eeaa1cb6 | 2561 | |
69b39198 OM |
2562 | if (threadInfo.isListener()) { |
2563 | if (listenOnTCP) { | |
2564 | if (TCPConnection::getCurrentConnections() > maxTcpClients) { // shutdown, too many connections | |
2565 | for (const auto fd : threadInfo.tcpSockets) { | |
2566 | t_fdm->removeReadFD(fd); | |
2567 | } | |
2568 | listenOnTCP = false; | |
eeaa1cb6 | 2569 | } |
eeaa1cb6 | 2570 | } |
69b39198 OM |
2571 | else { |
2572 | if (TCPConnection::getCurrentConnections() <= maxTcpClients) { // reenable | |
2573 | for (const auto fd : threadInfo.tcpSockets) { | |
2574 | t_fdm->addReadFD(fd, handleNewTCPQuestion); | |
2575 | } | |
2576 | listenOnTCP = true; | |
eeaa1cb6 | 2577 | } |
eeaa1cb6 OM |
2578 | } |
2579 | } | |
2580 | } | |
69b39198 OM |
2581 | } |
2582 | catch (PDNSException& ae) { | |
62b191dc OM |
2583 | SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl, |
2584 | log->error(Logr::Error, ae.reason, "Exception in RecursorThread", "exception", Logging::Loggable("PDNSException"))) | |
69b39198 OM |
2585 | } |
2586 | catch (std::exception& e) { | |
62b191dc OM |
2587 | SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl, |
2588 | log->error(Logr::Error, e.what(), "Exception in RecursorThread", "exception", Logging::Loggable("std::exception"))) | |
69b39198 OM |
2589 | } |
2590 | catch (...) { | |
62b191dc OM |
2591 | SLOG(g_log << Logger::Error << "any other exception in main: " << endl, |
2592 | log->info(Logr::Error, "Exception in RecursorThread")); | |
eeaa1cb6 | 2593 | } |
eeaa1cb6 OM |
2594 | } |
2595 | ||
8c1cb8aa | 2596 | int main(int argc, char** argv) |
eeaa1cb6 OM |
2597 | { |
2598 | g_argc = argc; | |
2599 | g_argv = argv; | |
eeaa1cb6 OM |
2600 | Utility::srandom(); |
2601 | versionSetProduct(ProductRecursor); | |
2602 | reportBasicTypes(); | |
2603 | reportOtherTypes(); | |
2604 | ||
2605 | int ret = EXIT_SUCCESS; | |
2606 | ||
2607 | try { | |
2608 | #if HAVE_FIBER_SANITIZER | |
2609 | // Asan needs more stack | |
8c1cb8aa | 2610 | ::arg().set("stack-size", "stack size per mthread") = "400000"; |
eeaa1cb6 | 2611 | #else |
8c1cb8aa | 2612 | ::arg().set("stack-size", "stack size per mthread") = "200000"; |
eeaa1cb6 | 2613 | #endif |
8c1cb8aa OM |
2614 | ::arg().set("soa-minimum-ttl", "Don't change") = "0"; |
2615 | ::arg().set("no-shuffle", "Don't change") = "off"; | |
2616 | ::arg().set("local-port", "port to listen on") = "53"; | |
2617 | ::arg().set("local-address", "IP addresses to listen on, separated by spaces or commas. Also accepts ports.") = "127.0.0.1"; | |
2618 | ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options") = "no"; | |
2619 | ::arg().set("trace", "if we should output heaps of logging. set to 'fail' to only log failing domains") = "off"; | |
2620 | ::arg().set("dnssec", "DNSSEC mode: off/process-no-validate/process (default)/log-fail/validate") = "process"; | |
2621 | ::arg().set("dnssec-log-bogus", "Log DNSSEC bogus validations") = "no"; | |
2622 | ::arg().set("signature-inception-skew", "Allow the signature inception to be off by this number of seconds") = "60"; | |
2623 | ::arg().set("daemon", "Operate as a daemon") = "no"; | |
2624 | ::arg().setSwitch("write-pid", "Write a PID file") = "yes"; | |
2625 | ::arg().set("loglevel", "Amount of logging. Higher is more. Do not set below 3") = "6"; | |
2626 | ::arg().set("disable-syslog", "Disable logging to syslog, useful when running inside a supervisor that logs stdout") = "no"; | |
2627 | ::arg().set("log-timestamp", "Print timestamps in log lines, useful to disable when running with a tool that timestamps stdout already") = "yes"; | |
2628 | ::arg().set("log-common-errors", "If we should log rather common errors") = "no"; | |
2629 | ::arg().set("chroot", "switch to chroot jail") = ""; | |
2630 | ::arg().set("setgid", "If set, change group id to this gid for more security" | |
eeaa1cb6 OM |
2631 | #ifdef HAVE_SYSTEMD |
2632 | #define SYSTEMD_SETID_MSG ". When running inside systemd, use the User and Group settings in the unit-file!" | |
8c1cb8aa | 2633 | SYSTEMD_SETID_MSG |
eeaa1cb6 | 2634 | #endif |
8c1cb8aa OM |
2635 | ) |
2636 | = ""; | |
2637 | ::arg().set("setuid", "If set, change user id to this uid for more security" | |
eeaa1cb6 | 2638 | #ifdef HAVE_SYSTEMD |
8c1cb8aa | 2639 | SYSTEMD_SETID_MSG |
eeaa1cb6 | 2640 | #endif |
8c1cb8aa OM |
2641 | ) |
2642 | = ""; | |
2643 | ::arg().set("network-timeout", "Wait this number of milliseconds for network i/o") = "1500"; | |
2644 | ::arg().set("threads", "Launch this number of threads") = "2"; | |
2645 | ::arg().set("distributor-threads", "Launch this number of distributor threads, distributing queries to other threads") = "0"; | |
2646 | ::arg().set("processes", "Launch this number of processes (EXPERIMENTAL, DO NOT CHANGE)") = "1"; // if we un-experimental this, need to fix openssl rand seeding for multiple PIDs! | |
2647 | ::arg().set("config-name", "Name of this virtual configuration - will rename the binary image") = ""; | |
eeaa1cb6 OM |
2648 | ::arg().set("api-config-dir", "Directory where REST API stores config and zones") = ""; |
2649 | ::arg().set("api-key", "Static pre-shared authentication key for access to the REST API") = ""; | |
2650 | ::arg().setSwitch("webserver", "Start a webserver (for REST API)") = "no"; | |
2651 | ::arg().set("webserver-address", "IP Address of webserver to listen on") = "127.0.0.1"; | |
2652 | ::arg().set("webserver-port", "Port of webserver to listen on") = "8082"; | |
2653 | ::arg().set("webserver-password", "Password required for accessing the webserver") = ""; | |
8c1cb8aa | 2654 | ::arg().set("webserver-allow-from", "Webserver access is only allowed from these subnets") = "127.0.0.1,::1"; |
eeaa1cb6 | 2655 | ::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal"; |
8c1cb8aa OM |
2656 | ::arg().setSwitch("webserver-hash-plaintext-credentials", "Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime") = "no"; |
2657 | ::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats") = ""; | |
2658 | ::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server IP address") = ""; | |
2659 | ::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates") = "30"; | |
2660 | ::arg().set("carbon-namespace", "If set overwrites the first part of the carbon string") = "pdns"; | |
2661 | ::arg().set("carbon-instance", "If set overwrites the instance name default") = "recursor"; | |
2662 | ||
2663 | ::arg().set("statistics-interval", "Number of seconds between printing of recursor statistics, 0 to disable") = "1800"; | |
2664 | ::arg().set("quiet", "Suppress logging of questions and answers") = ""; | |
2665 | ::arg().set("logging-facility", "Facility to log messages as. 0 corresponds to local0") = ""; | |
2666 | ::arg().set("config-dir", "Location of configuration directory (recursor.conf)") = SYSCONFDIR; | |
2667 | ::arg().set("socket-owner", "Owner of socket") = ""; | |
2668 | ::arg().set("socket-group", "Group of socket") = ""; | |
2669 | ::arg().set("socket-mode", "Permissions for socket") = ""; | |
2670 | ||
2671 | ::arg().set("socket-dir", string("Where the controlsocket will live, ") + LOCALSTATEDIR + "/pdns-recursor when unset and not chrooted" | |
eeaa1cb6 | 2672 | #ifdef HAVE_SYSTEMD |
8c1cb8aa OM |
2673 | + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).") |
2674 | = ""; | |
2675 | auto runtimeDir = getenv("RUNTIME_DIRECTORY"); | |
2676 | if (runtimeDir != nullptr) { | |
2677 | ::arg().set("socket-dir") = runtimeDir; | |
2678 | } | |
eeaa1cb6 | 2679 | #else |
8c1cb8aa OM |
2680 | ) |
2681 | = ""; | |
eeaa1cb6 | 2682 | #endif |
8c1cb8aa OM |
2683 | ::arg().set("query-local-address", "Source IP address for sending queries") = "0.0.0.0"; |
2684 | ::arg().set("client-tcp-timeout", "Timeout in seconds when talking to TCP clients") = "2"; | |
2685 | ::arg().set("max-mthreads", "Maximum number of simultaneous Mtasker threads") = "2048"; | |
2686 | ::arg().set("max-tcp-clients", "Maximum number of simultaneous TCP clients") = "128"; | |
eeaa1cb6 | 2687 | ::arg().set("max-concurrent-requests-per-tcp-connection", "Maximum number of requests handled concurrently per TCP connection") = "10"; |
8c1cb8aa OM |
2688 | ::arg().set("server-down-max-fails", "Maximum number of consecutive timeouts (and unreachables) to mark a server as down ( 0 => disabled )") = "64"; |
2689 | ::arg().set("server-down-throttle-time", "Number of seconds to throttle all queries to a server after being marked as down") = "60"; | |
2690 | ::arg().set("dont-throttle-names", "Do not throttle nameservers with this name or suffix") = ""; | |
2691 | ::arg().set("dont-throttle-netmasks", "Do not throttle nameservers with this IP netmask") = ""; | |
2692 | ::arg().set("non-resolving-ns-max-fails", "Number of failed address resolves of a nameserver to start throttling it, 0 is disabled") = "5"; | |
2693 | ::arg().set("non-resolving-ns-throttle-time", "Number of seconds to throttle a nameserver with a name failing to resolve") = "60"; | |
2694 | ||
2695 | ::arg().set("hint-file", "If set, load root hints from this file") = ""; | |
2696 | ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache") = "1000000"; | |
2697 | ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory") = "3600"; | |
2698 | ::arg().set("max-cache-bogus-ttl", "maximum number of seconds to keep a Bogus (positive or negative) cached entry in memory") = "3600"; | |
2699 | ::arg().set("max-cache-ttl", "maximum number of seconds to keep a cached entry in memory") = "86400"; | |
2700 | ::arg().set("packetcache-ttl", "maximum number of seconds to keep a cached entry in packetcache") = "3600"; | |
2701 | ::arg().set("max-packetcache-entries", "maximum number of entries to keep in the packetcache") = "500000"; | |
2702 | ::arg().set("packetcache-servfail-ttl", "maximum number of seconds to keep a cached servfail entry in packetcache") = "60"; | |
2703 | ::arg().set("server-id", "Returned when queried for 'id.server' TXT or NSID, defaults to hostname, set custom or 'disabled'") = ""; | |
2704 | ::arg().set("stats-ringbuffer-entries", "maximum number of packets to store statistics for") = "10000"; | |
2705 | ::arg().set("version-string", "string reported on version.pdns or version.bind") = fullVersionString(); | |
2706 | ::arg().set("allow-from", "If set, only allow these comma separated netmasks to recurse") = LOCAL_NETS; | |
2707 | ::arg().set("allow-from-file", "If set, load allowed netmasks from this file") = ""; | |
2708 | ::arg().set("allow-notify-for", "If set, NOTIFY requests for these zones will be allowed") = ""; | |
2709 | ::arg().set("allow-notify-for-file", "If set, load NOTIFY-allowed zones from this file") = ""; | |
2710 | ::arg().set("allow-notify-from", "If set, NOTIFY requests from these comma separated netmasks will be allowed") = ""; | |
2711 | ::arg().set("allow-notify-from-file", "If set, load NOTIFY-allowed netmasks from this file") = ""; | |
2712 | ::arg().set("entropy-source", "If set, read entropy from this file") = "/dev/urandom"; | |
2713 | ::arg().set("dont-query", "If set, do not query these netmasks for DNS data") = DONT_QUERY; | |
2714 | ::arg().set("max-tcp-per-client", "If set, maximum number of TCP sessions per client (IP address)") = "0"; | |
2715 | ::arg().set("max-tcp-queries-per-connection", "If set, maximum number of TCP queries in a TCP connection") = "0"; | |
2716 | ::arg().set("spoof-nearmiss-max", "If non-zero, assume spoofing after this many near misses") = "1"; | |
2717 | ::arg().set("single-socket", "If set, only use a single socket for outgoing queries") = "off"; | |
2718 | ::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ") = ""; | |
2719 | ::arg().set("lua-config-file", "More powerful configuration options") = ""; | |
2720 | ::arg().setSwitch("allow-trust-anchor-query", "Allow queries for trustanchor.server CH TXT and negativetrustanchor.server CH TXT") = "no"; | |
2721 | ||
2722 | ::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs") = ""; | |
2723 | ::arg().set("forward-zones-recurse", "Zones for which we forward queries with recursion bit, comma separated domain=ip pairs") = ""; | |
2724 | ::arg().set("forward-zones-file", "File with (+)domain=ip pairs for forwarding") = ""; | |
2725 | ::arg().set("export-etc-hosts", "If we should serve up contents from /etc/hosts") = "off"; | |
2726 | ::arg().set("export-etc-hosts-search-suffix", "Also serve up the contents of /etc/hosts with this suffix") = ""; | |
2727 | ::arg().set("etc-hosts-file", "Path to 'hosts' file") = "/etc/hosts"; | |
2728 | ::arg().set("serve-rfc1918", "If we should be authoritative for RFC 1918 private IP space") = "yes"; | |
2729 | ::arg().set("lua-dns-script", "Filename containing an optional 'lua' script that will be used to modify dns answers") = ""; | |
2730 | ::arg().set("lua-maintenance-interval", "Number of seconds between calls to the lua user defined maintenance() function") = "1"; | |
2731 | ::arg().set("latency-statistic-size", "Number of latency values to calculate the qa-latency average") = "10000"; | |
2732 | ::arg().setSwitch("disable-packetcache", "Disable packetcache") = "no"; | |
2733 | ::arg().set("ecs-ipv4-bits", "Number of bits of IPv4 address to pass for EDNS Client Subnet") = "24"; | |
2734 | ::arg().set("ecs-ipv4-cache-bits", "Maximum number of bits of IPv4 mask to cache ECS response") = "24"; | |
2735 | ::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet") = "56"; | |
2736 | ::arg().set("ecs-ipv6-cache-bits", "Maximum number of bits of IPv6 mask to cache ECS response") = "56"; | |
2737 | ::arg().setSwitch("ecs-ipv4-never-cache", "If we should never cache IPv4 ECS responses") = "no"; | |
2738 | ::arg().setSwitch("ecs-ipv6-never-cache", "If we should never cache IPv6 ECS responses") = "no"; | |
2739 | ::arg().set("ecs-minimum-ttl-override", "The minimum TTL for records in ECS-specific answers") = "1"; | |
2740 | ::arg().set("ecs-cache-limit-ttl", "Minimum TTL to cache ECS response") = "0"; | |
2741 | ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for (deprecated)") = ""; | |
2742 | ::arg().set("edns-subnet-allow-list", "List of netmasks and domains that we should enable EDNS subnet for") = ""; | |
2743 | ::arg().set("ecs-add-for", "List of client netmasks for which EDNS Client Subnet will be added") = "0.0.0.0/0, ::/0, " LOCAL_NETS_INVERSE; | |
2744 | ::arg().set("ecs-scope-zero-address", "Address to send to allow-listed authoritative servers for incoming queries with ECS prefix-length source of 0") = ""; | |
2745 | ::arg().setSwitch("use-incoming-edns-subnet", "Pass along received EDNS Client Subnet information") = "no"; | |
2746 | ::arg().setSwitch("pdns-distributes-queries", "If PowerDNS itself should distribute queries over threads") = "yes"; | |
2747 | ::arg().setSwitch("root-nx-trust", "If set, believe that an NXDOMAIN from the root means the TLD does not exist") = "yes"; | |
2748 | ::arg().setSwitch("any-to-tcp", "Answer ANY queries with tc=1, shunting to TCP") = "no"; | |
2749 | ::arg().setSwitch("lowercase-outgoing", "Force outgoing questions to lowercase") = "no"; | |
2750 | ::arg().setSwitch("gettag-needs-edns-options", "If EDNS Options should be extracted before calling the gettag() hook") = "no"; | |
2751 | ::arg().set("udp-truncation-threshold", "Maximum UDP response size before we truncate") = "1232"; | |
2752 | ::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size") = "1232"; | |
2753 | ::arg().set("minimum-ttl-override", "The minimum TTL") = "1"; | |
2754 | ::arg().set("max-qperq", "Maximum outgoing queries per query") = "60"; | |
a49b0b40 | 2755 | ::arg().set("max-ns-per-resolve", "Maximum number of NS records to consider to resolve a name, 0 is no limit") = "13"; |
8c1cb8aa OM |
2756 | ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query") = "10"; |
2757 | ::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited") = "7000"; | |
2758 | ::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited") = "40"; | |
2759 | ::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing") = "10000"; | |
2760 | ::arg().set("protobuf-use-kernel-timestamp", "Compute the latency of queries in protobuf messages by using the timestamp set by the kernel when the query was received (when available)") = ""; | |
2761 | ::arg().set("distribution-pipe-buffer-size", "Size in bytes of the internal buffer of the pipe used by the distributor to pass incoming queries to a worker thread") = "0"; | |
2762 | ||
2763 | ::arg().set("include-dir", "Include *.conf files from this directory") = ""; | |
2764 | ::arg().set("security-poll-suffix", "Domain name from which to query security update notifications") = "secpoll.powerdns.com."; | |
2765 | ||
2766 | ::arg().setSwitch("reuseport", "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address") = "no"; | |
2767 | ||
2768 | ::arg().setSwitch("snmp-agent", "If set, register as an SNMP agent") = "no"; | |
2769 | ::arg().set("snmp-master-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon (deprecated)") = ""; | |
2770 | ::arg().set("snmp-daemon-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon") = ""; | |
eeaa1cb6 OM |
2771 | |
2772 | std::string defaultAPIDisabledStats = "cache-bytes, packetcache-bytes, special-memory-usage"; | |
2773 | for (size_t idx = 0; idx < 32; idx++) { | |
2774 | defaultAPIDisabledStats += ", ecs-v4-response-bits-" + std::to_string(idx + 1); | |
2775 | } | |
2776 | for (size_t idx = 0; idx < 128; idx++) { | |
2777 | defaultAPIDisabledStats += ", ecs-v6-response-bits-" + std::to_string(idx + 1); | |
2778 | } | |
78cb013a | 2779 | std::string defaultDisabledStats = defaultAPIDisabledStats + ", cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count"; |
eeaa1cb6 | 2780 | |
8c1cb8aa OM |
2781 | ::arg().set("stats-api-blacklist", "List of statistics that are disabled when retrieving the complete list of statistics via the API (deprecated)") = defaultAPIDisabledStats; |
2782 | ::arg().set("stats-carbon-blacklist", "List of statistics that are prevented from being exported via Carbon (deprecated)") = defaultDisabledStats; | |
2783 | ::arg().set("stats-rec-control-blacklist", "List of statistics that are prevented from being exported via rec_control get-all (deprecated)") = defaultDisabledStats; | |
2784 | ::arg().set("stats-snmp-blacklist", "List of statistics that are prevented from being exported via SNMP (deprecated)") = defaultDisabledStats; | |
eeaa1cb6 | 2785 | |
8c1cb8aa OM |
2786 | ::arg().set("stats-api-disabled-list", "List of statistics that are disabled when retrieving the complete list of statistics via the API") = defaultAPIDisabledStats; |
2787 | ::arg().set("stats-carbon-disabled-list", "List of statistics that are prevented from being exported via Carbon") = defaultDisabledStats; | |
2788 | ::arg().set("stats-rec-control-disabled-list", "List of statistics that are prevented from being exported via rec_control get-all") = defaultDisabledStats; | |
2789 | ::arg().set("stats-snmp-disabled-list", "List of statistics that are prevented from being exported via SNMP") = defaultDisabledStats; | |
eeaa1cb6 | 2790 | |
8c1cb8aa OM |
2791 | ::arg().set("tcp-fast-open", "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size") = "0"; |
2792 | ::arg().set("tcp-fast-open-connect", "Enable TCP Fast Open support on outgoing sockets") = "no"; | |
2793 | ::arg().set("nsec3-max-iterations", "Maximum number of iterations allowed for an NSEC3 record") = "150"; | |
eeaa1cb6 | 2794 | |
8c1cb8aa | 2795 | ::arg().set("cpu-map", "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs") = ""; |
eeaa1cb6 | 2796 | |
8c1cb8aa | 2797 | ::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level") = "no"; |
eeaa1cb6 | 2798 | |
8c1cb8aa OM |
2799 | ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets") = ""; |
2800 | ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values") = "512"; | |
eeaa1cb6 | 2801 | |
8c1cb8aa | 2802 | ::arg().set("dns64-prefix", "DNS64 prefix") = ""; |
eeaa1cb6 | 2803 | |
8c1cb8aa OM |
2804 | ::arg().set("udp-source-port-min", "Minimum UDP port to bind on") = "1024"; |
2805 | ::arg().set("udp-source-port-max", "Maximum UDP port to bind on") = "65535"; | |
2806 | ::arg().set("udp-source-port-avoid", "List of comma separated UDP port number to avoid") = "11211"; | |
2807 | ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.") = "auto"; | |
2808 | ::arg().set("public-suffix-list-file", "Path to the Public Suffix List file, if any") = ""; | |
2809 | ::arg().set("distribution-load-factor", "The load factor used when PowerDNS is distributing queries to worker threads") = "0.0"; | |
eeaa1cb6 | 2810 | |
8c1cb8aa OM |
2811 | ::arg().setSwitch("qname-minimization", "Use Query Name Minimization") = "yes"; |
2812 | ::arg().setSwitch("nothing-below-nxdomain", "When an NXDOMAIN exists in cache for a name with fewer labels than the qname, send NXDOMAIN without doing a lookup (see RFC 8020)") = "dnssec"; | |
2813 | ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file") = "0"; | |
2814 | ::arg().set("max-include-depth", "Maximum nested $INCLUDE depth when loading a zone from a file") = "20"; | |
2815 | ::arg().set("record-cache-shards", "Number of shards in the record cache") = "1024"; | |
eeaa1cb6 | 2816 | ::arg().set("refresh-on-ttl-perc", "If a record is requested from the cache and only this % of original TTL remains, refetch") = "0"; |
55badaf8 | 2817 | ::arg().set("record-cache-locked-ttl-perc", "Replace records in record cache only after this % of original TTL has passed") = "0"; |
eeaa1cb6 | 2818 | |
8c1cb8aa | 2819 | ::arg().set("x-dnssec-names", "Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters") = ""; |
eeaa1cb6 OM |
2820 | |
2821 | #ifdef NOD_ENABLED | |
8c1cb8aa OM |
2822 | ::arg().set("new-domain-tracking", "Track newly observed domains (i.e. never seen before).") = "no"; |
2823 | ::arg().set("new-domain-log", "Log newly observed domains.") = "yes"; | |
2824 | ::arg().set("new-domain-lookup", "Perform a DNS lookup newly observed domains as a subdomain of the configured domain") = ""; | |
2825 | ::arg().set("new-domain-history-dir", "Persist new domain tracking data here to persist between restarts") = string(NODCACHEDIR) + "/nod"; | |
2826 | ::arg().set("new-domain-whitelist", "List of domains (and implicitly all subdomains) which will never be considered a new domain (deprecated)") = ""; | |
2827 | ::arg().set("new-domain-ignore-list", "List of domains (and implicitly all subdomains) which will never be considered a new domain") = ""; | |
2828 | ::arg().set("new-domain-db-size", "Size of the DB used to track new domains in terms of number of cells. Defaults to 67108864") = "67108864"; | |
2829 | ::arg().set("new-domain-pb-tag", "If protobuf is configured, the tag to use for messages containing newly observed domains. Defaults to 'pdns-nod'") = "pdns-nod"; | |
2830 | ::arg().set("unique-response-tracking", "Track unique responses (tuple of query name, type and RR).") = "no"; | |
2831 | ::arg().set("unique-response-log", "Log unique responses") = "yes"; | |
2832 | ::arg().set("unique-response-history-dir", "Persist unique response tracking data here to persist between restarts") = string(NODCACHEDIR) + "/udr"; | |
2833 | ::arg().set("unique-response-db-size", "Size of the DB used to track unique responses in terms of number of cells. Defaults to 67108864") = "67108864"; | |
2834 | ::arg().set("unique-response-pb-tag", "If protobuf is configured, the tag to use for messages containing unique DNS responses. Defaults to 'pdns-udr'") = "pdns-udr"; | |
eeaa1cb6 OM |
2835 | #endif /* NOD_ENABLED */ |
2836 | ||
8c1cb8aa | 2837 | ::arg().setSwitch("extended-resolution-errors", "If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors") = "no"; |
eeaa1cb6 | 2838 | |
8c1cb8aa | 2839 | ::arg().set("aggressive-nsec-cache-size", "The number of records to cache in the aggressive cache. If set to a value greater than 0, and DNSSEC processing or validation is enabled, the recursor will cache NSEC and NSEC3 records to generate negative answers, as defined in rfc8198") = "100000"; |
eeaa1cb6 | 2840 | |
fcd32fe8 | 2841 | ::arg().set("edns-padding-from", "List of netmasks (proxy IP in case of proxy-protocol presence, client IP otherwise) for which EDNS padding will be enabled in responses, provided that 'edns-padding-mode' applies") = ""; |
8c1cb8aa OM |
2842 | ::arg().set("edns-padding-mode", "Whether to add EDNS padding to all responses ('always') or only to responses for queries containing the EDNS padding option ('padded-queries-only', the default). In both modes, padding will only be added to responses for queries coming from `edns-padding-from`_ sources") = "padded-queries-only"; |
2843 | ::arg().set("edns-padding-tag", "Packetcache tag associated to responses sent with EDNS padding, to prevent sending these to clients for which padding is not enabled.") = "7830"; | |
fcf47f49 | 2844 | ::arg().setSwitch("edns-padding-out", "Whether to add EDNS padding to outgoing DoT messages") = "yes"; |
eeaa1cb6 | 2845 | |
8c1cb8aa OM |
2846 | ::arg().setSwitch("dot-to-port-853", "Force DoT connection to target port 853 if DoT compiled in") = "yes"; |
2847 | ::arg().set("dot-to-auth-names", "Use DoT to authoritative servers with these names or suffixes") = ""; | |
2848 | ::arg().set("event-trace-enabled", "If set, event traces are collected and send out via protobuf logging (1), logfile (2) or both(3)") = "0"; | |
eeaa1cb6 OM |
2849 | |
2850 | ::arg().set("tcp-out-max-idle-ms", "Time TCP/DoT connections are left idle in milliseconds or 0 if no limit") = "10000"; | |
2851 | ::arg().set("tcp-out-max-idle-per-auth", "Maximum number of idle TCP/DoT connections to a specific IP per thread, 0 means do not keep idle connections open") = "10"; | |
2852 | ::arg().set("tcp-out-max-queries", "Maximum total number of queries per TCP/DoT connection, 0 means no limit") = "0"; | |
2853 | ::arg().set("tcp-out-max-idle-per-thread", "Maximum number of idle TCP/DoT connections per thread") = "100"; | |
2854 | ::arg().setSwitch("structured-logging", "Prefer structured logging") = "yes"; | |
2281ecb1 | 2855 | ::arg().set("structured-logging-backend", "Structured logging backend") = "default"; |
1bb24087 | 2856 | ::arg().setSwitch("save-parent-ns-set", "Save parent NS set to be used if child NS set fails") = "yes"; |
78deedda | 2857 | ::arg().set("max-busy-dot-probes", "Maximum number of concurrent DoT probes") = "0"; |
edabff75 | 2858 | ::arg().set("serve-stale-extensions", "Number of times a record's ttl is extended by 30s to be served stale") = "0"; |
eeaa1cb6 | 2859 | |
8c1cb8aa OM |
2860 | ::arg().setCmd("help", "Provide a helpful message"); |
2861 | ::arg().setCmd("version", "Print version string"); | |
c5eab72f | 2862 | ::arg().setCmd("config", "Output blank configuration. You can use --config=check to test the config file and command line arguments."); |
eeaa1cb6 OM |
2863 | ::arg().setDefaults(); |
2864 | g_log.toConsole(Logger::Info); | |
8c1cb8aa | 2865 | ::arg().laxParse(argc, argv); // do a lax parse |
eeaa1cb6 | 2866 | |
8c1cb8aa | 2867 | if (::arg().mustDo("version")) { |
eeaa1cb6 OM |
2868 | showProductVersion(); |
2869 | showBuildConfiguration(); | |
2870 | exit(0); | |
2871 | } | |
ebaffe64 OM |
2872 | if (::arg().mustDo("help")) { |
2873 | cout << "syntax:" << endl | |
2874 | << endl; | |
2875 | cout << ::arg().helpstring(::arg()["help"]) << endl; | |
2876 | exit(0); | |
2877 | } | |
2878 | ||
2879 | // Pick up options given on command line to setup logging asap. | |
2880 | g_quiet = ::arg().mustDo("quiet"); | |
1e5eef5e | 2881 | s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel"); |
ebaffe64 | 2882 | g_slogStructured = ::arg().mustDo("structured-logging"); |
2281ecb1 | 2883 | s_structured_logger_backend = ::arg()["structured-logging-backend"]; |
ebaffe64 | 2884 | |
2cff1a09 | 2885 | if (s_logUrgency < Logger::Error) { |
1e5eef5e | 2886 | s_logUrgency = Logger::Error; |
2cff1a09 | 2887 | } |
1e5eef5e OM |
2888 | if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7 |
2889 | s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log | |
ebaffe64 | 2890 | } |
1e5eef5e OM |
2891 | g_log.setLoglevel(s_logUrgency); |
2892 | g_log.toConsole(s_logUrgency); | |
eeaa1cb6 | 2893 | |
8c1cb8aa | 2894 | string configname = ::arg()["config-dir"] + "/recursor.conf"; |
95100264 | 2895 | if (!::arg()["config-name"].empty()) { |
8c1cb8aa | 2896 | configname = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"] + ".conf"; |
b35191d7 | 2897 | g_programname += "-" + ::arg()["config-name"]; |
eeaa1cb6 OM |
2898 | } |
2899 | cleanSlashes(configname); | |
2900 | ||
8c1cb8aa OM |
2901 | if (!::arg().getCommands().empty()) { |
2902 | cerr << "Fatal: non-option"; | |
eeaa1cb6 | 2903 | if (::arg().getCommands().size() > 1) { |
8c1cb8aa | 2904 | cerr << "s"; |
eeaa1cb6 | 2905 | } |
8c1cb8aa | 2906 | cerr << " ("; |
eeaa1cb6 OM |
2907 | bool first = true; |
2908 | for (const auto& c : ::arg().getCommands()) { | |
2909 | if (!first) { | |
8c1cb8aa | 2910 | cerr << ", "; |
eeaa1cb6 OM |
2911 | } |
2912 | first = false; | |
8c1cb8aa | 2913 | cerr << c; |
eeaa1cb6 | 2914 | } |
8c1cb8aa | 2915 | cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl; |
eeaa1cb6 OM |
2916 | exit(99); |
2917 | } | |
2918 | ||
2281ecb1 | 2919 | if (s_structured_logger_backend == "systemd-journal") { |
0112056b | 2920 | #ifdef HAVE_SYSTEMD |
5d430b49 OM |
2921 | if (int fd = sd_journal_stream_fd("pdns-recusor", LOG_DEBUG, 0); fd >= 0) { |
2922 | g_slog = Logging::Logger::create(loggerSDBackend); | |
2923 | close(fd); | |
2924 | } | |
0112056b | 2925 | #endif |
2281ecb1 OM |
2926 | if (g_slog == nullptr) { |
2927 | cerr << "Structured logging to systemd-journal requested but it is not available" << endl; | |
2928 | } | |
2929 | } | |
2930 | ||
5d430b49 | 2931 | if (g_slog == nullptr) { |
0112056b OM |
2932 | g_slog = Logging::Logger::create(loggerBackend); |
2933 | } | |
ab26d8d5 | 2934 | |
ebaffe64 | 2935 | // Missing: a mechanism to call setVerbosity(x) |
d197c31c | 2936 | auto startupLog = g_slog->withName("config"); |
91092a9f OM |
2937 | g_slogtcpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("tcp")); |
2938 | g_slogudpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("udp")); | |
2939 | g_slogout = g_slog->withName("out"); | |
eeaa1cb6 | 2940 | |
d197c31c | 2941 | ::arg().setSLog(startupLog); |
c5eab72f FM |
2942 | |
2943 | if (::arg().mustDo("config")) { | |
2944 | string config = ::arg()["config"]; | |
2945 | if (config == "check") { | |
2946 | try { | |
2947 | if (!::arg().file(configname.c_str())) { | |
2948 | SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl, | |
2949 | startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname))); | |
2950 | exit(1); | |
2951 | } | |
2952 | ::arg().parse(argc, argv); | |
2953 | exit(0); | |
2954 | } | |
2955 | catch (const ArgException& argException) { | |
2956 | SLOG(g_log << Logger::Warning << "Unable to parse configuration file '" << configname << "': " << argException.reason << endl, | |
2957 | startupLog->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname), "reason", Logging::Loggable(argException.reason))); | |
2958 | exit(1); | |
2959 | } | |
2960 | } | |
ded8ecb6 | 2961 | else if (config == "default" || config.empty()) { |
c5eab72f FM |
2962 | cout << ::arg().configstring(false, true); |
2963 | } | |
2964 | else if (config == "diff") { | |
2965 | if (!::arg().laxFile(configname.c_str())) { | |
2966 | SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl, | |
2967 | startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname))); | |
2968 | exit(1); | |
2969 | } | |
2970 | ::arg().laxParse(argc, argv); | |
2971 | cout << ::arg().configstring(true, false); | |
2972 | } | |
2973 | else { | |
2974 | if (!::arg().laxFile(configname.c_str())) { | |
2975 | SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl, | |
2976 | startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname))); | |
2977 | exit(1); | |
2978 | } | |
2979 | ::arg().laxParse(argc, argv); | |
2980 | cout << ::arg().configstring(true, true); | |
2981 | } | |
2982 | exit(0); | |
2983 | } | |
2984 | ||
8c1cb8aa | 2985 | if (!::arg().file(configname.c_str())) { |
7482be89 FM |
2986 | SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl, |
2987 | startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname))); | |
eeaa1cb6 OM |
2988 | } |
2989 | ||
ebaffe64 | 2990 | // Reparse, now with config file as well |
8c1cb8aa | 2991 | ::arg().parse(argc, argv); |
eeaa1cb6 | 2992 | |
ebaffe64 | 2993 | g_quiet = ::arg().mustDo("quiet"); |
1e5eef5e | 2994 | s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel"); |
ebaffe64 OM |
2995 | g_slogStructured = ::arg().mustDo("structured-logging"); |
2996 | ||
2cff1a09 | 2997 | if (s_logUrgency < Logger::Error) { |
1e5eef5e | 2998 | s_logUrgency = Logger::Error; |
2cff1a09 | 2999 | } |
1e5eef5e OM |
3000 | if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7 |
3001 | s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log | |
ebaffe64 | 3002 | } |
1e5eef5e OM |
3003 | g_log.setLoglevel(s_logUrgency); |
3004 | g_log.toConsole(s_logUrgency); | |
ebaffe64 | 3005 | |
8c1cb8aa OM |
3006 | if (!::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty()) { |
3007 | SLOG(g_log << Logger::Error << "Using chroot and enabling the API is not possible" << endl, | |
fb29e225 | 3008 | startupLog->info(Logr::Error, "Cannot use chroot and enable the API at the same time")); |
eeaa1cb6 OM |
3009 | exit(EXIT_FAILURE); |
3010 | } | |
3011 | ||
3012 | if (::arg()["socket-dir"].empty()) { | |
3013 | if (::arg()["chroot"].empty()) | |
3014 | ::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor"; | |
3015 | else | |
3016 | ::arg().set("socket-dir") = "/"; | |
3017 | } | |
3018 | ||
8c1cb8aa | 3019 | if (::arg().asNum("threads") == 1) { |
eeaa1cb6 | 3020 | if (::arg().mustDo("pdns-distributes-queries")) { |
8c1cb8aa | 3021 | SLOG(g_log << Logger::Warning << "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl, |
eeaa1cb6 | 3022 | startupLog->v(1)->info("Only one thread, no need to distribute queries ourselves")); |
8c1cb8aa | 3023 | ::arg().set("pdns-distributes-queries") = "no"; |
eeaa1cb6 OM |
3024 | } |
3025 | } | |
3026 | ||
8c1cb8aa OM |
3027 | if (::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") <= 0) { |
3028 | SLOG(g_log << Logger::Warning << "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl, | |
eeaa1cb6 | 3029 | startupLog->v(1)->info("Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1")); |
8c1cb8aa | 3030 | ::arg().set("distributor-threads") = "1"; |
eeaa1cb6 OM |
3031 | } |
3032 | ||
3033 | if (!::arg().mustDo("pdns-distributes-queries")) { | |
8c1cb8aa | 3034 | ::arg().set("distributor-threads") = "0"; |
eeaa1cb6 OM |
3035 | } |
3036 | ||
eeaa1cb6 OM |
3037 | g_recCache = std::make_unique<MemRecursorCache>(::arg().asNum("record-cache-shards")); |
3038 | g_negCache = std::make_unique<NegCache>(::arg().asNum("record-cache-shards")); | |
3039 | ||
d8d1d955 | 3040 | ret = serviceMain(argc, argv, startupLog); |
eeaa1cb6 | 3041 | } |
fb29e225 OM |
3042 | catch (const PDNSException& ae) { |
3043 | SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl, | |
e5d6a4e1 | 3044 | g_slog->withName("config")->error(Logr::Critical, ae.reason, "Fatal error", "exception", Logging::Loggable("PDNSException"))); |
8c1cb8aa | 3045 | ret = EXIT_FAILURE; |
eeaa1cb6 | 3046 | } |
fb29e225 OM |
3047 | catch (const std::exception& e) { |
3048 | SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl, | |
e5d6a4e1 | 3049 | g_slog->withName("config")->error(Logr::Critical, e.what(), "Fatal error", "exception", Logging::Loggable("std::exception"))); |
8c1cb8aa | 3050 | ret = EXIT_FAILURE; |
eeaa1cb6 | 3051 | } |
8c1cb8aa | 3052 | catch (...) { |
fb29e225 | 3053 | SLOG(g_log << Logger::Error << "any other exception in main: " << endl, |
e5d6a4e1 | 3054 | g_slog->withName("config")->info(Logr::Critical, "Fatal error")); |
8c1cb8aa | 3055 | ret = EXIT_FAILURE; |
eeaa1cb6 OM |
3056 | } |
3057 | ||
3058 | return ret; | |
3059 | } | |
3060 | ||
eeaa1cb6 OM |
3061 | static RecursorControlChannel::Answer* doReloadLuaScript() |
3062 | { | |
8c1cb8aa | 3063 | string fname = ::arg()["lua-dns-script"]; |
e5d6a4e1 | 3064 | auto log = g_slog->withName("runtime")->withValues("name", Logging::Loggable(fname)); |
eeaa1cb6 | 3065 | try { |
8c1cb8aa | 3066 | if (fname.empty()) { |
eeaa1cb6 | 3067 | t_pdl.reset(); |
e5d6a4e1 OM |
3068 | SLOG(g_log << Logger::Info << RecThreadInfo::id() << " Unloaded current lua script" << endl, |
3069 | log->info(Logr::Info, "Unloaded current lua script")); | |
eeaa1cb6 OM |
3070 | return new RecursorControlChannel::Answer{0, string("unloaded\n")}; |
3071 | } | |
a34586cf FM |
3072 | |
3073 | t_pdl = std::make_shared<RecursorLua4>(); | |
3074 | try { | |
3075 | t_pdl->loadFile(fname); | |
3076 | } | |
3077 | catch (std::runtime_error& ex) { | |
3078 | string msg = std::to_string(RecThreadInfo::id()) + " Retaining current script, could not read '" + fname + "': " + ex.what(); | |
e5d6a4e1 OM |
3079 | SLOG(g_log << Logger::Error << msg << endl, |
3080 | log->error(Logr::Error, ex.what(), "Retaining current script, could not read new script")); | |
a34586cf | 3081 | return new RecursorControlChannel::Answer{1, msg + "\n"}; |
eeaa1cb6 OM |
3082 | } |
3083 | } | |
8c1cb8aa | 3084 | catch (std::exception& e) { |
e5d6a4e1 OM |
3085 | SLOG(g_log << Logger::Error << RecThreadInfo::id() << " Retaining current script, error from '" << fname << "': " << e.what() << endl, |
3086 | log->error(Logr::Error, e.what(), "Retaining current script, error in new script")); | |
8c1cb8aa | 3087 | return new RecursorControlChannel::Answer{1, string("retaining current script, error from '" + fname + "': " + e.what() + "\n")}; |
eeaa1cb6 OM |
3088 | } |
3089 | ||
e5d6a4e1 OM |
3090 | SLOG(g_log << Logger::Warning << RecThreadInfo::id() << " (Re)loaded lua script from '" << fname << "'" << endl, |
3091 | log->info(Logr::Warning, "(Re)loaded lua script")); | |
8c1cb8aa | 3092 | return new RecursorControlChannel::Answer{0, string("(re)loaded '" + fname + "'\n")}; |
eeaa1cb6 OM |
3093 | } |
3094 | ||
3095 | RecursorControlChannel::Answer doQueueReloadLuaScript(vector<string>::const_iterator begin, vector<string>::const_iterator end) | |
3096 | { | |
8c1cb8aa | 3097 | if (begin != end) |
eeaa1cb6 OM |
3098 | ::arg().set("lua-dns-script") = *begin; |
3099 | ||
3100 | return broadcastAccFunction<RecursorControlChannel::Answer>(doReloadLuaScript); | |
3101 | } | |
3102 | ||
3103 | static string* pleaseUseNewTraceRegex(const std::string& newRegex) | |
8c1cb8aa OM |
3104 | try { |
3105 | if (newRegex.empty()) { | |
eeaa1cb6 OM |
3106 | t_traceRegex.reset(); |
3107 | return new string("unset\n"); | |
3108 | } | |
3109 | else { | |
3110 | t_traceRegex = std::make_shared<Regex>(newRegex); | |
3111 | return new string("ok\n"); | |
3112 | } | |
3113 | } | |
8c1cb8aa OM |
3114 | catch (PDNSException& ae) { |
3115 | return new string(ae.reason + "\n"); | |
eeaa1cb6 OM |
3116 | } |
3117 | ||
3118 | string doTraceRegex(vector<string>::const_iterator begin, vector<string>::const_iterator end) | |
3119 | { | |
8c1cb8aa | 3120 | return broadcastAccFunction<string>([=] { return pleaseUseNewTraceRegex(begin != end ? *begin : ""); }); |
eeaa1cb6 OM |
3121 | } |
3122 | ||
eeaa1cb6 OM |
3123 | static uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree, uint16_t qtype) |
3124 | { | |
4482f39f | 3125 | return new uint64_t(t_packetCache ? t_packetCache->doWipePacketCache(canon, qtype, subtree) : 0); |
eeaa1cb6 OM |
3126 | } |
3127 | ||
3128 | struct WipeCacheResult wipeCaches(const DNSName& canon, bool subtree, uint16_t qtype) | |
3129 | { | |
3130 | struct WipeCacheResult res; | |
3131 | ||
3132 | try { | |
3133 | res.record_count = g_recCache->doWipeCache(canon, subtree, qtype); | |
8ae4f2bc | 3134 | // scanbuild complains here about an allocated function object that is being leaked. Needs investigation |
8c1cb8aa | 3135 | res.packet_count = broadcastAccFunction<uint64_t>([=] { return pleaseWipePacketCache(canon, subtree, qtype); }); |
eeaa1cb6 OM |
3136 | res.negative_record_count = g_negCache->wipe(canon, subtree); |
3137 | if (g_aggressiveNSECCache) { | |
3138 | g_aggressiveNSECCache->removeZoneInfo(canon, subtree); | |
3139 | } | |
3140 | } | |
3141 | catch (const std::exception& e) { | |
e5d6a4e1 OM |
3142 | auto log = g_slog->withName("runtime"); |
3143 | SLOG(g_log << Logger::Warning << ", failed: " << e.what() << endl, | |
3144 | log->error(Logr::Warning, e.what(), "Wipecache failed")); | |
eeaa1cb6 OM |
3145 | } |
3146 | ||
3147 | return res; | |
3148 | } |