• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 12268421405

11 Dec 2024 02:20AM UTC coverage: 64.728% (+3.2%) from 61.567%
12268421405

Pull #14954

github

web-flow
Merge d6414658d into 5089e2c08
Pull Request #14954: clang-tidy: use std::min/max

37522 of 88810 branches covered (42.25%)

Branch coverage included in aggregate %.

3 of 10 new or added lines in 4 files covered. (30.0%)

169 existing lines in 9 files now uncovered.

125906 of 163674 relevant lines covered (76.92%)

4802085.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.56
/pdns/auth-main.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#ifdef HAVE_CONFIG_H
23
#include "config.h"
24
#endif
25
#include <cstdio>
26
#include <csignal>
27
#include <cstring>
28
#include <cstdlib>
29
#include <sys/types.h>
30
#include <sys/socket.h>
31
#include <netinet/in.h>
32
#include <arpa/inet.h>
33
#include <iostream>
34
#include <string>
35
#include <sys/stat.h>
36
#include <unistd.h>
37
#include <sys/resource.h>
38
#include <sys/time.h>
39
#include <sys/wait.h>
40
#include <cerrno>
41
#include <pthread.h>
42
#include <thread>
43
#include <unistd.h>
44
#include <sys/mman.h>
45
#include <fcntl.h>
46
#include <fstream>
47
#include <boost/algorithm/string.hpp>
48
#ifdef HAVE_LIBSODIUM
49
#include <sodium.h>
50
#endif
51
#ifdef HAVE_SYSTEMD
52
#include <systemd/sd-daemon.h>
53
#endif
54

55
#include "auth-main.hh"
56
#include "coverage.hh"
57
#include "secpoll-auth.hh"
58
#include "dynhandler.hh"
59
#include "dnsseckeeper.hh"
60
#include "threadname.hh"
61
#include "misc.hh"
62
#include "query-local-address.hh"
63
#include "trusted-notification-proxy.hh"
64
#include "packetcache.hh"
65
#include "packethandler.hh"
66
#include "opensslsigners.hh"
67
#include "dns.hh"
68
#include "dnsbackend.hh"
69
#include "ueberbackend.hh"
70
#include "dnspacket.hh"
71
#include "nameserver.hh"
72
#include "distributor.hh"
73
#include "logger.hh"
74
#include "arguments.hh"
75
#include "packethandler.hh"
76
#include "statbag.hh"
77
#include "tcpreceiver.hh"
78
#include "misc.hh"
79
#include "dynlistener.hh"
80
#include "dynhandler.hh"
81
#include "communicator.hh"
82
#include "dnsproxy.hh"
83
#include "utility.hh"
84
#include "dnsrecords.hh"
85
#include "version.hh"
86
#include "ws-auth.hh"
87

88
#ifdef HAVE_LUA_RECORDS
89
#include "minicurl.hh"
90
#endif /* HAVE_LUA_RECORDS */
91

92
time_t g_starttime;
93

94
string g_programname = "pdns"; // used in packethandler.cc
95

96
const char* funnytext = "*****************************************************************************\n"
97
                        "Ok, you just ran pdns-auth through 'strings' hoping to find funny messages.  \n"
98
                        "Well, you found one.                                                         \n"
99
                        "Two ions are flying through their particle accelerator, says the one to the  \n"
100
                        "other 'I think I've lost an electron!'                                       \n"
101
                        "So the other one says, 'Are you sure?'. 'YEAH! I'M POSITIVE!'                \n"
102
                        "                                                                             \n"
103
                        "                                            the pdns crew - pdns@powerdns.com\n"
104
                        "*****************************************************************************\n";
105

106
bool g_anyToTcp;
107
bool g_8bitDNS;
108
#ifdef HAVE_LUA_RECORDS
109
bool g_doLuaRecord;
110
int g_luaRecordExecLimit;
111
time_t g_luaHealthChecksInterval{5};
112
time_t g_luaHealthChecksExpireDelay{3600};
113
time_t g_luaConsistentHashesExpireDelay{86400};
114
time_t g_luaConsistentHashesCleanupInterval{3600};
115
#endif
116
#ifdef ENABLE_GSS_TSIG
117
bool g_doGssTSIG;
118
#endif
119
typedef Distributor<DNSPacket, DNSPacket, PacketHandler> DNSDistributor;
120

121
ArgvMap theArg;
122
StatBag S; //!< Statistics are gathered across PDNS via the StatBag class S
123
AuthPacketCache PC; //!< This is the main PacketCache, shared across all threads
124
AuthQueryCache QC;
125
AuthZoneCache g_zoneCache;
126
std::unique_ptr<DNSProxy> DP{nullptr};
127
static std::unique_ptr<DynListener> s_dynListener{nullptr};
128
CommunicatorClass Communicator;
129
static double avg_latency{0.0}, receive_latency{0.0}, cache_latency{0.0}, backend_latency{0.0}, send_latency{0.0};
130
static unique_ptr<TCPNameserver> s_tcpNameserver{nullptr};
131
static vector<DNSDistributor*> s_distributors;
132
static shared_ptr<UDPNameserver> s_udpNameserver{nullptr};
133
static vector<std::shared_ptr<UDPNameserver>> s_udpReceivers;
134
NetmaskGroup g_proxyProtocolACL;
135
size_t g_proxyProtocolMaximumSize;
136

137
ArgvMap& arg()
138
{
1,448,045✔
139
  return theArg;
1,448,045✔
140
}
1,448,045✔
141

