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