142
static void declareArguments()
143
{
256✔
144
  ::arg().set("config-dir", "Location of configuration directory (pdns.conf)") = SYSCONFDIR;
256✔
145
  ::arg().set("config-name", "Name of this virtual configuration - will rename the binary image") = "";
256✔
146
  ::arg().set("socket-dir", string("Where the controlsocket will live, ") + LOCALSTATEDIR + "/pdns when unset and not chrooted"
256✔
147
#ifdef HAVE_SYSTEMD
256✔
148
                + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")
256✔
149
    = "";
256✔
150
  auto runtimeDir = getenv("RUNTIME_DIRECTORY");
256✔
151
  if (runtimeDir != nullptr) {
256!
152
    ::arg().set("socket-dir") = runtimeDir;
×
153
  }
×
154
#else
155
              )
156
    = "";
157
#endif
158
  ::arg().set("module-dir", "Default directory for modules") = PKGLIBDIR;
256✔
159
  ::arg().set("chroot", "If set, chroot to this directory for more security") = "";
256✔
160
  ::arg().set("logging-facility", "Log under a specific facility") = "";
256✔
161
  ::arg().set("daemon", "Operate as a daemon") = "no";
256✔
162

163
  ::arg().set("local-port", "The port on which we listen") = "53";
256✔
164
  ::arg().setSwitch("dnsupdate", "Enable/Disable DNS update (RFC2136) support. Default is no.") = "no";
256✔
165
  ::arg().setSwitch("write-pid", "Write a PID file") = "yes";
256✔
166
  ::arg().set("allow-dnsupdate-from", "A global setting to allow DNS updates from these IP ranges.") = "127.0.0.0/8,::1";
256✔
167
  ::arg().setSwitch("dnsupdate-require-tsig", "Require TSIG secured DNS updates. Default is no.") = "no";
256✔
168
  ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets, and is mandatory then too.") = "";
256✔
169
  ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values") = "512";
256✔
170
  ::arg().setSwitch("send-signed-notify", "Send TSIG secured NOTIFY if TSIG key is configured for a zone") = "yes";
256✔
171
  ::arg().set("allow-unsigned-notify", "Allow unsigned notifications for TSIG secured zones") = "yes"; // FIXME: change to 'no' later
256✔
172
  ::arg().set("allow-unsigned-autoprimary", "Allow autoprimaries to create zones without TSIG signed NOTIFY") = "yes";
256✔
173
  ::arg().setSwitch("forward-dnsupdate", "A global setting to allow DNS update packages that are for a Secondary zone, to be forwarded to the primary.") = "yes";
256✔
174
  ::arg().setSwitch("log-dns-details", "If PDNS should log DNS non-erroneous details") = "no";
256✔
175
  ::arg().setSwitch("log-dns-queries", "If PDNS should log all incoming DNS queries") = "no";
256✔
176
  ::arg().set("local-address", "Local IP addresses to which we bind") = "0.0.0.0, ::";
256✔
177
  ::arg().setSwitch("local-address-nonexist-fail", "Fail to start if one or more of the local-address's do not exist on this server") = "yes";
256✔
178
  ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options") = "no";
256✔
179
  ::arg().setSwitch("reuseport", "Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket") = "no";
256✔
180
  ::arg().set("query-local-address", "Source IP addresses for sending queries") = "0.0.0.0 ::";
256✔
181
  ::arg().set("overload-queue-length", "Maximum queuelength moving to packetcache only") = "0";
256✔
182
  ::arg().set("max-queue-length", "Maximum queuelength before considering situation lost") = "5000";
256✔
183

184
  ::arg().set("retrieval-threads", "Number of AXFR-retrieval threads for secondary operation") = "2";
256✔
185
  ::arg().setSwitch("api", "Enable/disable the REST API (including HTTP listener)") = "no";
256✔
186
  ::arg().set("api-key", "Static pre-shared authentication key for access to the REST API") = "";
256✔
187
  ::arg().setSwitch("default-api-rectify", "Default API-RECTIFY value for zones") = "yes";
256✔
188
  ::arg().setSwitch("dname-processing", "If we should support DNAME records") = "no";
256✔
189

190
  ::arg().setCmd("help", "Provide a helpful message");
256✔
191
  ::arg().setCmd("version", "Output version and compilation date");
256✔
192
  ::arg().setCmd("config", "Provide configuration file on standard output");
256✔
193
  ::arg().setCmd("list-modules", "Lists all modules available");
256✔
194
  ::arg().setCmd("no-config", "Don't parse configuration file");
256✔
195

196
  ::arg().set("version-string", "PowerDNS version in packets - full, anonymous, powerdns or custom") = "full";
256✔
197
  ::arg().set("control-console", "Debugging switch - don't use") = "no"; // but I know you will!
256✔
198
  ::arg().set("loglevel", "Amount of logging. Higher is more. Do not set below 3") = "4";
256✔
199
  ::arg().setSwitch("loglevel-show", "Include log level indicator in log output") = "no";
256✔
200
  ::arg().set("disable-syslog", "Disable logging to syslog, useful when running inside a supervisor that logs stderr") = "no";
256✔
201
  ::arg().set("log-timestamp", "Print timestamps in log lines") = "yes";
256✔
202
  ::arg().set("distributor-threads", "Default number of Distributor (backend) threads to start") = "3";
256✔
203
  ::arg().set("signing-threads", "Default number of signer threads to start") = "3";
256✔
204
  ::arg().setSwitch("workaround-11804", "Workaround for issue 11804: send single RR per AXFR chunk") = "no";
256✔
205
  ::arg().set("receiver-threads", "Default number of receiver threads to start") = "1";
256✔
206
  ::arg().set("queue-limit", "Maximum number of milliseconds to queue a query") = "1500";
256✔
207
  ::arg().set("resolver", "Use this resolver for ALIAS and the internal stub resolver") = "no";
256✔
208
  ::arg().set("dnsproxy-udp-port-range", "Select DNS Proxy outgoing UDP port from given range (lower upper)") = "10000 60000";
256✔
209
  ::arg().set("udp-truncation-threshold", "Maximum UDP response size before we truncate") = "1232";
256✔
210

211
  ::arg().set("config-name", "Name of this virtual configuration - will rename the binary image") = "";
256✔
212

213
  ::arg().set("load-modules", "Load this module - supply absolute or relative path") = "";
256✔
214
  ::arg().set("launch", "Which backends to launch and order to query them in") = "";
256✔
215
  ::arg().setSwitch("disable-axfr", "Disable zonetransfers but do allow TCP queries") = "no";
256✔
216
  ::arg().set("allow-axfr-ips", "Allow zonetransfers only to these subnets") = "127.0.0.0/8,::1";
256✔
217
  ::arg().set("only-notify", "Only send AXFR NOTIFY to these IP addresses or netmasks") = "0.0.0.0/0,::/0";
256✔
218
  ::arg().set("also-notify", "When notifying a zone, also notify these nameservers") = "";
256✔
219
  ::arg().set("allow-notify-from", "Allow AXFR NOTIFY from these IP ranges. If empty, drop all incoming notifies.") = "0.0.0.0/0,::/0";
256✔
220
  ::arg().set("xfr-cycle-interval", "Schedule primary/secondary SOA freshness checks once every .. seconds") = "60";
256✔
221
  ::arg().set("secondary-check-signature-freshness", "Check signatures in SOA freshness check. Sets DO flag on SOA queries. Outside some very problematic scenarios, say yes here.") = "yes";
256✔
222

223
  ::arg().set("tcp-control-address", "If set, PowerDNS can be controlled over TCP on this address") = "";
256✔
224
  ::arg().set("tcp-control-port", "If set, PowerDNS can be controlled over TCP on this address") = "53000";
256✔
225
  ::arg().set("tcp-control-secret", "If set, PowerDNS can be controlled over TCP after passing this secret") = "";
256✔
226
  ::arg().set("tcp-control-range", "If set, remote control of PowerDNS is possible over these networks only") = "127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10";
256✔
227

228
  ::arg().setSwitch("secondary", "Act as a secondary") = "no";
256✔
229
  ::arg().setSwitch("primary", "Act as a primary") = "no";
256✔
230
  ::arg().setSwitch("autosecondary", "Act as an autosecondary") = "no";
256✔
231
  ::arg().setSwitch("disable-axfr-rectify", "Disable the rectify step during an outgoing AXFR. Only required for regression testing.") = "no";
256✔
232
  ::arg().setSwitch("guardian", "Run within a guardian process") = "no";
256✔
233
  ::arg().setSwitch("prevent-self-notification", "Don't send notifications to what we think is ourself") = "yes";
256✔
234
  ::arg().setSwitch("any-to-tcp", "Answer ANY queries with tc=1, shunting to TCP") = "yes";
256✔
235
  ::arg().setSwitch("edns-subnet-processing", "If we should act on EDNS Subnet options") = "no";
256✔
236
  ::arg().set("delay-notifications", "Configure a delay to send out notifications, no delay by default") = "0";
256✔
237

238
  ::arg().set("edns-cookie-secret", "When set, set a server cookie when responding to a query with a Client cookie (in hex)") = "";
256✔
239

240
  ::arg().setSwitch("webserver", "Start a webserver for monitoring (api=yes also enables the HTTP listener)") = "no";
256✔
241
  ::arg().setSwitch("webserver-print-arguments", "If the webserver should print arguments") = "no";
256✔
242
  ::arg().set("webserver-address", "IP Address of webserver/API to listen on") = "127.0.0.1";
256✔
243
  ::arg().set("webserver-port", "Port of webserver/API to listen on") = "8081";
256✔
244
  ::arg().set("webserver-password", "Password required for accessing the webserver") = "";
256✔
245
  ::arg().set("webserver-allow-from", "Webserver/API access is only allowed from these subnets") = "127.0.0.1,::1";
256✔
246
  ::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
256✔
247
  ::arg().set("webserver-max-bodysize", "Webserver/API maximum request/response body size in megabytes") = "2";
256✔
248
  ::arg().set("webserver-connection-timeout", "Webserver/API request/response timeout in seconds") = "5";
256✔
249
  ::arg().setSwitch("webserver-hash-plaintext-credentials", "Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime") = "no";
256✔
250

251
  ::arg().setSwitch("query-logging", "Hint backends that queries should be logged") = "no";
256✔
252

253
  ::arg().set("carbon-namespace", "If set overwrites the first part of the carbon string") = "pdns";
256✔
254
  ::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats") = "";
256✔
255
  ::arg().set("carbon-instance", "If set overwrites the instance name default") = "auth";
256✔
256
  ::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server IP address") = "";
256✔
257
  ::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates") = "30";
256✔
258

259
  ::arg().set("cache-ttl", "Seconds to store packets in the PacketCache") = "20";
256✔
260
  ::arg().set("negquery-cache-ttl", "Seconds to store negative query results in the QueryCache") = "60";
256✔
261
  ::arg().set("query-cache-ttl", "Seconds to store query results in the QueryCache") = "20";
256✔
262
  ::arg().set("zone-cache-refresh-interval", "Seconds to cache list of known zones") = "300";
256✔
263
  ::arg().set("server-id", "Returned when queried for 'id.server' TXT or NSID, defaults to hostname - disabled or custom") = "";
256✔
264
  ::arg().set("default-soa-content", "Default SOA content") = "a.misconfigured.dns.server.invalid hostmaster.@ 0 10800 3600 604800 3600";
256✔
265
  ::arg().set("default-soa-edit", "Default SOA-EDIT value") = "";
256✔
266
  ::arg().set("default-soa-edit-signed", "Default SOA-EDIT value for signed zones") = "";
256✔
267
  ::arg().set("dnssec-key-cache-ttl", "Seconds to cache DNSSEC keys from the database") = "30";
256✔
268
  ::arg().set("domain-metadata-cache-ttl", "Seconds to cache zone metadata from the database") = "";
256✔
269
  ::arg().set("zone-metadata-cache-ttl", "Seconds to cache zone metadata from the database") = "60";
256✔
270

271
  ::arg().set("trusted-notification-proxy", "IP address of incoming notification proxy") = "";
256✔
272
  ::arg().set("secondary-do-renotify", "If this secondary should send out notifications after receiving zone transfers from a primary") = "no";
256✔
273
  ::arg().set("forward-notify", "IP addresses to forward received notifications to regardless of primary or secondary settings") = "";
256✔
274

275
  ::arg().set("default-ttl", "Seconds a result is valid if not set otherwise") = "3600";
256✔
276
  ::arg().set("max-tcp-connections", "Maximum number of TCP connections") = "20";
256✔
277
  ::arg().set("max-tcp-connections-per-client", "Maximum number of simultaneous TCP connections per client") = "0";
256✔
278
  ::arg().set("max-tcp-transactions-per-conn", "Maximum number of subsequent queries per TCP connection") = "0";
256✔
279
  ::arg().set("max-tcp-connection-duration", "Maximum time in seconds that a TCP DNS connection is allowed to stay open.") = "0";
256✔
280
  ::arg().set("tcp-idle-timeout", "Maximum time in seconds that a TCP DNS connection is allowed to stay open while being idle") = "5";
256✔
281

282
  ::arg().setSwitch("no-shuffle", "Set this to prevent random shuffling of answers - for regression testing") = "off";
256✔
283

284
  ::arg().set("setuid", "If set, change user id to this uid for more security") = "";
256✔
285
  ::arg().set("setgid", "If set, change group id to this gid for more security") = "";
256✔
286

287
  ::arg().set("max-cache-entries", "Maximum number of entries in the query cache") = "1000000";
256✔
288
  ::arg().set("max-packet-cache-entries", "Maximum number of entries in the packet cache") = "1000000";
256✔
289
  ::arg().set("max-signature-cache-entries", "Maximum number of signatures cache entries") = "";
256✔
290
  ::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone") = "100000";
256✔
291
  ::arg().set("entropy-source", "If set, read entropy from this file") = "/dev/urandom";
256✔
292

293
  ::arg().set("lua-prequery-script", "Lua script with prequery handler (DO NOT USE)") = "";
256✔
294
  ::arg().set("lua-dnsupdate-policy-script", "Lua script with DNS update policy handler") = "";
256✔
295
  ::arg().set("lua-global-include-dir", "Include *.lua files from this directory into Lua contexts") = "";
256✔
296

297
  ::arg().setSwitch("traceback-handler", "Enable the traceback handler (Linux only)") = "yes";
256✔
298
  ::arg().setSwitch("direct-dnskey", "Fetch DNSKEY, CDS and CDNSKEY RRs from backend during DNSKEY or CDS/CDNSKEY synthesis") = "no";
256✔
299
  ::arg().set("default-ksk-algorithm", "Default KSK algorithm") = "ecdsa256";
256✔
300
  ::arg().set("default-ksk-size", "Default KSK size (0 means default)") = "0";
256✔
301
  ::arg().set("default-zsk-algorithm", "Default ZSK algorithm") = "";
256✔
302
  ::arg().set("default-zsk-size", "Default ZSK size (0 means default)") = "0";
256✔
303
  ::arg().set("max-nsec3-iterations", "Limit the number of NSEC3 hash iterations") = "100";
256✔
304
  ::arg().set("default-publish-cdnskey", "Default value for PUBLISH-CDNSKEY") = "";
256✔
305
  ::arg().set("default-publish-cds", "Default value for PUBLISH-CDS") = "";
256✔
306

307
  ::arg().set("include-dir", "Include *.conf files from this directory");
256✔
308
  ::arg().set("security-poll-suffix", "Zone name from which to query security update notifications") = "secpoll.powerdns.com.";
256✔
309

310
  ::arg().setSwitch("expand-alias", "Expand ALIAS records") = "no";
256✔
311
  ::arg().set("outgoing-axfr-expand-alias", "Expand ALIAS records during outgoing AXFR") = "no";
256✔
312
  ::arg().setSwitch("resolve-across-zones", "Resolve CNAME targets and other referrals across local zones") = "yes";
256✔
313
  ::arg().setSwitch("8bit-dns", "Allow 8bit dns queries") = "no";
256✔
314
#ifdef HAVE_LUA_RECORDS
256✔
315
  ::arg().setSwitch("enable-lua-records", "Process LUA records for all zones (metadata overrides this)") = "no";
256✔
316
  ::arg().setSwitch("lua-records-insert-whitespace", "Insert whitespace when combining LUA chunks") = "no";
256✔
317
  ::arg().set("lua-records-exec-limit", "LUA records scripts execution limit (instructions count). Values <= 0 mean no limit") = "1000";
256✔
318
  ::arg().set("lua-health-checks-expire-delay", "Stops doing health checks after the record hasn't been used for that delay (in seconds)") = "3600";
256✔
319
  ::arg().set("lua-health-checks-interval", "LUA records health checks monitoring interval in seconds") = "5";
256✔
320
  ::arg().set("lua-consistent-hashes-cleanup-interval", "Pre-computed hashes cleanup interval (in seconds)") = "3600";
256✔
321
  ::arg().set("lua-consistent-hashes-expire-delay", "Cleanup pre-computed hashes that haven't been used for the given delay (in seconds). See pickchashed() LUA function") = "86400";
256✔
322
#endif
256✔
323
  ::arg().setSwitch("axfr-lower-serial", "Also AXFR a zone from a primary with a lower serial") = "no";
256✔
324

325
  ::arg().set("lua-axfr-script", "Script to be used to edit incoming AXFRs") = "";
256✔
326
  ::arg().set("xfr-max-received-mbytes", "Maximum number of megabytes received from an incoming XFR") = "100";
256✔
327
  ::arg().set("axfr-fetch-timeout", "Maximum time in seconds for inbound AXFR to start or be idle after starting") = "10";
256✔
328

329
  ::arg().set("tcp-fast-open", "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size") = "0";
256✔
330

331
  ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file") = "0";
256✔
332
  ::arg().set("max-include-depth", "Maximum number of nested $INCLUDE directives while processing a zone file") = "20";
256✔
333
  ::arg().setSwitch("upgrade-unknown-types", "Transparently upgrade known TYPExxx records. Recommended to keep off, except for PowerDNS upgrades until data sources are cleaned up") = "no";
256✔
334
  ::arg().setSwitch("svc-autohints", "Transparently fill ipv6hint=auto ipv4hint=auto SVC params with AAAA/A records for the target name of the record (if within the same zone)") = "no";
256✔
335

336
  ::arg().setSwitch("consistent-backends", "Assume individual zones are not divided over backends. Send only ANY lookup operations to the backend to reduce the number of lookups") = "yes";
256✔
337

338
  ::arg().set("rng", "Specify the random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.") = "auto";
256✔
339

340
  ::arg().set("default-catalog-zone", "Catalog zone to assign newly created primary zones (via the API) to") = "";
256✔
341

342
#ifdef ENABLE_GSS_TSIG
256✔
343
  ::arg().setSwitch("enable-gss-tsig", "Enable GSS TSIG processing") = "no";
256✔
344
#endif
256✔
345
  ::arg().setDefaults();
256✔
346
}
256✔
347

348
static time_t s_start = time(nullptr);
349
static uint64_t uptimeOfProcess(const std::string& /* str */)
350
{
13✔
351
  return time(nullptr) - s_start;
13✔
352
}
13✔
353

354
static uint64_t getSysUserTimeMsec(const std::string& str)
355
{
18✔
356
  struct rusage ru;
18✔
357
  getrusage(RUSAGE_SELF, &ru);
18✔
358

359
  if (str == "sys-msec") {
18✔
360
    return (ru.ru_stime.tv_sec * 1000ULL + ru.ru_stime.tv_usec / 1000);
9✔
361
  }
9✔
362
  else
9✔
363
    return (ru.ru_utime.tv_sec * 1000ULL + ru.ru_utime.tv_usec / 1000);
9✔
364
}
18✔
365

366
static uint64_t getTCPConnectionCount(const std::string& /* str */)
367
{
9✔
368
  return s_tcpNameserver->numTCPConnections();
9✔
369
}
9✔
370

371
static uint64_t getQCount(const std::string& /* str */)
372
try {
9✔
373
  int totcount = 0;
9✔
374
  for (const auto& d : s_distributors) {
9✔
375
    if (!d)
9!
376
      continue;
×
377
    totcount += d->getQueueSize(); // this does locking and other things, so don't get smart
9✔
378
  }
9✔
379
  return totcount;
9✔
380
}
9✔
381
catch (std::exception& e) {
9✔
382
  g_log << Logger::Error << "Had error retrieving queue sizes: " << e.what() << endl;
×
383
  return 0;
×
384
}
×
385
catch (PDNSException& e) {
9✔
386
  g_log << Logger::Error << "Had error retrieving queue sizes: " << e.reason << endl;
×
387
  return 0;
×
388
}
×
389

390
static uint64_t getLatency(const std::string& /* str */)
391
{
13✔
392
  return round(avg_latency);
13✔
393
}
13✔
394

395
static uint64_t getReceiveLatency(const std::string& /* str */)
396
{
9✔
397
  return round(receive_latency);
9✔
398
}
9✔
399

400
static uint64_t getCacheLatency(const std::string& /* str */)
401
{
9✔
402
  return round(cache_latency);
9✔
403
}
9✔
404

405
static uint64_t getBackendLatency(const std::string& /* str */)
406
{
9✔
407
  return round(backend_latency);
9✔
408
}
9✔
409

410
static uint64_t getSendLatency(const std::string& /* str */)
411
{
9✔
412
  return round(send_latency);
9✔
413
}
9✔
414

415
static void declareStats()
416
{
159✔
417
  S.declare("udp-queries", "Number of UDP queries received");
159✔
418
  S.declare("udp-do-queries", "Number of UDP queries received with DO bit");
159✔
419
  S.declare("udp-cookie-queries", "Number of UDP queries received with the COOKIE EDNS option");
159✔
420
  S.declare("udp-answers", "Number of answers sent out over UDP");
159✔
421
  S.declare("udp-answers-bytes", "Total size of answers sent out over UDP");
159✔
422
  S.declare("udp4-answers-bytes", "Total size of answers sent out over UDPv4");
159✔
423
  S.declare("udp6-answers-bytes", "Total size of answers sent out over UDPv6");
159✔
424

425
  S.declare("udp4-answers", "Number of IPv4 answers sent out over UDP");
159✔
426
  S.declare("udp4-queries", "Number of IPv4 UDP queries received");
159✔
427
  S.declare("udp6-answers", "Number of IPv6 answers sent out over UDP");
159✔
428
  S.declare("udp6-queries", "Number of IPv6 UDP queries received");
159✔
429
  S.declare("overload-drops", "Queries dropped because backends overloaded");
159✔
430

431
  S.declare("rd-queries", "Number of recursion desired questions");
159✔
432
  S.declare("recursion-unanswered", "Number of packets unanswered by configured recursor");
159✔
433
  S.declare("recursing-answers", "Number of recursive answers sent out");
159✔
434
  S.declare("recursing-questions", "Number of questions sent to recursor");
159✔
435
  S.declare("corrupt-packets", "Number of corrupt packets received");
159✔
436
  S.declare("signatures", "Number of DNSSEC signatures made");
159✔
437
  S.declare("tcp-queries", "Number of TCP queries received");
159✔
438
  S.declare("tcp-cookie-queries", "Number of TCP queries received with the COOKIE option");
159✔
439
  S.declare("tcp-answers", "Number of answers sent out over TCP");
159✔
440
  S.declare("tcp-answers-bytes", "Total size of answers sent out over TCP");
159✔
441
  S.declare("tcp4-answers-bytes", "Total size of answers sent out over TCPv4");
159✔
442
  S.declare("tcp6-answers-bytes", "Total size of answers sent out over TCPv6");
159✔
443

444
  S.declare("tcp4-queries", "Number of IPv4 TCP queries received");
159✔
445
  S.declare("tcp4-answers", "Number of IPv4 answers sent out over TCP");
159✔
446

447
  S.declare("tcp6-queries", "Number of IPv6 TCP queries received");
159✔
448
  S.declare("tcp6-answers", "Number of IPv6 answers sent out over TCP");
159✔
449

450
  S.declare("open-tcp-connections", "Number of currently open TCP connections", getTCPConnectionCount, StatType::gauge);
159✔
451

452
  S.declare("qsize-q", "Number of questions waiting for database attention", getQCount, StatType::gauge);
159✔
453

454
  S.declare("dnsupdate-queries", "DNS update packets received.");
159✔
455
  S.declare("dnsupdate-answers", "DNS update packets successfully answered.");
159✔
456
  S.declare("dnsupdate-refused", "DNS update packets that are refused.");
159✔
457
  S.declare("dnsupdate-changes", "DNS update changes to records in total.");
159✔
458

459
  S.declare("incoming-notifications", "NOTIFY packets received.");
159✔
460

461
  S.declare("uptime", "Uptime of process in seconds", uptimeOfProcess, StatType::counter);
159✔
462
  S.declare("real-memory-usage", "Actual unique use of memory in bytes (approx)", getRealMemoryUsage, StatType::gauge);
159✔
463
  S.declare("special-memory-usage", "Actual unique use of memory in bytes (approx)", getSpecialMemoryUsage, StatType::gauge);
159✔
464
  S.declare("fd-usage", "Number of open filedescriptors", getOpenFileDescriptors, StatType::gauge);
159✔
465
#ifdef __linux__
159✔
466
  S.declare("udp-recvbuf-errors", "UDP 'recvbuf' errors", udpErrorStats, StatType::counter);
159✔
467
  S.declare("udp-sndbuf-errors", "UDP 'sndbuf' errors", udpErrorStats, StatType::counter);
159✔
468
  S.declare("udp-noport-errors", "UDP 'noport' errors", udpErrorStats, StatType::counter);
159✔
469
  S.declare("udp-in-errors", "UDP 'in' errors", udpErrorStats, StatType::counter);
159✔
470
  S.declare("udp-in-csum-errors", "UDP 'in checksum' errors", udpErrorStats, StatType::counter);
159✔
471
  S.declare("udp6-in-errors", "UDP 'in' errors over IPv6", udp6ErrorStats, StatType::counter);
159✔
472
  S.declare("udp6-recvbuf-errors", "UDP 'recvbuf' errors over IPv6", udp6ErrorStats, StatType::counter);
159✔
473
  S.declare("udp6-sndbuf-errors", "UDP 'sndbuf' errors over IPv6", udp6ErrorStats, StatType::counter);
159✔
474
  S.declare("udp6-noport-errors", "UDP 'noport' errors over IPv6", udp6ErrorStats, StatType::counter);
159✔
475
  S.declare("udp6-in-csum-errors", "UDP 'in checksum' errors over IPv6", udp6ErrorStats, StatType::counter);
159✔
476
#endif
159✔
477

478
  S.declare("sys-msec", "Number of msec spent in system time", getSysUserTimeMsec, StatType::counter);
159✔
479
  S.declare("user-msec", "Number of msec spent in user time", getSysUserTimeMsec, StatType::counter);
159✔
480

481
#ifdef __linux__
159✔
482
  S.declare("cpu-iowait", "Time spent waiting for I/O to complete by the whole system, in units of USER_HZ", getCPUIOWait, StatType::counter);
159✔
483
  S.declare("cpu-steal", "Stolen time, which is the time spent by the whole system in other operating systems when running in a virtualized environment, in units of USER_HZ", getCPUSteal, StatType::counter);
159✔
484
#endif
159✔
485

486
  S.declare("meta-cache-size", "Number of entries in the metadata cache", DNSSECKeeper::dbdnssecCacheSizes, StatType::gauge);
159✔
487
  S.declare("key-cache-size", "Number of entries in the key cache", DNSSECKeeper::dbdnssecCacheSizes, StatType::gauge);
159✔
488
  S.declare("signature-cache-size", "Number of entries in the signature cache", signatureCacheSize, StatType::gauge);
159✔
489

490
  S.declare("nxdomain-packets", "Number of times an NXDOMAIN packet was sent out");
159✔
491
  S.declare("noerror-packets", "Number of times a NOERROR packet was sent out");
159✔
492
  S.declare("servfail-packets", "Number of times a server-failed packet was sent out");
159✔
493
  S.declare("unauth-packets", "Number of times a zone we are not auth for was queried");
159✔
494
  S.declare("latency", "Average number of microseconds needed to answer a question", getLatency, StatType::gauge);
159✔
495
  S.declare("receive-latency", "Average number of microseconds needed to receive a query", getReceiveLatency, StatType::gauge);
159✔
496
  S.declare("cache-latency", "Average number of microseconds needed for a packet cache lookup", getCacheLatency, StatType::gauge);
159✔
497
  S.declare("backend-latency", "Average number of microseconds needed for a backend lookup", getBackendLatency, StatType::gauge);
159✔
498
  S.declare("send-latency", "Average number of microseconds needed to send the answer", getSendLatency, StatType::gauge);
159✔
499
  S.declare("timedout-packets", "Number of packets which weren't answered within timeout set");
159✔
500
  S.declare("security-status", "Security status based on regular polling", StatType::gauge);
159✔
501
  S.declare(
159✔
502
    "xfr-queue", "Size of the queue of zones to be XFRd", [](const string&) { return Communicator.getSuckRequestsWaiting(); }, StatType::gauge);
163✔
503
  S.declareDNSNameQTypeRing("queries", "UDP Queries Received");
159✔
504
  S.declareDNSNameQTypeRing("nxdomain-queries", "Queries for nonexistent records within existent zones");
159✔
505
  S.declareDNSNameQTypeRing("noerror-queries", "Queries for existing records, but for type we don't have");
159✔
506
  S.declareDNSNameQTypeRing("servfail-queries", "Queries that could not be answered due to backend errors");
159✔
507
  S.declareDNSNameQTypeRing("unauth-queries", "Queries for zones that we are not authoritative for");
159✔
508
  S.declareRing("logmessages", "Log Messages");
159✔
509
  S.declareComboRing("remotes", "Remote server IP addresses");
159✔
510
  S.declareComboRing("remotes-unauth", "Remote hosts querying zones for which we are not auth");
159✔
511
  S.declareComboRing("remotes-corrupt", "Remote hosts sending corrupt packets");
159✔
512
}
159✔
513

514
static int isGuarded(char** argv)
515
{
159✔
516
  char* p = strstr(argv[0], "-instance");
159✔
517

518
  return !!p;
159✔
519
}
159✔
520

521
static void sendout(std::unique_ptr<DNSPacket>& a, int start)
522
{
86,769✔
523
  if (!a)
86,769✔
524
    return;
187✔
525

526
  try {
86,582✔
527
    int diff = a->d_dt.udiffNoReset();
86,582✔
528
    backend_latency = 0.999 * backend_latency + 0.001 * std::max(diff - start, 0);
86,582✔
529
    start = diff;
86,582✔
530

531
    s_udpNameserver->send(*a);
86,582✔
532

533
    diff = a->d_dt.udiff();
86,582✔
534
    send_latency = 0.999 * send_latency + 0.001 * std::max(diff - start, 0);
86,582✔
535

536
    avg_latency = 0.999 * avg_latency + 0.001 * std::max(diff, 0);
86,582✔
537
  }
86,582✔
538
  catch (const std::exception& e) {
86,582✔
539
    g_log << Logger::Error << "Caught unhandled exception while sending a response: " << e.what() << endl;
×
540
  }
×
541
}
86,582✔
542

543
//! The qthread receives questions over the internet via the Nameserver class, and hands them to the Distributor for further processing
544
static void qthread(unsigned int num)
545
try {
159✔
546
  setThreadName("pdns/receiver");
159✔
547

548
  s_distributors[num] = DNSDistributor::Create(::arg().asNum("distributor-threads", 1));
159✔
549
  DNSDistributor* distributor = s_distributors[num]; // the big dispatcher!
159✔
550
  DNSPacket question(true);
159✔
551
  DNSPacket cached(false);
159✔
552

553
  AtomicCounter& numreceived = *S.getPointer("udp-queries");
159✔
554
  AtomicCounter& numreceiveddo = *S.getPointer("udp-do-queries");
159✔
555
  AtomicCounter& numreceivedcookie = *S.getPointer("udp-cookie-queries");
159✔
556

557
  AtomicCounter& numreceived4 = *S.getPointer("udp4-queries");
159✔
558

559
  AtomicCounter& numreceived6 = *S.getPointer("udp6-queries");
159✔
560
  AtomicCounter& overloadDrops = *S.getPointer("overload-drops");
159✔
561

562
  int diff, start;
159✔
563
  bool logDNSQueries = ::arg().mustDo("log-dns-queries");
159✔
564
  shared_ptr<UDPNameserver> NS;
159✔
565
  std::string buffer;
159✔
566
  ComboAddress accountremote;
159✔
567

568
  // If we have SO_REUSEPORT then create a new port for all receiver threads
569
  // other than the first one.
570
  if (s_udpNameserver->canReusePort()) {
159!
571
    NS = s_udpReceivers[num];
×
572
    if (NS == nullptr) {
×
573
      NS = s_udpNameserver;
×
574
    }
×
575
  }
×
576
  else {
159✔
577
    NS = s_udpNameserver;
159✔
578
  }
159✔
579

580
  for (;;) {
97,173✔
581
    try {
97,173✔
582
      if (g_proxyProtocolACL.empty()) {
97,173!
583
        buffer.resize(DNSPacket::s_udpTruncationThreshold);
97,173✔
584
      }
97,173✔
585
      else {
×
586
        buffer.resize(DNSPacket::s_udpTruncationThreshold + g_proxyProtocolMaximumSize);
×
587
      }
×
588

589
      if (!NS->receive(question, buffer)) { // receive a packet         inline
97,173!
590
        continue; // packet was broken, try again
×
591
      }
×
592

593
      diff = question.d_dt.udiffNoReset();
97,173✔
594
      receive_latency = 0.999 * receive_latency + 0.001 * std::max(diff, 0);
97,173✔
595

596
      numreceived++;
97,173✔
597

598
      accountremote = question.d_remote;
97,173✔
599
      if (question.d_inner_remote)
97,173!
600
        accountremote = *question.d_inner_remote;
×
601

602
      if (accountremote.sin4.sin_family == AF_INET)
97,173✔
603
        numreceived4++;
97,012✔
604
      else
161✔
605
        numreceived6++;
161✔
606

607
      if (question.d_dnssecOk)
97,173✔
608
        numreceiveddo++;
88,805✔
609

610
      if (question.hasEDNSCookie())
97,173!
611
        numreceivedcookie++;
×
612

613
      if (question.d.qr)
97,173!
614
        continue;
×
615

616
      S.ringAccount("queries", question.qdomain, question.qtype);
97,173✔
617
      S.ringAccount("remotes", question.getInnerRemote());
97,173✔
618
      if (logDNSQueries) {
97,173✔
619
        g_log << Logger::Notice << "Remote " << question.getRemoteString() << " wants '" << question.qdomain << "|" << question.qtype << "', do = " << question.d_dnssecOk << ", bufsize = " << question.getMaxReplyLen();
16✔
620
        if (question.d_ednsRawPacketSizeLimit > 0 && question.getMaxReplyLen() != (unsigned int)question.d_ednsRawPacketSizeLimit)
16!
621
          g_log << " (" << question.d_ednsRawPacketSizeLimit << ")";
×
622
      }
16✔
623

624
      if (PC.enabled() && (question.d.opcode != Opcode::Notify && question.d.opcode != Opcode::Update) && question.couldBeCached()) {
97,173!
625
        start = diff;
22,263✔
626
        bool haveSomething = PC.get(question, cached); // does the PacketCache recognize this question?
22,263✔
627
        if (haveSomething) {
22,263✔
628
          if (logDNSQueries)
10,233!
629
            g_log << ": packetcache HIT" << endl;
×
630
          cached.setRemote(&question.d_remote); // inlined
10,233✔
631
          cached.d_inner_remote = question.d_inner_remote;
10,233✔
632
          cached.setSocket(question.getSocket()); // inlined
10,233✔
633
          cached.d_anyLocal = question.d_anyLocal;
10,233✔
634
          cached.setMaxReplyLen(question.getMaxReplyLen());
10,233✔
635
          cached.d.rd = question.d.rd; // copy in recursion desired bit
10,233✔
636
          cached.d.id = question.d.id;
10,233✔
637
          cached.commitD(); // commit d to the packet                        inlined
10,233✔
638

639
          diff = question.d_dt.udiffNoReset();
10,233✔
640
          cache_latency = 0.999 * cache_latency + 0.001 * std::max(diff - start, 0);
10,233✔
641
          start = diff;
10,233✔
642

643
          NS->send(cached); // answer it then                              inlined
10,233✔
644

645
          diff = question.d_dt.udiff();
10,233✔
646
          send_latency = 0.999 * send_latency + 0.001 * std::max(diff - start, 0);
10,233✔
647
          avg_latency = 0.999 * avg_latency + 0.001 * std::max(diff, 0); // 'EWMA'
10,233✔
648
          continue;
10,233✔
649
        }
10,233✔
650
        diff = question.d_dt.udiffNoReset();
12,030✔
651
        cache_latency = 0.999 * cache_latency + 0.001 * std::max(diff - start, 0);
12,030✔
652
      }
12,030✔
653

654
      if (distributor->isOverloaded()) {
86,940✔
655
        if (logDNSQueries)
1!
656
          g_log << ": Dropped query, backends are overloaded" << endl;
1✔
657
        overloadDrops++;
1✔
658
        continue;
1✔
659
      }
1✔
660

661
      if (logDNSQueries) {
86,939✔
662
        if (PC.enabled()) {
15!
663
          g_log << ": packetcache MISS" << endl;
15✔
664
        }
15✔
665
        else {
×
666
          g_log << endl;
×
667
        }
×
668
      }
15✔
669

670
      try {
86,939✔
671
        distributor->question(question, &sendout); // otherwise, give to the distributor
86,939✔
672
      }
86,939✔
673
      catch (DistributorFatal& df) { // when this happens, we have leaked loads of memory. Bailing out time.
86,939✔
674
        _exit(1);
×
675
      }
×
676
    }
86,939✔
677
    catch (const std::exception& e) {
97,173✔
678
      g_log << Logger::Error << "Caught unhandled exception in question thread: " << e.what() << endl;
×
679
    }
×
680
  }
97,173✔
681
}
159✔
682
catch (PDNSException& pe) {
159✔
683
  g_log << Logger::Error << "Fatal error in question thread: " << pe.reason << endl;
×
684
  _exit(1);
×
685
}
×
686

687
static void dummyThread()
688
{
×
689
}
×
690

691
static void triggerLoadOfLibraries()
692
{
×
693
  std::thread dummy(dummyThread);
×
694
  dummy.join();
×
695
}
×
696

697
static void mainthread()
698
{
159✔
699
  gid_t newgid = 0;
159✔
700
  if (!::arg()["setgid"].empty())
159!
701
    newgid = strToGID(::arg()["setgid"]);
×
702
  uid_t newuid = 0;
159✔
703
  if (!::arg()["setuid"].empty())
159!
704
    newuid = strToUID(::arg()["setuid"]);
×
705

706
  g_anyToTcp = ::arg().mustDo("any-to-tcp");
159✔
707
  g_8bitDNS = ::arg().mustDo("8bit-dns");
159✔
708
#ifdef HAVE_LUA_RECORDS
159✔
709
  g_doLuaRecord = ::arg().mustDo("enable-lua-records");
159✔
710
  g_LuaRecordSharedState = (::arg()["enable-lua-records"] == "shared");
159✔
711
  g_luaRecordExecLimit = ::arg().asNum("lua-records-exec-limit");
159✔
712
  g_luaRecordInsertWhitespace = ::arg().mustDo("lua-records-insert-whitespace");
159✔
713
  g_luaHealthChecksInterval = ::arg().asNum("lua-health-checks-interval");
159✔
714
  g_luaConsistentHashesExpireDelay = ::arg().asNum("lua-consistent-hashes-expire-delay");
159✔
715
  g_luaConsistentHashesCleanupInterval = ::arg().asNum("lua-consistent-hashes-cleanup-interval");
159✔
716
  g_luaHealthChecksExpireDelay = ::arg().asNum("lua-health-checks-expire-delay");
159✔
717
#endif
159✔
718
#ifdef ENABLE_GSS_TSIG
159✔
719
  g_doGssTSIG = ::arg().mustDo("enable-gss-tsig");
159✔
720
#endif
159✔
721

722
  DNSPacket::s_udpTruncationThreshold = std::max(512, ::arg().asNum("udp-truncation-threshold"));
159✔
723
  DNSPacket::s_doEDNSSubnetProcessing = ::arg().mustDo("edns-subnet-processing");
159✔
724
  PacketHandler::s_SVCAutohints = ::arg().mustDo("svc-autohints");
159✔
725

726
  g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]);
159✔
727
  g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
159✔
728

729
  if (::arg()["edns-cookie-secret"].size() != 0) {
159!
730
    // User wants cookie processing
731
#ifdef HAVE_CRYPTO_SHORTHASH // we can do siphash-based cookies
×
732
    DNSPacket::s_doEDNSCookieProcessing = true;
×
733
    try {
×
734
      if (::arg()["edns-cookie-secret"].size() != EDNSCookiesOpt::EDNSCookieSecretSize) {
×
735
        throw std::range_error("wrong size (" + std::to_string(::arg()["edns-cookie-secret"].size()) + "), must be " + std::to_string(EDNSCookiesOpt::EDNSCookieSecretSize));
×
736
      }
×
737
      DNSPacket::s_EDNSCookieKey = makeBytesFromHex(::arg()["edns-cookie-secret"]);
×
738
    }
×
739
    catch (const std::range_error& e) {
×
740
      g_log << Logger::Error << "edns-cookie-secret invalid: " << e.what() << endl;
×
741
      exit(1);
×
742
    }
×
743
#else
744
    g_log << Logger::Error << "Support for EDNS Cookies is not available because of missing cryptographic functions (libsodium support should be enabled, with the crypto_shorthash() function available)" << endl;
745
    exit(1);
746
#endif
747
  }
×
748

749
  PC.setTTL(::arg().asNum("cache-ttl"));
159✔
750
  PC.setMaxEntries(::arg().asNum("max-packet-cache-entries"));
159✔
751
  QC.setMaxEntries(::arg().asNum("max-cache-entries"));
159✔
752
  DNSSECKeeper::setMaxEntries(::arg().asNum("max-cache-entries"));
159✔
753

754
  if (!PC.enabled() && ::arg().mustDo("log-dns-queries")) {
159!
755
    g_log << Logger::Warning << "Packet cache disabled, logging queries without HIT/MISS" << endl;
×
756
  }
×
757
  if (::arg()["outgoing-axfr-expand-alias"] == "ignore-errors") {
159!
758
    g_log << Logger::Error << "Ignoring ALIAS resolve failures on outgoing AXFR transfers, see option \"outgoing-axfr-expand-alias\"" << endl;
×
759
  }
×
760

761
  stubParseResolveConf();
159✔
762

763
  if (!::arg()["chroot"].empty()) {
159!
764
#ifdef HAVE_SYSTEMD
×
765
    char* ns;
×
766
    ns = getenv("NOTIFY_SOCKET");
×
767
    if (ns != nullptr) {
×
768
      g_log << Logger::Error << "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'" << endl;
×
769
      exit(1);
×
770
    }
×
771
#endif
×
772
    triggerLoadOfLibraries();
×
773
    if (::arg().mustDo("primary") || ::arg().mustDo("secondary"))
×
774
      gethostbyname("a.root-servers.net"); // this forces all lookup libraries to be loaded
×
775
    Utility::dropGroupPrivs(newuid, newgid);
×
776
    if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
×
777
      g_log << Logger::Error << "Unable to chroot to '" + ::arg()["chroot"] + "': " << stringerror() << ", exiting" << endl;
×
778
      exit(1);
×
779
    }
×
780
    else
×
781
      g_log << Logger::Error << "Chrooted to '" << ::arg()["chroot"] << "'" << endl;
×
782
  }
×
783
  else {
159✔
784
    Utility::dropGroupPrivs(newuid, newgid);
159✔
785
  }
159✔
786

787
  AuthWebServer webserver;
159✔
788
  Utility::dropUserPrivs(newuid);
159✔
789

790
  if (::arg().mustDo("resolver")) {
159✔
791
    DP = std::make_unique<DNSProxy>(::arg()["resolver"], ::arg()["dnsproxy-udp-port-range"]);
71✔
792
    DP->go();
71✔
793
  }
71✔
794

795
  try {
159✔
796
    doSecPoll(true);
159✔
797
  }
159✔
798
  catch (...) {
159✔
799
  }
×
800

801
  {
159✔
802
    // Some sanity checking on default key settings
803
    bool hadKeyError = false;
159✔
804
    int kskAlgo{0}, zskAlgo{0};
159✔
805
    for (const string algotype : {"ksk", "zsk"}) {
318✔
806
      int algo, size;
318✔
807
      if (::arg()["default-" + algotype + "-algorithm"].empty())
318✔
808
        continue;
159✔
809
      algo = DNSSECKeeper::shorthand2algorithm(::arg()["default-" + algotype + "-algorithm"]);
159✔
810
      size = ::arg().asNum("default-" + algotype + "-size");
159✔
811
      if (algo == -1) {
159!
812
        g_log << Logger::Error << "Error: default-" << algotype << "-algorithm set to unknown algorithm: " << ::arg()["default-" + algotype + "-algorithm"] << endl;
×
813
        hadKeyError = true;
×
814
      }
×
815
      else if (algo <= 10 && size == 0) {
159!
816
        g_log << Logger::Error << "Error: default-" << algotype << "-algorithm is set to an algorithm (" << ::arg()["default-" + algotype + "-algorithm"] << ") that requires a non-zero default-" << algotype << "-size!" << endl;
×
817
        hadKeyError = true;
×
818
      }
×
819
      if (algotype == "ksk") {
159!
820
        kskAlgo = algo;
159✔
821
      }
159✔
822
      else {
×
823
        zskAlgo = algo;
×
824
      }
×
825
    }
159✔
826
    if (hadKeyError) {
159!
827
      exit(1);
×
828
    }
×
829
    if (kskAlgo == 0 && zskAlgo != 0) {
159!
830
      g_log << Logger::Error << "Error: default-zsk-algorithm is set, but default-ksk-algorithm is not set." << endl;
×
831
      exit(1);
×
832
    }
×
833
    if (zskAlgo != 0 && zskAlgo != kskAlgo) {
159!
834
      g_log << Logger::Error << "Error: default-zsk-algorithm (" << ::arg()["default-zsk-algorithm"] << "), when set, can not be different from default-ksk-algorithm (" << ::arg()["default-ksk-algorithm"] << ")." << endl;
×
835
      exit(1);
×
836
    }
×
837
  }
159✔
838

839
  pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
159✔
840

841
  pdns::parseTrustedNotificationProxy(::arg()["trusted-notification-proxy"]);
159✔
842

843
  UeberBackend::go();
159✔
844

845
  // Setup the zone cache
846
  g_zoneCache.setRefreshInterval(::arg().asNum("zone-cache-refresh-interval"));
159✔
847
  try {
159✔
848
    UeberBackend B;
159✔
849
    B.updateZoneCache();
159✔
850
  }
159✔
851
  catch (PDNSException& e) {
159✔
852
    g_log << Logger::Error << "PDNSException while filling the zone cache: " << e.reason << endl;
×
853
    exit(1);
×
854
  }
×
855
  catch (std::exception& e) {
159✔
856
    g_log << Logger::Error << "STL Exception while filling the zone cache: " << e.what() << endl;
×
857
    exit(1);
×
858
  }
×
859

860
  // NOW SAFE TO CREATE THREADS!
861
  s_dynListener->go();
159✔
862

863
  if (::arg().mustDo("webserver") || ::arg().mustDo("api")) {
159✔
864
    webserver.go(S);
12✔
865
  }
12✔
866

867
  if (::arg().mustDo("primary") || ::arg().mustDo("secondary") || !::arg()["forward-notify"].empty())
159!
868
    Communicator.go();
108✔
869

870
  s_tcpNameserver->go(); // tcp nameserver launch
159✔
871

872
  unsigned int max_rthreads = ::arg().asNum("receiver-threads", 1);
159✔
873
  s_distributors.resize(max_rthreads);
159✔
874
  for (unsigned int n = 0; n < max_rthreads; ++n) {
318✔
875
    std::thread t(qthread, n);
159✔
876
    t.detach();
159✔
877
  }
159✔
878

879
  std::thread carbonThread(carbonDumpThread); // runs even w/o carbon, might change @ runtime
159✔
880

881
#ifdef HAVE_SYSTEMD
159✔
882
  /* If we are here, notify systemd that we are ay-ok! This might have some
883
   * timing issues with the backend-threads. e.g. if the initial MySQL connection
884
   * is slow and times out (leading to process termination through the backend)
885
   * We probably have told systemd already that we have started correctly.
886
   */
887
  sd_notify(0, "READY=1");
159✔
888
#endif
159✔
889

890
  const uint32_t secpollInterval = 1800;
159✔
891
  uint32_t secpollSince = 0;
159✔
892
  uint32_t zoneCacheUpdateSince = 0;
159✔
893
  for (;;) {
199✔
894
    const uint32_t sleeptime = g_zoneCache.getRefreshInterval() == 0 ? secpollInterval : std::min(secpollInterval, g_zoneCache.getRefreshInterval());
199✔
895
    sleep(sleeptime); // if any signals arrive, we might run more often than expected.
199✔
896

897
    zoneCacheUpdateSince += sleeptime;
199✔
898
    if (zoneCacheUpdateSince >= g_zoneCache.getRefreshInterval()) {
199✔
899
      try {
40✔
900
        UeberBackend B;
40✔
901
        B.updateZoneCache();
40✔
902
        zoneCacheUpdateSince = 0;
40✔
903
      }
40✔
904
      catch (PDNSException& e) {
40✔
905
        g_log << Logger::Error << "PDNSException while updating zone cache: " << e.reason << endl;
×
906
      }
×
907
      catch (std::exception& e) {
40✔
908
        g_log << Logger::Error << "STL Exception while updating zone cache: " << e.what() << endl;
×
909
      }
×
910
    }
40✔
911

912
    secpollSince += sleeptime;
199✔
913
    if (secpollSince >= secpollInterval) {
199!
914
      secpollSince = 0;
×
915
      try {
×
916
        doSecPoll(false);
×
917
      }
×
918
      catch (...) {
×
919
      }
×
920
    }
×
921
  }
199✔
922

923
  g_log << Logger::Error << "Mainthread exiting - should never happen" << endl;
159✔
924
}
159✔
925

926
static void daemonize()
927
{
×
928
  if (fork())
×
929
    exit(0); // bye bye
×
930

931
  setsid();
×
932

933
  int i = open("/dev/null", O_RDWR); /* open stdin */
×
934
  if (i < 0)
×
935
    g_log << Logger::Critical << "Unable to open /dev/null: " << stringerror() << endl;
×
936
  else {
×
937
    dup2(i, 0); /* stdin */
×
938
    dup2(i, 1); /* stderr */
×
939
    dup2(i, 2); /* stderr */
×
940
    close(i);
×
941
  }
×
942
}
×
943

944
static int cpid;
945
static void takedown(int /* i */)
946
{
×
947
  if (cpid) {
×
948
    g_log << Logger::Error << "Guardian is killed, taking down children with us" << endl;
×
949
    kill(cpid, SIGKILL);
×
950
    exit(0);
×
951
  }
×
952
}
×
953

954
static void writePid()
955
{
159✔
956
  if (!::arg().mustDo("write-pid"))
159!
957
    return;
×
958

959
  string fname = ::arg()["socket-dir"];
159✔
960
  if (::arg()["socket-dir"].empty()) {
159!
961
    if (::arg()["chroot"].empty())
×
962
      fname = std::string(LOCALSTATEDIR) + "/pdns";
×
963
    else
×
964
      fname = ::arg()["chroot"] + "/";
×
965
  }
×
966
  else if (!::arg()["socket-dir"].empty() && !::arg()["chroot"].empty()) {
159!
967
    fname = ::arg()["chroot"] + ::arg()["socket-dir"];
×
968
  }
×
969

970
  fname += +"/" + g_programname + ".pid";
159✔
971
  ofstream of(fname.c_str());
159✔
972
  if (of)
159!
973
    of << getpid() << endl;
159✔
974
  else
×
975
    g_log << Logger::Error << "Writing pid for " << getpid() << " to " << fname << " failed: " << stringerror() << endl;
×
976
}
159✔
977

978
static int g_fd1[2], g_fd2[2];
979
static FILE* g_fp;
980
static std::mutex g_guardian_lock;
981

982
// The next two methods are not in dynhandler.cc because they use a few items declared in this file.
983
static string DLCycleHandler(const vector<string>& /* parts */, pid_t /* ppid */)
984
{
×
985
  kill(cpid, SIGKILL); // why?
×
986
  kill(cpid, SIGKILL); // why?
×
987
  sleep(1);
×
988
  return "ok";
×
989
}
×
990

991
static string DLRestHandler(const vector<string>& parts, pid_t /* ppid */)
992
{
×
993
  string line;
×
994

995
  for (vector<string>::const_iterator i = parts.begin(); i != parts.end(); ++i) {
×
996
    if (i != parts.begin())
×
997
      line.append(1, ' ');
×
998
    line.append(*i);
×
999
  }
×
1000
  line.append(1, '\n');
×
1001

1002
  std::lock_guard<std::mutex> l(g_guardian_lock);
×
1003

1004
  try {
×
1005
    writen2(g_fd1[1], line.c_str(), line.size() + 1);
×
1006
  }
×
1007
  catch (PDNSException& ae) {
×
1008
    return "Error communicating with instance: " + ae.reason;
×
1009
  }
×
1010
  char mesg[512];
×
1011
  string response;
×
1012
  while (fgets(mesg, sizeof(mesg), g_fp)) {
×
1013
    if (*mesg == '\0')
×
1014
      break;
×
1015
    response += mesg;
×
1016
  }
×
1017
  boost::trim_right(response);
×
1018
  return response;
×
1019
}
×
1020

1021
static int guardian(int argc, char** argv)
1022
{
×
1023
  if (isGuarded(argv))
×
1024
    return 0;
×
1025

1026
  int infd = 0, outfd = 1;
×
1027

1028
  DynListener dlg(g_programname);
×
1029
  dlg.registerFunc("QUIT", &DLQuitHandler, "quit daemon");
×
1030
  dlg.registerFunc("CYCLE", &DLCycleHandler, "restart instance");
×
1031
  dlg.registerFunc("PING", &DLPingHandler, "ping guardian");
×
1032
  dlg.registerFunc("STATUS", &DLStatusHandler, "get instance status from guardian");
×
1033
  dlg.registerRestFunc(&DLRestHandler);
×
1034
  dlg.go();
×
1035
  string progname = argv[0];
×
1036

1037
  bool first = true;
×
1038
  cpid = 0;
×
1039

1040
  g_guardian_lock.lock();
×
1041

1042
  for (;;) {
×
1043
    int pid;
×
1044
    setStatus("Launching child");
×
1045

1046
    if (pipe(g_fd1) < 0 || pipe(g_fd2) < 0) {
×
1047
      g_log << Logger::Critical << "Unable to open pipe for coprocess: " << stringerror() << endl;
×
1048
      exit(1);
×
1049
    }
×
1050

1051
    if (!(g_fp = fdopen(g_fd2[0], "r"))) {
×
1052
      g_log << Logger::Critical << "Unable to associate a file pointer with pipe: " << stringerror() << endl;
×
1053
      exit(1);
×
1054
    }
×
1055
    setbuf(g_fp, nullptr); // no buffering please, confuses select
×
1056

1057
    if (!(pid = fork())) { // child
×
1058
      signal(SIGTERM, SIG_DFL);
×
1059

1060
      signal(SIGHUP, SIG_DFL);
×
1061
      signal(SIGUSR1, SIG_DFL);
×
1062
      signal(SIGUSR2, SIG_DFL);
×
1063

1064
      char** const newargv = new char*[argc + 2];
×
1065
      int n;
×
1066

1067
      if (::arg()["config-name"] != "") {
×
1068
        progname += "-" + ::arg()["config-name"];
×
1069
        g_log << Logger::Error << "Virtual configuration name: " << ::arg()["config-name"] << endl;
×
1070
      }
×
1071

1072
      newargv[0] = strdup(const_cast<char*>((progname + "-instance").c_str()));
×
1073
      for (n = 1; n < argc; n++) {
×
1074
        newargv[n] = argv[n];
×
1075
      }
×
1076
      newargv[n] = nullptr;
×
1077

1078
      g_log << Logger::Error << "Guardian is launching an instance" << endl;
×
1079
      close(g_fd1[1]);
×
1080
      fclose(g_fp); // this closes g_fd2[0] for us
×
1081

1082
      if (g_fd1[0] != infd) {
×
1083
        dup2(g_fd1[0], infd);
×
1084
        close(g_fd1[0]);
×
1085
      }
×
1086

1087
      if (g_fd2[1] != outfd) {
×
1088
        dup2(g_fd2[1], outfd);
×
1089
        close(g_fd2[1]);
×
1090
      }
×
1091
      if (execvp(argv[0], newargv) < 0) {
×
1092
        g_log << Logger::Error << "Unable to execvp '" << argv[0] << "': " << stringerror() << endl;
×
1093
        char** p = newargv;
×
1094
        while (*p)
×
1095
          g_log << Logger::Error << *p++ << endl;
×
1096

1097
        exit(1);
×
1098
      }
×
1099
      g_log << Logger::Error << "execvp returned!!" << endl;
×
1100
      // never reached
1101
    }
×
1102
    else if (pid > 0) { // parent
×
1103
      close(g_fd1[0]);
×
1104
      close(g_fd2[1]);
×
1105

1106
      if (first) {
×
1107
        first = false;
×
1108
        signal(SIGTERM, takedown);
×
1109

1110
        signal(SIGHUP, SIG_IGN);
×
1111
        signal(SIGUSR1, SIG_IGN);
×
1112
        signal(SIGUSR2, SIG_IGN);
×
1113

1114
        writePid();
×
1115
      }
×
1116
      g_guardian_lock.unlock();
×
1117
      int status;
×
1118
      cpid = pid;
×
1119
      for (;;) {
×
1120
        int ret = waitpid(pid, &status, WNOHANG);
×
1121

1122
        if (ret < 0) {
×
1123
          g_log << Logger::Error << "In guardian loop, waitpid returned error: " << stringerror() << endl;
×
1124
          g_log << Logger::Error << "Dying" << endl;
×
1125
          exit(1);
×
1126
        }
×
1127
        else if (ret) // something exited
×
1128
          break;
×
1129
        else { // child is alive
×
1130
          // execute some kind of ping here
1131
          if (DLQuitPlease())
×
1132
            takedown(1); // needs a parameter..
×
1133
          setStatus("Child running on pid " + std::to_string(pid));
×
1134
          sleep(1);
×
1135
        }
×
1136
      }
×
1137

1138
      g_guardian_lock.lock();
×
1139
      close(g_fd1[1]);
×
1140
      fclose(g_fp);
×
1141
      g_fp = nullptr;
×
1142

1143
      if (WIFEXITED(status)) {
×
1144
        int ret = WEXITSTATUS(status);
×
1145

1146
        if (ret == 99) {
×
1147
          g_log << Logger::Error << "Child requested a stop, exiting" << endl;
×
1148
          exit(1);
×
1149
        }
×
1150
        setStatus("Child died with code " + std::to_string(ret));
×
1151
        g_log << Logger::Error << "Our pdns instance exited with code " << ret << ", respawning" << endl;
×
1152

1153
        sleep(1);
×
1154
        continue;
×
1155
      }
×
1156
      if (WIFSIGNALED(status)) {
×
1157
        int sig = WTERMSIG(status);
×
1158
        setStatus("Child died because of signal " + std::to_string(sig));
×
1159
        g_log << Logger::Error << "Our pdns instance (" << pid << ") exited after signal " << sig << endl;
×
1160
#ifdef WCOREDUMP
×
1161
        if (WCOREDUMP(status))
×
1162
          g_log << Logger::Error << "Dumped core" << endl;
×
1163
#endif
×
1164

1165
        g_log << Logger::Error << "Respawning" << endl;
×
1166
        sleep(1);
×
1167
        continue;
×
1168
      }
×
1169
      g_log << Logger::Error << "No clue what happened! Respawning" << endl;
×
1170
    }
×
1171
    else {
×
1172
      g_log << Logger::Error << "Unable to fork: " << stringerror() << endl;
×
1173
      exit(1);
×
1174
    }
×
1175
  }
×
1176
}
×
1177

1178
#if defined(__GLIBC__) && !defined(__UCLIBC__)
1179
#include <execinfo.h>
1180
static void tbhandler(int num)
1181
{
×
1182
  g_log << Logger::Critical << "Got a signal " << num << ", attempting to print trace: " << endl;
×
1183
  void* array[20]; // only care about last 17 functions (3 taken with tracing support)
×
1184
  size_t size;
×
1185
  char** strings;
×
1186
  size_t i;
×
1187

1188
  size = backtrace(array, 20);
×
1189
  strings = backtrace_symbols(array, size); // Need -rdynamic gcc (linker) flag for this to work
×
1190

1191
  for (i = 0; i < size; i++) // skip useless functions
×
1192
    g_log << Logger::Error << strings[i] << endl;
×
1193

1194
  signal(SIGABRT, SIG_DFL);
×
1195
  abort(); // hopefully will give core
×
1196
}
×
1197
#endif
1198

1199
#ifdef COVERAGE
1200
static void sigTermHandler([[maybe_unused]] int signal)
1201
{
159✔
1202
  pdns::coverage::dumpCoverageData();
159✔
1203
  _exit(EXIT_SUCCESS);
159✔
1204
}
159✔
1205
#endif /* COVERAGE */
1206

1207
//! The main function of pdns, the pdns process
1208
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
1209
int main(int argc, char** argv)
1210
{
256✔
1211
  versionSetProduct(ProductAuthoritative);
256✔
1212
  reportAllTypes(); // init MOADNSParser
256✔
1213

1214
  g_programname = "pdns";
256✔
1215
  g_starttime = time(nullptr);
256✔
1216

1217
#if defined(__GLIBC__) && !defined(__UCLIBC__)
256✔
1218
  signal(SIGSEGV, tbhandler);
256✔
1219
  signal(SIGFPE, tbhandler);
256✔
1220
  signal(SIGABRT, tbhandler);
256✔
1221
  signal(SIGILL, tbhandler);
256✔
1222
#endif
256✔
1223

1224
  std::ios_base::sync_with_stdio(false);
256✔
1225

1226
  g_log.toConsole(Logger::Warning);
256✔
1227
  try {
256✔
1228
    declareArguments();
256✔
1229

1230
    ::arg().laxParse(argc, argv); // do a lax parse
256✔
1231

1232
    if (::arg().mustDo("version")) {
256✔
1233
      cout << getProductVersion();
97✔
1234
      cout << getBuildConfiguration();
97✔
1235
      return 0;
97✔
1236
    }
97✔
1237

1238
    if (::arg()["config-name"] != "")
159✔
1239
      g_programname += "-" + ::arg()["config-name"];
134✔
1240

1241
    g_log.setName(g_programname);
159✔
1242

1243
    string configname = ::arg()["config-dir"] + "/" + g_programname + ".conf";
159✔
1244
    cleanSlashes(configname);
159✔
1245

1246
    if (::arg()["config"] != "default" && !::arg().mustDo("no-config")) // "config" == print a configuration file
159!
1247
      ::arg().laxFile(configname.c_str());
132✔
1248

1249
    ::arg().laxParse(argc, argv); // reparse so the commandline still wins
159✔
1250
    if (!::arg()["logging-facility"].empty()) {
159!
UNCOV
1251
      int val = logFacilityToLOG(::arg().asNum("logging-facility"));
×
UNCOV
1252
      if (val >= 0)
×
UNCOV
1253
        g_log.setFacility(val);
×
UNCOV
1254
      else
×
UNCOV
1255
        g_log << Logger::Error << "Unknown logging facility " << ::arg().asNum("logging-facility") << endl;
×
UNCOV
1256
    }
×
1257

1258
    if (!::arg().isEmpty("domain-metadata-cache-ttl"))
159✔
1259
      ::arg().set("zone-metadata-cache-ttl") = ::arg()["domain-metadata-cache-ttl"];
1✔
1260

1261
    // this mirroring back is on purpose, so that config dumps reflect the actual setting on both names
1262
    ::arg().set("domain-metadata-cache-ttl") = ::arg()["zone-metadata-cache-ttl"];
159✔
1263

1264
    g_log.setLoglevel((Logger::Urgency)(::arg().asNum("loglevel")));
159✔
1265
    g_log.setPrefixed(::arg().mustDo("loglevel-show"));
159✔
1266
    g_log.disableSyslog(::arg().mustDo("disable-syslog"));
159✔
1267
    g_log.setTimestamps(::arg().mustDo("log-timestamp"));
159✔
1268
    g_log.toConsole((Logger::Urgency)(::arg().asNum("loglevel")));
159✔
1269

1270
    if (::arg().mustDo("help") || ::arg().mustDo("config")) {
159!
UNCOV
1271
      ::arg().set("daemon") = "no";
×
UNCOV
1272
      ::arg().set("guardian") = "no";
×
UNCOV
1273
    }
×
1274

1275
    if (::arg().mustDo("guardian") && !isGuarded(argv)) {
159!
UNCOV
1276
      if (::arg().mustDo("daemon")) {
×
UNCOV
1277
        g_log.toConsole(Logger::Critical);
×
UNCOV
1278
        daemonize();
×
UNCOV
1279
      }
×
UNCOV
1280
      guardian(argc, argv);
×
1281
      // never get here, guardian will reinvoke process
UNCOV
1282
      cerr << "Um, we did get here!" << endl;
×
UNCOV
1283
    }
×
1284

1285
#ifdef COVERAGE
159✔
1286
    if (!::arg().mustDo("guardian") && !::arg().mustDo("daemon")) {
159!
1287
      signal(SIGTERM, sigTermHandler);
159✔
1288
    }
159✔
1289
#endif
159✔
1290

1291
    // we really need to do work - either standalone or as an instance
1292

1293
#if defined(__GLIBC__) && !defined(__UCLIBC__)
159✔
1294
    if (!::arg().mustDo("traceback-handler")) {
159!
UNCOV
1295
      g_log << Logger::Warning << "Disabling traceback handler" << endl;
×
UNCOV
1296
      signal(SIGSEGV, SIG_DFL);
×
UNCOV
1297
      signal(SIGFPE, SIG_DFL);
×
UNCOV
1298
      signal(SIGABRT, SIG_DFL);
×
UNCOV
1299
      signal(SIGILL, SIG_DFL);
×
UNCOV
1300
    }
×
1301
#endif
159✔
1302

1303
#ifdef HAVE_LIBSODIUM
159✔
1304
    if (sodium_init() == -1) {
159!
UNCOV
1305
      cerr << "Unable to initialize sodium crypto library" << endl;
×
UNCOV
1306
      exit(99);
×
UNCOV
1307
    }
×
1308
#endif
159✔
1309

1310
    openssl_thread_setup();
159✔
1311
    openssl_seed();
159✔
1312

1313
#ifdef HAVE_LUA_RECORDS
159✔
1314
    MiniCurl::init();
159✔
1315
#endif /* HAVE_LUA_RECORDS */
159✔
1316

1317
    if (!::arg()["load-modules"].empty()) {
159!
UNCOV
1318
      vector<string> modules;
×
1319

UNCOV
1320
      stringtok(modules, ::arg()["load-modules"], ", ");
×
UNCOV
1321
      if (!UeberBackend::loadModules(modules, ::arg()["module-dir"])) {
×
UNCOV
1322
        exit(1);
×
UNCOV
1323
      }
×
UNCOV
1324
    }
×
1325

1326
    BackendMakers().launch(::arg()["launch"]); // vrooooom!
159✔
1327

1328
    if (!::arg().getCommands().empty()) {
159!
UNCOV
1329
      cerr << "Fatal: non-option";
×
UNCOV
1330
      if (::arg().getCommands().size() > 1) {
×
UNCOV
1331
        cerr << "s";
×
UNCOV
1332
      }
×
UNCOV
1333
      cerr << " (";
×
UNCOV
1334
      bool first = true;
×
UNCOV
1335
      for (const auto& c : ::arg().getCommands()) {
×
UNCOV
1336
        if (!first) {
×
UNCOV
1337
          cerr << ", ";
×
UNCOV
1338
        }
×
UNCOV
1339
        first = false;
×
UNCOV
1340
        cerr << c;
×
UNCOV
1341
      }
×
UNCOV
1342
      cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl;
×
UNCOV
1343
      exit(99);
×
UNCOV
1344
    }
×
1345

1346
    if (::arg().mustDo("help")) {
159!
UNCOV
1347
      cout << "syntax:" << endl
×
UNCOV
1348
           << endl;
×
UNCOV
1349
      cout << ::arg().helpstring(::arg()["help"]) << endl;
×
UNCOV
1350
      exit(0);
×
UNCOV
1351
    }
×
1352

1353
    if (::arg().mustDo("config")) {
159!
UNCOV
1354
      string config = ::arg()["config"];
×
UNCOV
1355
      if (config == "default") {
×
UNCOV
1356
        cout << ::arg().configstring(false, true);
×
UNCOV
1357
      }
×
UNCOV
1358
      else if (config == "diff") {
×
UNCOV
1359
        cout << ::arg().configstring(true, false);
×
UNCOV
1360
      }
×
UNCOV
1361
      else if (config == "check") {
×
UNCOV
1362
        try {
×
UNCOV
1363
          if (!::arg().mustDo("no-config"))
×
UNCOV
1364
            ::arg().file(configname.c_str());
×
UNCOV
1365
          ::arg().parse(argc, argv);
×
UNCOV
1366
          exit(0);
×
UNCOV
1367
        }
×
UNCOV
1368
        catch (const ArgException& A) {
×
UNCOV
1369
          cerr << "Fatal error: " << A.reason << endl;
×
UNCOV
1370
          exit(1);
×
UNCOV
1371
        }
×
UNCOV
1372
      }
×
UNCOV
1373
      else {
×
UNCOV
1374
        cout << ::arg().configstring(true, true);
×
UNCOV
1375
      }
×
UNCOV
1376
      exit(0);
×
UNCOV
1377
    }
×
1378

1379
    if (::arg().mustDo("list-modules")) {
159!
UNCOV
1380
      auto modules = BackendMakers().getModules();
×
UNCOV
1381
      cout << "Modules available:" << endl;
×
UNCOV
1382
      for (const auto& m : modules)
×
UNCOV
1383
        cout << m << endl;
×
1384

UNCOV
1385
      _exit(99);
×
UNCOV
1386
    }
×
1387

1388
    if (!::arg().asNum("local-port")) {
159!
UNCOV
1389
      g_log << Logger::Error << "Unable to launch, binding to no port or port 0 makes no sense" << endl;
×
UNCOV
1390
      exit(99); // this isn't going to fix itself either
×
UNCOV
1391
    }
×
1392
    if (!BackendMakers().numLauncheable()) {
159!
UNCOV
1393
      g_log << Logger::Error << "Unable to launch, no backends configured for querying" << endl;
×
UNCOV
1394
      exit(99); // this isn't going to fix itself either
×
UNCOV
1395
    }
×
1396
    if (::arg().mustDo("daemon")) {
159!
UNCOV
1397
      g_log.toConsole(Logger::None);
×
UNCOV
1398
      if (!isGuarded(argv))
×
UNCOV
1399
        daemonize();
×
UNCOV
1400
    }
×
1401

1402
    if (isGuarded(argv)) {
159!
UNCOV
1403
      g_log << Logger::Warning << "This is a guarded instance of pdns" << endl;
×
UNCOV
1404
      s_dynListener = std::make_unique<DynListener>(); // listens on stdin
×
UNCOV
1405
    }
×
1406
    else {
159✔
1407
      g_log << Logger::Warning << "This is a standalone pdns" << endl;
159✔
1408

1409
      if (::arg().mustDo("control-console"))
159!
UNCOV
1410
        s_dynListener = std::make_unique<DynListener>();
×
1411
      else
159✔
1412
        s_dynListener = std::make_unique<DynListener>(g_programname);
159✔
1413

1414
      writePid();
159✔
1415
    }
159✔
1416
    DynListener::registerFunc("SHOW", &DLShowHandler, "show a specific statistic or * to get a list", "<statistic>");
159✔
1417
    DynListener::registerFunc("RPING", &DLPingHandler, "ping instance");
159✔
1418
    DynListener::registerFunc("QUIT", &DLRQuitHandler, "quit daemon");
159✔
1419
    DynListener::registerFunc("UPTIME", &DLUptimeHandler, "get instance uptime");
159✔
1420
    DynListener::registerFunc("NOTIFY-HOST", &DLNotifyHostHandler, "notify host for specific zone", "<zone> <host>");
159✔
1421
    DynListener::registerFunc("NOTIFY", &DLNotifyHandler, "queue a notification", "<zone>");
159✔
1422
    DynListener::registerFunc("RELOAD", &DLReloadHandler, "reload all zones");
159✔
1423
    DynListener::registerFunc("REDISCOVER", &DLRediscoverHandler, "discover any new zones");
159✔
1424
    DynListener::registerFunc("VERSION", &DLVersionHandler, "get instance version");
159✔
1425
    DynListener::registerFunc("PURGE", &DLPurgeHandler, "purge entries from packet cache", "[<record>]");
159✔
1426
    DynListener::registerFunc("CCOUNTS", &DLCCHandler, "get cache statistics");
159✔
1427
    DynListener::registerFunc("QTYPES", &DLQTypesHandler, "get QType statistics");
159✔
1428
    DynListener::registerFunc("RESPSIZES", &DLRSizesHandler, "get histogram of response sizes");
159✔
1429
    DynListener::registerFunc("REMOTES", &DLRemotesHandler, "get top remotes");
159✔
1430
    DynListener::registerFunc("SET", &DLSettingsHandler, "set config variables", "<var> <value>");
159✔
1431
    DynListener::registerFunc("RETRIEVE", &DLNotifyRetrieveHandler, "retrieve secondary zone", "<zone> [<ip>]");
159✔
1432
    DynListener::registerFunc("CURRENT-CONFIG", &DLCurrentConfigHandler, "retrieve the current configuration", "[diff]");
159✔
1433
    DynListener::registerFunc("LIST-ZONES", &DLListZones, "show list of zones", "[primary|secondary|native|consumer|producer]");
159✔
1434
    DynListener::registerFunc("TOKEN-LOGIN", &DLTokenLogin, "Login to a PKCS#11 token", "<module> <slot> <pin>");
159✔
1435
    DynListener::registerFunc("XFR-QUEUE", &DLSuckRequests, "Get all requests for XFR in queue");
159✔
1436

1437
    if (!::arg()["tcp-control-address"].empty()) {
159!
UNCOV
1438
      DynListener* dlTCP = new DynListener(ComboAddress(::arg()["tcp-control-address"], ::arg().asNum("tcp-control-port")));
×
UNCOV
1439
      dlTCP->go();
×
UNCOV
1440
    }
×
1441

1442
    // reparse, with error checking
1443
    if (!::arg().mustDo("no-config"))
159✔
1444
      ::arg().file(configname.c_str());
132✔
1445
    ::arg().parse(argc, argv);
159✔
1446

1447
    if (::arg()["server-id"].empty()) {
159!
1448
      char tmp[128];
159✔
1449
      if (gethostname(tmp, sizeof(tmp) - 1) == 0) {
159!
1450
        ::arg().set("server-id") = tmp;
159✔
1451
      }
159✔
UNCOV
1452
      else {
×
UNCOV
1453
        g_log << Logger::Warning << "Unable to get the hostname, NSID and id.server values will be empty: " << stringerror() << endl;
×
UNCOV
1454
      }
×
1455
    }
159✔
1456

1457
    s_udpNameserver = std::make_shared<UDPNameserver>(); // this fails when we are not root, throws exception
159✔
1458
    s_udpReceivers.push_back(s_udpNameserver);
159✔
1459

1460
    size_t rthreads = ::arg().asNum("receiver-threads", 1);
159✔
1461
    if (rthreads > 1 && s_udpNameserver->canReusePort()) {
159!
UNCOV
1462
      s_udpReceivers.resize(rthreads);
×
1463

UNCOV
1464
      for (size_t idx = 1; idx < rthreads; idx++) {
×
UNCOV
1465
        try {
×
UNCOV
1466
          s_udpReceivers[idx] = std::make_shared<UDPNameserver>(true);
×
UNCOV
1467
        }
×
UNCOV
1468
        catch (const PDNSException& e) {
×
UNCOV
1469
          g_log << Logger::Error << "Unable to reuse port, falling back to original bind" << endl;
×
UNCOV
1470
          break;
×
UNCOV
1471
        }
×
UNCOV
1472
      }
×
UNCOV
1473
    }
×
1474

1475
    s_tcpNameserver = make_unique<TCPNameserver>();
159✔
1476
  }
159✔
1477
  catch (const ArgException& A) {
256✔
UNCOV
1478
    g_log << Logger::Error << "Fatal error: " << A.reason << endl;
×
UNCOV
1479
    exit(1);
×
UNCOV
1480
  }
×
1481
  catch (const std::exception& e) {
256✔
UNCOV
1482
    g_log << Logger::Error << "Fatal error: " << e.what() << endl;
×
UNCOV
1483
    exit(1);
×
UNCOV
1484
  }
×
1485

1486
  try {
159✔
1487
    declareStats();
159✔
1488
  }
159✔
1489
  catch (const PDNSException& PE) {
159✔
UNCOV
1490
    g_log << Logger::Error << "Exiting because: " << PE.reason << endl;
×
UNCOV
1491
    exit(1);
×
UNCOV
1492
  }
×
1493

1494
  try {
159✔
1495
    auto defaultCatalog = ::arg()["default-catalog-zone"];
159✔
1496
    if (!defaultCatalog.empty()) {
159✔
1497
      auto defCatalog = DNSName(defaultCatalog);
4✔
1498
    }
4✔
1499
  }
159✔
1500
  catch (const std::exception& e) {
159✔
UNCOV
1501
    g_log << Logger::Error << "Invalid value '" << ::arg()["default-catalog-zone"] << "' for default-catalog-zone: " << e.what() << endl;
×
UNCOV
1502
    exit(1);
×
UNCOV
1503
  }
×
1504
  S.blacklist("special-memory-usage");
159✔
1505

1506
  DLOG(g_log << Logger::Warning << "Verbose logging in effect" << endl);
159✔
1507

1508
  for (const string& line : getProductVersionLines()) {
477✔
1509
    g_log << Logger::Info << line << endl;
477✔
1510
  }
477✔
1511

1512
  try {
159✔
1513
    mainthread();
159✔
1514
  }
159✔
1515
  catch (const PDNSException& e) {
159✔
UNCOV
1516
    try {
×
UNCOV
1517
      if (!::arg().mustDo("daemon")) {
×
UNCOV
1518
        cerr << "Exiting because: " << e.reason << endl;
×
UNCOV
1519
      }
×
UNCOV
1520
    }
×
UNCOV
1521
    catch (const ArgException& A) {
×
UNCOV
1522
    }
×
UNCOV
1523
    g_log << Logger::Error << "Exiting because: " << e.reason << endl;
×
UNCOV
1524
  }
×
1525
  catch (const std::exception& e) {
159✔
UNCOV
1526
    try {
×
UNCOV
1527
      if (!::arg().mustDo("daemon")) {
×
UNCOV
1528
        cerr << "Exiting because of STL error: " << e.what() << endl;
×
UNCOV
1529
      }
×
UNCOV
1530
    }
×
UNCOV
1531
    catch (const ArgException& A) {
×
UNCOV
1532
    }
×
UNCOV
1533
    g_log << Logger::Error << "Exiting because of STL error: " << e.what() << endl;
×
UNCOV
1534
  }
×
1535
  catch (...) {
159✔
UNCOV
1536
    cerr << "Uncaught exception of unknown type - sorry" << endl;
×
UNCOV
1537
  }
×
1538

1539
  exit(1);
159✔
1540
}
159✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc