]>
Commit | Line | Data |
---|---|---|
12c86877 | 1 | /* |
12471842 PL |
2 | * This file is part of PowerDNS or dnsdist. |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
870a0fe4 AT |
22 | #ifdef HAVE_CONFIG_H |
23 | #include "config.h" | |
24 | #endif | |
8cb70f23 | 25 | #include <cstdio> |
dc6aa7f5 | 26 | #include <csignal> |
8cb70f23 CH |
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> | |
e903706d | 37 | #include <sys/resource.h> |
8cb70f23 CH |
38 | #include <sys/time.h> |
39 | #include <sys/wait.h> | |
dc6aa7f5 | 40 | #include <cerrno> |
8cb70f23 CH |
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" | |
460a6daa | 56 | #include "coverage.hh" |
8cb70f23 | 57 | #include "secpoll-auth.hh" |
f45a622c | 58 | #include "dynhandler.hh" |
82ee5aa5 | 59 | #include "dnsseckeeper.hh" |
519f5484 | 60 | #include "threadname.hh" |
2211dac9 | 61 | #include "misc.hh" |
20829585 | 62 | #include "query-local-address.hh" |
1886a4b3 | 63 | #include "trusted-notification-proxy.hh" |
8cb70f23 | 64 | #include "packetcache.hh" |
0a21c8ea | 65 | #include "packethandler.hh" |
8cb70f23 CH |
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" | |
fa8fd4d2 | 87 | |
8cb70f23 CH |
88 | #ifdef HAVE_LUA_RECORDS |
89 | #include "minicurl.hh" | |
90 | #endif /* HAVE_LUA_RECORDS */ | |
c2826d2e | 91 | |
c509c9fa | 92 | time_t g_starttime; |
8cb70f23 | 93 | |
c509c9fa | 94 | string g_programname = "pdns"; // used in packethandler.cc |
ad5f04f1 CH |
95 | |
96 | const char* funnytext = "*****************************************************************************\n" | |
bcd5ed15 FM |
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" | |
ad5f04f1 CH |
103 | " the pdns crew - pdns@powerdns.com\n" |
104 | "*****************************************************************************\n"; | |
b653a73d | 105 | |
357f6a75 | 106 | bool g_anyToTcp; |
fc41a1a6 | 107 | bool g_8bitDNS; |
8900e2e3 | 108 | #ifdef HAVE_LUA_RECORDS |
cb6bd1a9 | 109 | bool g_doLuaRecord; |
af68014f | 110 | int g_luaRecordExecLimit; |
a6897a16 CHB |
111 | time_t g_luaHealthChecksInterval{5}; |
112 | time_t g_luaHealthChecksExpireDelay{3600}; | |
a6be268f CHB |
113 | time_t g_luaConsistentHashesExpireDelay{86400}; |
114 | time_t g_luaConsistentHashesCleanupInterval{3600}; | |
8900e2e3 | 115 | #endif |
c113acc3 | 116 | #ifdef ENABLE_GSS_TSIG |
fd7bbdb7 | 117 | bool g_doGssTSIG; |
c113acc3 | 118 | #endif |
ad5f04f1 | 119 | typedef Distributor<DNSPacket, DNSPacket, PacketHandler> DNSDistributor; |
619e8acc | 120 | |
619e8acc | 121 | ArgvMap theArg; |
ad5f04f1 | 122 | StatBag S; //!< Statistics are gathered across PDNS via the StatBag class S |
bf269e28 RG |
123 | AuthPacketCache PC; //!< This is the main PacketCache, shared across all threads |
124 | AuthQueryCache QC; | |
1f0fb39a | 125 | AuthZoneCache g_zoneCache; |
c2826d2e | 126 | std::unique_ptr<DNSProxy> DP{nullptr}; |
a270fbcb | 127 | static std::unique_ptr<DynListener> s_dynListener{nullptr}; |
619e8acc | 128 | CommunicatorClass Communicator; |
7163e400 | 129 | static double avg_latency{0.0}, receive_latency{0.0}, cache_latency{0.0}, backend_latency{0.0}, send_latency{0.0}; |
6738003e | 130 | static unique_ptr<TCPNameserver> s_tcpNameserver{nullptr}; |
346872ff | 131 | static vector<DNSDistributor*> s_distributors; |
dd81fb3b | 132 | static shared_ptr<UDPNameserver> s_udpNameserver{nullptr}; |
fcab180d | 133 | static vector<std::shared_ptr<UDPNameserver>> s_udpReceivers; |
4172a5b2 PD |
134 | NetmaskGroup g_proxyProtocolACL; |
135 | size_t g_proxyProtocolMaximumSize; | |
619e8acc | 136 | |
ad5f04f1 | 137 | ArgvMap& arg() |
619e8acc BH |
138 | { |
139 | return theArg; | |
140 | } | |
141 | ||
8de02315 | 142 | static void declareArguments() |
619e8acc | 143 | { |
ad5f04f1 CH |
144 | ::arg().set("config-dir", "Location of configuration directory (pdns.conf)") = SYSCONFDIR; |
145 | ::arg().set("config-name", "Name of this virtual configuration - will rename the binary image") = ""; | |
146 | ::arg().set("socket-dir", string("Where the controlsocket will live, ") + LOCALSTATEDIR + "/pdns when unset and not chrooted" | |
9a5b0a54 | 147 | #ifdef HAVE_SYSTEMD |
ad5f04f1 CH |
148 | + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).") |
149 | = ""; | |
150 | auto runtimeDir = getenv("RUNTIME_DIRECTORY"); | |
151 | if (runtimeDir != nullptr) { | |
152 | ::arg().set("socket-dir") = runtimeDir; | |
153 | } | |
9a5b0a54 | 154 | #else |
ad5f04f1 CH |
155 | ) |
156 | = ""; | |
9a5b0a54 | 157 | #endif |
ad5f04f1 CH |
158 | ::arg().set("module-dir", "Default directory for modules") = PKGLIBDIR; |
159 | ::arg().set("chroot", "If set, chroot to this directory for more security") = ""; | |
160 | ::arg().set("logging-facility", "Log under a specific facility") = ""; | |
161 | ::arg().set("daemon", "Operate as a daemon") = "no"; | |
162 | ||
163 | ::arg().set("local-port", "The port on which we listen") = "53"; | |
164 | ::arg().setSwitch("dnsupdate", "Enable/Disable DNS update (RFC2136) support. Default is no.") = "no"; | |
165 | ::arg().setSwitch("write-pid", "Write a PID file") = "yes"; | |
166 | ::arg().set("allow-dnsupdate-from", "A global setting to allow DNS updates from these IP ranges.") = "127.0.0.0/8,::1"; | |
006dac60 | 167 | ::arg().setSwitch("dnsupdate-require-tsig", "Require TSIG secured DNS updates. Default is no.") = "no"; |
ad5f04f1 CH |
168 | ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets, and is mandatory then too.") = ""; |
169 | ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values") = "512"; | |
2dc0dd0c | 170 | ::arg().setSwitch("send-signed-notify", "Send TSIG secured NOTIFY if TSIG key is configured for a zone") = "yes"; |
ad5f04f1 | 171 | ::arg().set("allow-unsigned-notify", "Allow unsigned notifications for TSIG secured zones") = "yes"; // FIXME: change to 'no' later |
ad5f04f1 | 172 | ::arg().set("allow-unsigned-autoprimary", "Allow autoprimaries to create zones without TSIG signed NOTIFY") = "yes"; |
c02c999b | 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"; |
ad5f04f1 CH |
174 | ::arg().setSwitch("log-dns-details", "If PDNS should log DNS non-erroneous details") = "no"; |
175 | ::arg().setSwitch("log-dns-queries", "If PDNS should log all incoming DNS queries") = "no"; | |
176 | ::arg().set("local-address", "Local IP addresses to which we bind") = "0.0.0.0, ::"; | |
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"; | |
178 | ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options") = "no"; | |
179 | ::arg().setSwitch("reuseport", "Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket") = "no"; | |
180 | ::arg().set("query-local-address", "Source IP addresses for sending queries") = "0.0.0.0 ::"; | |
181 | ::arg().set("overload-queue-length", "Maximum queuelength moving to packetcache only") = "0"; | |
182 | ::arg().set("max-queue-length", "Maximum queuelength before considering situation lost") = "5000"; | |
183 | ||
c02c999b | 184 | ::arg().set("retrieval-threads", "Number of AXFR-retrieval threads for secondary operation") = "2"; |
ad5f04f1 CH |
185 | ::arg().setSwitch("api", "Enable/disable the REST API (including HTTP listener)") = "no"; |
186 | ::arg().set("api-key", "Static pre-shared authentication key for access to the REST API") = ""; | |
187 | ::arg().setSwitch("default-api-rectify", "Default API-RECTIFY value for zones") = "yes"; | |
188 | ::arg().setSwitch("dname-processing", "If we should support DNAME records") = "no"; | |
189 | ||
190 | ::arg().setCmd("help", "Provide a helpful message"); | |
191 | ::arg().setCmd("version", "Output version and compilation date"); | |
192 | ::arg().setCmd("config", "Provide configuration file on standard output"); | |
193 | ::arg().setCmd("list-modules", "Lists all modules available"); | |
194 | ::arg().setCmd("no-config", "Don't parse configuration file"); | |
195 | ||
196 | ::arg().set("version-string", "PowerDNS version in packets - full, anonymous, powerdns or custom") = "full"; | |
197 | ::arg().set("control-console", "Debugging switch - don't use") = "no"; // but I know you will! | |
198 | ::arg().set("loglevel", "Amount of logging. Higher is more. Do not set below 3") = "4"; | |
46eec001 | 199 | ::arg().setSwitch("loglevel-show", "Include log level indicator in log output") = "no"; |
cd77295b | 200 | ::arg().set("disable-syslog", "Disable logging to syslog, useful when running inside a supervisor that logs stderr") = "no"; |
ad5f04f1 CH |
201 | ::arg().set("log-timestamp", "Print timestamps in log lines") = "yes"; |
202 | ::arg().set("distributor-threads", "Default number of Distributor (backend) threads to start") = "3"; | |
203 | ::arg().set("signing-threads", "Default number of signer threads to start") = "3"; | |
3af419da | 204 | ::arg().setSwitch("workaround-11804", "Workaround for issue 11804: send single RR per AXFR chunk") = "no"; |
ad5f04f1 CH |
205 | ::arg().set("receiver-threads", "Default number of receiver threads to start") = "1"; |
206 | ::arg().set("queue-limit", "Maximum number of milliseconds to queue a query") = "1500"; | |
207 | ::arg().set("resolver", "Use this resolver for ALIAS and the internal stub resolver") = "no"; | |
208 | ::arg().set("udp-truncation-threshold", "Maximum UDP response size before we truncate") = "1232"; | |
209 | ||
210 | ::arg().set("config-name", "Name of this virtual configuration - will rename the binary image") = ""; | |
211 | ||
212 | ::arg().set("load-modules", "Load this module - supply absolute or relative path") = ""; | |
213 | ::arg().set("launch", "Which backends to launch and order to query them in") = ""; | |
214 | ::arg().setSwitch("disable-axfr", "Disable zonetransfers but do allow TCP queries") = "no"; | |
215 | ::arg().set("allow-axfr-ips", "Allow zonetransfers only to these subnets") = "127.0.0.0/8,::1"; | |
216 | ::arg().set("only-notify", "Only send AXFR NOTIFY to these IP addresses or netmasks") = "0.0.0.0/0,::/0"; | |
2dc0dd0c | 217 | ::arg().set("also-notify", "When notifying a zone, also notify these nameservers") = ""; |
ad5f04f1 | 218 | ::arg().set("allow-notify-from", "Allow AXFR NOTIFY from these IP ranges. If empty, drop all incoming notifies.") = "0.0.0.0/0,::/0"; |
ad5f04f1 | 219 | ::arg().set("xfr-cycle-interval", "Schedule primary/secondary SOA freshness checks once every .. seconds") = "60"; |
d804bbeb | 220 | ::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"; |
ad5f04f1 CH |
221 | |
222 | ::arg().set("tcp-control-address", "If set, PowerDNS can be controlled over TCP on this address") = ""; | |
223 | ::arg().set("tcp-control-port", "If set, PowerDNS can be controlled over TCP on this address") = "53000"; | |
224 | ::arg().set("tcp-control-secret", "If set, PowerDNS can be controlled over TCP after passing this secret") = ""; | |
225 | ::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"; | |
226 | ||
ad5f04f1 | 227 | ::arg().setSwitch("secondary", "Act as a secondary") = "no"; |
ad5f04f1 | 228 | ::arg().setSwitch("primary", "Act as a primary") = "no"; |
734646dd | 229 | ::arg().setSwitch("autosecondary", "Act as an autosecondary") = "no"; |
ad5f04f1 CH |
230 | ::arg().setSwitch("disable-axfr-rectify", "Disable the rectify step during an outgoing AXFR. Only required for regression testing.") = "no"; |
231 | ::arg().setSwitch("guardian", "Run within a guardian process") = "no"; | |
232 | ::arg().setSwitch("prevent-self-notification", "Don't send notifications to what we think is ourself") = "yes"; | |
233 | ::arg().setSwitch("any-to-tcp", "Answer ANY queries with tc=1, shunting to TCP") = "yes"; | |
234 | ::arg().setSwitch("edns-subnet-processing", "If we should act on EDNS Subnet options") = "no"; | |
7bd9b307 | 235 | ::arg().set("delay-notifications", "Configure a delay to send out notifications, no delay by default") = "0"; |
ad5f04f1 CH |
236 | |
237 | ::arg().set("edns-cookie-secret", "When set, set a server cookie when responding to a query with a Client cookie (in hex)") = ""; | |
238 | ||
239 | ::arg().setSwitch("webserver", "Start a webserver for monitoring (api=yes also enables the HTTP listener)") = "no"; | |
240 | ::arg().setSwitch("webserver-print-arguments", "If the webserver should print arguments") = "no"; | |
241 | ::arg().set("webserver-address", "IP Address of webserver/API to listen on") = "127.0.0.1"; | |
242 | ::arg().set("webserver-port", "Port of webserver/API to listen on") = "8081"; | |
243 | ::arg().set("webserver-password", "Password required for accessing the webserver") = ""; | |
244 | ::arg().set("webserver-allow-from", "Webserver/API access is only allowed from these subnets") = "127.0.0.1,::1"; | |
8ca656a8 | 245 | ::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal"; |
ad5f04f1 CH |
246 | ::arg().set("webserver-max-bodysize", "Webserver/API maximum request/response body size in megabytes") = "2"; |
247 | ::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"; | |
dea466f3 | 248 | |
ad5f04f1 | 249 | ::arg().setSwitch("query-logging", "Hint backends that queries should be logged") = "no"; |
952d3fcb | 250 | |
ad5f04f1 CH |
251 | ::arg().set("carbon-namespace", "If set overwrites the first part of the carbon string") = "pdns"; |
252 | ::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats") = ""; | |
253 | ::arg().set("carbon-instance", "If set overwrites the instance name default") = "auth"; | |
254 | ::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server IP address") = ""; | |
255 | ::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates") = "30"; | |
952d3fcb | 256 | |
ad5f04f1 CH |
257 | ::arg().set("cache-ttl", "Seconds to store packets in the PacketCache") = "20"; |
258 | ::arg().set("negquery-cache-ttl", "Seconds to store negative query results in the QueryCache") = "60"; | |
259 | ::arg().set("query-cache-ttl", "Seconds to store query results in the QueryCache") = "20"; | |
30285d45 | 260 | ::arg().set("zone-cache-refresh-interval", "Seconds to cache list of known zones") = "300"; |
ad5f04f1 CH |
261 | ::arg().set("server-id", "Returned when queried for 'id.server' TXT or NSID, defaults to hostname - disabled or custom") = ""; |
262 | ::arg().set("default-soa-content", "Default SOA content") = "a.misconfigured.dns.server.invalid hostmaster.@ 0 10800 3600 604800 3600"; | |
263 | ::arg().set("default-soa-edit", "Default SOA-EDIT value") = ""; | |
264 | ::arg().set("default-soa-edit-signed", "Default SOA-EDIT value for signed zones") = ""; | |
265 | ::arg().set("dnssec-key-cache-ttl", "Seconds to cache DNSSEC keys from the database") = "30"; | |
58753225 | 266 | ::arg().set("domain-metadata-cache-ttl", "Seconds to cache zone metadata from the database") = ""; |
b810842e | 267 | ::arg().set("zone-metadata-cache-ttl", "Seconds to cache zone metadata from the database") = "60"; |
dea466f3 | 268 | |
ad5f04f1 | 269 | ::arg().set("trusted-notification-proxy", "IP address of incoming notification proxy") = ""; |
ad5f04f1 | 270 | ::arg().set("secondary-do-renotify", "If this secondary should send out notifications after receiving zone transfers from a primary") = "no"; |
c02c999b | 271 | ::arg().set("forward-notify", "IP addresses to forward received notifications to regardless of primary or secondary settings") = ""; |
ad5f04f1 CH |
272 | |
273 | ::arg().set("default-ttl", "Seconds a result is valid if not set otherwise") = "3600"; | |
274 | ::arg().set("max-tcp-connections", "Maximum number of TCP connections") = "20"; | |
275 | ::arg().set("max-tcp-connections-per-client", "Maximum number of simultaneous TCP connections per client") = "0"; | |
276 | ::arg().set("max-tcp-transactions-per-conn", "Maximum number of subsequent queries per TCP connection") = "0"; | |
277 | ::arg().set("max-tcp-connection-duration", "Maximum time in seconds that a TCP DNS connection is allowed to stay open.") = "0"; | |
278 | ::arg().set("tcp-idle-timeout", "Maximum time in seconds that a TCP DNS connection is allowed to stay open while being idle") = "5"; | |
279 | ||
280 | ::arg().setSwitch("no-shuffle", "Set this to prevent random shuffling of answers - for regression testing") = "off"; | |
281 | ||
282 | ::arg().set("setuid", "If set, change user id to this uid for more security") = ""; | |
283 | ::arg().set("setgid", "If set, change group id to this gid for more security") = ""; | |
284 | ||
285 | ::arg().set("max-cache-entries", "Maximum number of entries in the query cache") = "1000000"; | |
286 | ::arg().set("max-packet-cache-entries", "Maximum number of entries in the packet cache") = "1000000"; | |
287 | ::arg().set("max-signature-cache-entries", "Maximum number of signatures cache entries") = ""; | |
288 | ::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone") = "100000"; | |
289 | ::arg().set("entropy-source", "If set, read entropy from this file") = "/dev/urandom"; | |
290 | ||
291 | ::arg().set("lua-prequery-script", "Lua script with prequery handler (DO NOT USE)") = ""; | |
292 | ::arg().set("lua-dnsupdate-policy-script", "Lua script with DNS update policy handler") = ""; | |
293 | ||
294 | ::arg().setSwitch("traceback-handler", "Enable the traceback handler (Linux only)") = "yes"; | |
295 | ::arg().setSwitch("direct-dnskey", "Fetch DNSKEY, CDS and CDNSKEY RRs from backend during DNSKEY or CDS/CDNSKEY synthesis") = "no"; | |
296 | ::arg().set("default-ksk-algorithm", "Default KSK algorithm") = "ecdsa256"; | |
297 | ::arg().set("default-ksk-size", "Default KSK size (0 means default)") = "0"; | |
298 | ::arg().set("default-zsk-algorithm", "Default ZSK algorithm") = ""; | |
299 | ::arg().set("default-zsk-size", "Default ZSK size (0 means default)") = "0"; | |
5a5d565f | 300 | ::arg().set("max-nsec3-iterations", "Limit the number of NSEC3 hash iterations") = "100"; |
ad5f04f1 CH |
301 | ::arg().set("default-publish-cdnskey", "Default value for PUBLISH-CDNSKEY") = ""; |
302 | ::arg().set("default-publish-cds", "Default value for PUBLISH-CDS") = ""; | |
0a862860 | 303 | |
ad5f04f1 | 304 | ::arg().set("include-dir", "Include *.conf files from this directory"); |
2dc0dd0c | 305 | ::arg().set("security-poll-suffix", "Zone name from which to query security update notifications") = "secpoll.powerdns.com."; |
7f6f4d4f | 306 | |
ad5f04f1 | 307 | ::arg().setSwitch("expand-alias", "Expand ALIAS records") = "no"; |
833b07fe | 308 | ::arg().set("outgoing-axfr-expand-alias", "Expand ALIAS records during outgoing AXFR") = "no"; |
ad5f04f1 | 309 | ::arg().setSwitch("8bit-dns", "Allow 8bit dns queries") = "no"; |
af68014f | 310 | #ifdef HAVE_LUA_RECORDS |
ad5f04f1 CH |
311 | ::arg().setSwitch("enable-lua-records", "Process LUA records for all zones (metadata overrides this)") = "no"; |
312 | ::arg().set("lua-records-exec-limit", "LUA records scripts execution limit (instructions count). Values <= 0 mean no limit") = "1000"; | |
313 | ::arg().set("lua-health-checks-expire-delay", "Stops doing health checks after the record hasn't been used for that delay (in seconds)") = "3600"; | |
314 | ::arg().set("lua-health-checks-interval", "LUA records health checks monitoring interval in seconds") = "5"; | |
a6be268f CHB |
315 | ::arg().set("lua-consistent-hashes-cleanup-interval", "Pre-computed hashes cleanup interval (in seconds)") = "3600"; |
316 | ::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"; | |
af68014f | 317 | #endif |
d525b58b | 318 | ::arg().setSwitch("axfr-lower-serial", "Also AXFR a zone from a primary with a lower serial") = "no"; |
db8f9152 | 319 | |
ad5f04f1 CH |
320 | ::arg().set("lua-axfr-script", "Script to be used to edit incoming AXFRs") = ""; |
321 | ::arg().set("xfr-max-received-mbytes", "Maximum number of megabytes received from an incoming XFR") = "100"; | |
322 | ::arg().set("axfr-fetch-timeout", "Maximum time in seconds for inbound AXFR to start or be idle after starting") = "10"; | |
940d7811 | 323 | |
ad5f04f1 | 324 | ::arg().set("tcp-fast-open", "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size") = "0"; |
e97cb679 | 325 | |
ad5f04f1 CH |
326 | ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file") = "0"; |
327 | ::arg().set("max-include-depth", "Maximum number of nested $INCLUDE directives while processing a zone file") = "20"; | |
328 | ::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"; | |
329 | ::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"; | |
ba3d53d1 | 330 | |
2dc0dd0c | 331 | ::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"; |
bd8df5bc | 332 | |
ad5f04f1 | 333 | ::arg().set("rng", "Specify the random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.") = "auto"; |
c8734ecd PD |
334 | |
335 | ::arg().set("default-catalog-zone", "Catalog zone to assign newly created primary zones (via the API) to") = ""; | |
7bd9b307 | 336 | |
c113acc3 | 337 | #ifdef ENABLE_GSS_TSIG |
fd7bbdb7 | 338 | ::arg().setSwitch("enable-gss-tsig", "Enable GSS TSIG processing") = "no"; |
c113acc3 | 339 | #endif |
8864bdf6 | 340 | ::arg().setDefaults(); |
619e8acc BH |
341 | } |
342 | ||
ad5f04f1 | 343 | static time_t s_start = time(nullptr); |
d73de874 | 344 | static uint64_t uptimeOfProcess(const std::string& /* str */) |
e903706d | 345 | { |
4646277d | 346 | return time(nullptr) - s_start; |
e903706d | 347 | } |
348 | ||
349 | static uint64_t getSysUserTimeMsec(const std::string& str) | |
350 | { | |
351 | struct rusage ru; | |
352 | getrusage(RUSAGE_SELF, &ru); | |
353 | ||
ad5f04f1 CH |
354 | if (str == "sys-msec") { |
355 | return (ru.ru_stime.tv_sec * 1000ULL + ru.ru_stime.tv_usec / 1000); | |
a2746484 | 356 | } |
e903706d | 357 | else |
ad5f04f1 | 358 | return (ru.ru_utime.tv_sec * 1000ULL + ru.ru_utime.tv_usec / 1000); |
e903706d | 359 | } |
360 | ||
d73de874 | 361 | static uint64_t getTCPConnectionCount(const std::string& /* str */) |
d322f931 | 362 | { |
6738003e | 363 | return s_tcpNameserver->numTCPConnections(); |
d322f931 PD |
364 | } |
365 | ||
d73de874 | 366 | static uint64_t getQCount(const std::string& /* str */) |
ad5f04f1 CH |
367 | try { |
368 | int totcount = 0; | |
346872ff | 369 | for (const auto& d : s_distributors) { |
ad5f04f1 | 370 | if (!d) |
ba98c6d7 | 371 | continue; |
ad5f04f1 | 372 | totcount += d->getQueueSize(); // this does locking and other things, so don't get smart |
1a46890f | 373 | } |
374 | return totcount; | |
375 | } | |
ad5f04f1 CH |
376 | catch (std::exception& e) { |
377 | g_log << Logger::Error << "Had error retrieving queue sizes: " << e.what() << endl; | |
2bf07cf4 | 378 | return 0; |
379 | } | |
ad5f04f1 CH |
380 | catch (PDNSException& e) { |
381 | g_log << Logger::Error << "Had error retrieving queue sizes: " << e.reason << endl; | |
2bf07cf4 | 382 | return 0; |
383 | } | |
1a46890f | 384 | |
d73de874 | 385 | static uint64_t getLatency(const std::string& /* str */) |
1a46890f | 386 | { |
6fd633be | 387 | return round(avg_latency); |
1a46890f | 388 | } |
389 | ||
d73de874 | 390 | static uint64_t getReceiveLatency(const std::string& /* str */) |
386c5b13 KM |
391 | { |
392 | return round(receive_latency); | |
393 | } | |
394 | ||
d73de874 | 395 | static uint64_t getCacheLatency(const std::string& /* str */) |
386c5b13 KM |
396 | { |
397 | return round(cache_latency); | |
398 | } | |
399 | ||
d73de874 | 400 | static uint64_t getBackendLatency(const std::string& /* str */) |
386c5b13 KM |
401 | { |
402 | return round(backend_latency); | |
403 | } | |
404 | ||
d73de874 | 405 | static uint64_t getSendLatency(const std::string& /* str */) |
386c5b13 KM |
406 | { |
407 | return round(send_latency); | |
408 | } | |
409 | ||
8de02315 | 410 | static void declareStats() |
619e8acc | 411 | { |
ad5f04f1 CH |
412 | S.declare("udp-queries", "Number of UDP queries received"); |
413 | S.declare("udp-do-queries", "Number of UDP queries received with DO bit"); | |
78f1d7b5 | 414 | S.declare("udp-cookie-queries", "Number of UDP queries received with the COOKIE EDNS option"); |
ad5f04f1 CH |
415 | S.declare("udp-answers", "Number of answers sent out over UDP"); |
416 | S.declare("udp-answers-bytes", "Total size of answers sent out over UDP"); | |
417 | S.declare("udp4-answers-bytes", "Total size of answers sent out over UDPv4"); | |
418 | S.declare("udp6-answers-bytes", "Total size of answers sent out over UDPv6"); | |
bd852e59 | 419 | |
ad5f04f1 CH |
420 | S.declare("udp4-answers", "Number of IPv4 answers sent out over UDP"); |
421 | S.declare("udp4-queries", "Number of IPv4 UDP queries received"); | |
422 | S.declare("udp6-answers", "Number of IPv6 answers sent out over UDP"); | |
423 | S.declare("udp6-queries", "Number of IPv6 UDP queries received"); | |
424 | S.declare("overload-drops", "Queries dropped because backends overloaded"); | |
bd852e59 | 425 | |
bb6e54fe | 426 | S.declare("rd-queries", "Number of recursion desired questions"); |
427 | S.declare("recursion-unanswered", "Number of packets unanswered by configured recursor"); | |
ad5f04f1 CH |
428 | S.declare("recursing-answers", "Number of recursive answers sent out"); |
429 | S.declare("recursing-questions", "Number of questions sent to recursor"); | |
430 | S.declare("corrupt-packets", "Number of corrupt packets received"); | |
c681ff64 | 431 | S.declare("signatures", "Number of DNSSEC signatures made"); |
ad5f04f1 CH |
432 | S.declare("tcp-queries", "Number of TCP queries received"); |
433 | S.declare("tcp-cookie-queries", "Number of TCP queries received with the COOKIE option"); | |
434 | S.declare("tcp-answers", "Number of answers sent out over TCP"); | |
435 | S.declare("tcp-answers-bytes", "Total size of answers sent out over TCP"); | |
436 | S.declare("tcp4-answers-bytes", "Total size of answers sent out over TCPv4"); | |
437 | S.declare("tcp6-answers-bytes", "Total size of answers sent out over TCPv6"); | |
50dbdbeb | 438 | |
ad5f04f1 CH |
439 | S.declare("tcp4-queries", "Number of IPv4 TCP queries received"); |
440 | S.declare("tcp4-answers", "Number of IPv4 answers sent out over TCP"); | |
d322f931 | 441 | |
ad5f04f1 CH |
442 | S.declare("tcp6-queries", "Number of IPv6 TCP queries received"); |
443 | S.declare("tcp6-answers", "Number of IPv6 answers sent out over TCP"); | |
619e8acc | 444 | |
ad5f04f1 CH |
445 | S.declare("open-tcp-connections", "Number of currently open TCP connections", getTCPConnectionCount, StatType::gauge); |
446 | ||
447 | S.declare("qsize-q", "Number of questions waiting for database attention", getQCount, StatType::gauge); | |
619e8acc | 448 | |
71f758e0 KM |
449 | S.declare("dnsupdate-queries", "DNS update packets received."); |
450 | S.declare("dnsupdate-answers", "DNS update packets successfully answered."); | |
451 | S.declare("dnsupdate-refused", "DNS update packets that are refused."); | |
452 | S.declare("dnsupdate-changes", "DNS update changes to records in total."); | |
619e8acc | 453 | |
93aecccc RK |
454 | S.declare("incoming-notifications", "NOTIFY packets received."); |
455 | ||
dec69610 MTH |
456 | S.declare("uptime", "Uptime of process in seconds", uptimeOfProcess, StatType::counter); |
457 | S.declare("real-memory-usage", "Actual unique use of memory in bytes (approx)", getRealMemoryUsage, StatType::gauge); | |
458 | S.declare("special-memory-usage", "Actual unique use of memory in bytes (approx)", getSpecialMemoryUsage, StatType::gauge); | |
459 | S.declare("fd-usage", "Number of open filedescriptors", getOpenFileDescriptors, StatType::gauge); | |
f45a622c | 460 | #ifdef __linux__ |
dec69610 MTH |
461 | S.declare("udp-recvbuf-errors", "UDP 'recvbuf' errors", udpErrorStats, StatType::counter); |
462 | S.declare("udp-sndbuf-errors", "UDP 'sndbuf' errors", udpErrorStats, StatType::counter); | |
463 | S.declare("udp-noport-errors", "UDP 'noport' errors", udpErrorStats, StatType::counter); | |
464 | S.declare("udp-in-errors", "UDP 'in' errors", udpErrorStats, StatType::counter); | |
c0f5b6e8 | 465 | S.declare("udp-in-csum-errors", "UDP 'in checksum' errors", udpErrorStats, StatType::counter); |
db102a10 RG |
466 | S.declare("udp6-in-errors", "UDP 'in' errors over IPv6", udp6ErrorStats, StatType::counter); |
467 | S.declare("udp6-recvbuf-errors", "UDP 'recvbuf' errors over IPv6", udp6ErrorStats, StatType::counter); | |
468 | S.declare("udp6-sndbuf-errors", "UDP 'sndbuf' errors over IPv6", udp6ErrorStats, StatType::counter); | |
469 | S.declare("udp6-noport-errors", "UDP 'noport' errors over IPv6", udp6ErrorStats, StatType::counter); | |
470 | S.declare("udp6-in-csum-errors", "UDP 'in checksum' errors over IPv6", udp6ErrorStats, StatType::counter); | |
f45a622c | 471 | #endif |
472 | ||
dec69610 MTH |
473 | S.declare("sys-msec", "Number of msec spent in system time", getSysUserTimeMsec, StatType::counter); |
474 | S.declare("user-msec", "Number of msec spent in user time", getSysUserTimeMsec, StatType::counter); | |
149a72d8 RG |
475 | |
476 | #ifdef __linux__ | |
dec69610 MTH |
477 | S.declare("cpu-iowait", "Time spent waiting for I/O to complete by the whole system, in units of USER_HZ", getCPUIOWait, StatType::counter); |
478 | 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); | |
149a72d8 RG |
479 | #endif |
480 | ||
dec69610 MTH |
481 | S.declare("meta-cache-size", "Number of entries in the metadata cache", DNSSECKeeper::dbdnssecCacheSizes, StatType::gauge); |
482 | S.declare("key-cache-size", "Number of entries in the key cache", DNSSECKeeper::dbdnssecCacheSizes, StatType::gauge); | |
483 | S.declare("signature-cache-size", "Number of entries in the signature cache", signatureCacheSize, StatType::gauge); | |
e903706d | 484 | |
ad5f04f1 CH |
485 | S.declare("nxdomain-packets", "Number of times an NXDOMAIN packet was sent out"); |
486 | S.declare("noerror-packets", "Number of times a NOERROR packet was sent out"); | |
487 | S.declare("servfail-packets", "Number of times a server-failed packet was sent out"); | |
2dc0dd0c | 488 | S.declare("unauth-packets", "Number of times a zone we are not auth for was queried"); |
ad5f04f1 | 489 | S.declare("latency", "Average number of microseconds needed to answer a question", getLatency, StatType::gauge); |
386c5b13 KM |
490 | S.declare("receive-latency", "Average number of microseconds needed to receive a query", getReceiveLatency, StatType::gauge); |
491 | S.declare("cache-latency", "Average number of microseconds needed for a packet cache lookup", getCacheLatency, StatType::gauge); | |
492 | S.declare("backend-latency", "Average number of microseconds needed for a backend lookup", getBackendLatency, StatType::gauge); | |
493 | S.declare("send-latency", "Average number of microseconds needed to send the answer", getSendLatency, StatType::gauge); | |
ad5f04f1 | 494 | S.declare("timedout-packets", "Number of packets which weren't answered within timeout set"); |
33b222ed | 495 | S.declare("security-status", "Security status based on regular polling", StatType::gauge); |
2dc0dd0c KM |
496 | S.declare( |
497 | "xfr-queue", "Size of the queue of zones to be XFRd", [](const string&) { return Communicator.getSuckRequestsWaiting(); }, StatType::gauge); | |
ad5f04f1 | 498 | S.declareDNSNameQTypeRing("queries", "UDP Queries Received"); |
e746a2f6 | 499 | S.declareDNSNameQTypeRing("nxdomain-queries", "Queries for nonexistent records within existent zones"); |
ad5f04f1 CH |
500 | S.declareDNSNameQTypeRing("noerror-queries", "Queries for existing records, but for type we don't have"); |
501 | S.declareDNSNameQTypeRing("servfail-queries", "Queries that could not be answered due to backend errors"); | |
2dc0dd0c | 502 | S.declareDNSNameQTypeRing("unauth-queries", "Queries for zones that we are not authoritative for"); |
ad5f04f1 CH |
503 | S.declareRing("logmessages", "Log Messages"); |
504 | S.declareComboRing("remotes", "Remote server IP addresses"); | |
2dc0dd0c | 505 | S.declareComboRing("remotes-unauth", "Remote hosts querying zones for which we are not auth"); |
ad5f04f1 | 506 | S.declareComboRing("remotes-corrupt", "Remote hosts sending corrupt packets"); |
619e8acc BH |
507 | } |
508 | ||
8de02315 | 509 | static int isGuarded(char** argv) |
619e8acc | 510 | { |
ad5f04f1 | 511 | char* p = strstr(argv[0], "-instance"); |
619e8acc BH |
512 | |
513 | return !!p; | |
514 | } | |
515 | ||
386c5b13 | 516 | static void sendout(std::unique_ptr<DNSPacket>& a, int start) |
619e8acc | 517 | { |
ad5f04f1 | 518 | if (!a) |
619e8acc | 519 | return; |
bd852e59 | 520 | |
b226e8fa | 521 | try { |
386c5b13 KM |
522 | int diff = a->d_dt.udiffNoReset(); |
523 | backend_latency = 0.999 * backend_latency + 0.001 * std::max(diff - start, 0); | |
524 | start = diff; | |
525 | ||
dd81fb3b | 526 | s_udpNameserver->send(*a); |
b226e8fa | 527 | |
386c5b13 KM |
528 | diff = a->d_dt.udiff(); |
529 | send_latency = 0.999 * send_latency + 0.001 * std::max(diff - start, 0); | |
530 | ||
531 | avg_latency = 0.999 * avg_latency + 0.001 * std::max(diff, 0); | |
b226e8fa RG |
532 | } |
533 | catch (const std::exception& e) { | |
ad5f04f1 | 534 | g_log << Logger::Error << "Caught unhandled exception while sending a response: " << e.what() << endl; |
b226e8fa | 535 | } |
619e8acc BH |
536 | } |
537 | ||
abc1d928 | 538 | //! The qthread receives questions over the internet via the Nameserver class, and hands them to the Distributor for further processing |
c2826d2e | 539 | static void qthread(unsigned int num) |
ad5f04f1 | 540 | try { |
519f5484 | 541 | setThreadName("pdns/receiver"); |
09b12b5d | 542 | |
346872ff CH |
543 | s_distributors[num] = DNSDistributor::Create(::arg().asNum("distributor-threads", 1)); |
544 | DNSDistributor* distributor = s_distributors[num]; // the big dispatcher! | |
27c0050c RG |
545 | DNSPacket question(true); |
546 | DNSPacket cached(false); | |
619e8acc | 547 | |
ad5f04f1 CH |
548 | AtomicCounter& numreceived = *S.getPointer("udp-queries"); |
549 | AtomicCounter& numreceiveddo = *S.getPointer("udp-do-queries"); | |
550 | AtomicCounter& numreceivedcookie = *S.getPointer("udp-cookie-queries"); | |
bd852e59 | 551 | |
ad5f04f1 | 552 | AtomicCounter& numreceived4 = *S.getPointer("udp4-queries"); |
bd852e59 | 553 | |
ad5f04f1 CH |
554 | AtomicCounter& numreceived6 = *S.getPointer("udp6-queries"); |
555 | AtomicCounter& overloadDrops = *S.getPointer("overload-drops"); | |
90b07284 | 556 | |
386c5b13 | 557 | int diff, start; |
21a303f3 | 558 | bool logDNSQueries = ::arg().mustDo("log-dns-queries"); |
12f224ab | 559 | shared_ptr<UDPNameserver> NS; |
2c001eb6 | 560 | std::string buffer; |
208abc4c | 561 | ComboAddress accountremote; |
4ee30c66 MZ |
562 | |
563 | // If we have SO_REUSEPORT then create a new port for all receiver threads | |
564 | // other than the first one. | |
dd81fb3b | 565 | if (s_udpNameserver->canReusePort()) { |
fcab180d | 566 | NS = s_udpReceivers[num]; |
12f224ab | 567 | if (NS == nullptr) { |
dd81fb3b | 568 | NS = s_udpNameserver; |
ed3afdfc | 569 | } |
ad5f04f1 CH |
570 | } |
571 | else { | |
dd81fb3b | 572 | NS = s_udpNameserver; |
3a56adcc | 573 | } |
4ee30c66 | 574 | |
ad5f04f1 | 575 | for (;;) { |
b226e8fa RG |
576 | try { |
577 | if (g_proxyProtocolACL.empty()) { | |
578 | buffer.resize(DNSPacket::s_udpTruncationThreshold); | |
579 | } | |
580 | else { | |
581 | buffer.resize(DNSPacket::s_udpTruncationThreshold + g_proxyProtocolMaximumSize); | |
582 | } | |
4172a5b2 | 583 | |
ad5f04f1 CH |
584 | if (!NS->receive(question, buffer)) { // receive a packet inline |
585 | continue; // packet was broken, try again | |
b226e8fa RG |
586 | } |
587 | ||
386c5b13 KM |
588 | diff = question.d_dt.udiffNoReset(); |
589 | receive_latency = 0.999 * receive_latency + 0.001 * std::max(diff, 0); | |
590 | ||
b226e8fa | 591 | numreceived++; |
fe172793 | 592 | |
208abc4c KM |
593 | accountremote = question.d_remote; |
594 | if (question.d_inner_remote) | |
595 | accountremote = *question.d_inner_remote; | |
596 | ||
597 | if (accountremote.sin4.sin_family == AF_INET) | |
b226e8fa RG |
598 | numreceived4++; |
599 | else | |
600 | numreceived6++; | |
8a63d3ce | 601 | |
ad5f04f1 | 602 | if (question.d_dnssecOk) |
b226e8fa | 603 | numreceiveddo++; |
619e8acc | 604 | |
ad5f04f1 | 605 | if (question.hasEDNSCookie()) |
b226e8fa | 606 | numreceivedcookie++; |
9951e2d0 | 607 | |
ad5f04f1 | 608 | if (question.d.qr) |
b226e8fa | 609 | continue; |
78f1d7b5 | 610 | |
b226e8fa | 611 | S.ringAccount("queries", question.qdomain, question.qtype); |
531fce77 | 612 | S.ringAccount("remotes", question.getInnerRemote()); |
ad5f04f1 CH |
613 | if (logDNSQueries) { |
614 | g_log << Logger::Notice << "Remote " << question.getRemoteString() << " wants '" << question.qdomain << "|" << question.qtype << "', do = " << question.d_dnssecOk << ", bufsize = " << question.getMaxReplyLen(); | |
615 | if (question.d_ednsRawPacketSizeLimit > 0 && question.getMaxReplyLen() != (unsigned int)question.d_ednsRawPacketSizeLimit) | |
616 | g_log << " (" << question.d_ednsRawPacketSizeLimit << ")"; | |
b226e8fa | 617 | } |
6a90bacf | 618 | |
ad5f04f1 | 619 | if (PC.enabled() && (question.d.opcode != Opcode::Notify && question.d.opcode != Opcode::Update) && question.couldBeCached()) { |
386c5b13 | 620 | start = diff; |
ad5f04f1 | 621 | bool haveSomething = PC.get(question, cached); // does the PacketCache recognize this question? |
b226e8fa | 622 | if (haveSomething) { |
ad5f04f1 CH |
623 | if (logDNSQueries) |
624 | g_log << ": packetcache HIT" << endl; | |
625 | cached.setRemote(&question.d_remote); // inlined | |
208abc4c | 626 | cached.d_inner_remote = question.d_inner_remote; |
ad5f04f1 | 627 | cached.setSocket(question.getSocket()); // inlined |
b226e8fa RG |
628 | cached.d_anyLocal = question.d_anyLocal; |
629 | cached.setMaxReplyLen(question.getMaxReplyLen()); | |
ad5f04f1 CH |
630 | cached.d.rd = question.d.rd; // copy in recursion desired bit |
631 | cached.d.id = question.d.id; | |
b226e8fa | 632 | cached.commitD(); // commit d to the packet inlined |
386c5b13 KM |
633 | |
634 | diff = question.d_dt.udiffNoReset(); | |
635 | cache_latency = 0.999 * cache_latency + 0.001 * std::max(diff - start, 0); | |
636 | start = diff; | |
637 | ||
b226e8fa | 638 | NS->send(cached); // answer it then inlined |
386c5b13 | 639 | |
ad5f04f1 | 640 | diff = question.d_dt.udiff(); |
386c5b13 KM |
641 | send_latency = 0.999 * send_latency + 0.001 * std::max(diff - start, 0); |
642 | avg_latency = 0.999 * avg_latency + 0.001 * std::max(diff, 0); // 'EWMA' | |
b226e8fa RG |
643 | continue; |
644 | } | |
386c5b13 KM |
645 | diff = question.d_dt.udiffNoReset(); |
646 | cache_latency = 0.999 * cache_latency + 0.001 * std::max(diff - start, 0); | |
b226e8fa | 647 | } |
abc8f3f9 | 648 | |
ad5f04f1 CH |
649 | if (distributor->isOverloaded()) { |
650 | if (logDNSQueries) | |
651 | g_log << ": Dropped query, backends are overloaded" << endl; | |
b226e8fa | 652 | overloadDrops++; |
75fde355 KM |
653 | continue; |
654 | } | |
bbe4b041 | 655 | |
b226e8fa RG |
656 | if (logDNSQueries) { |
657 | if (PC.enabled()) { | |
ad5f04f1 CH |
658 | g_log << ": packetcache MISS" << endl; |
659 | } | |
660 | else { | |
661 | g_log << endl; | |
b226e8fa | 662 | } |
bbe4b041 | 663 | } |
e7e691cc | 664 | |
b226e8fa RG |
665 | try { |
666 | distributor->question(question, &sendout); // otherwise, give to the distributor | |
667 | } | |
ad5f04f1 | 668 | catch (DistributorFatal& df) { // when this happens, we have leaked loads of memory. Bailing out time. |
b226e8fa RG |
669 | _exit(1); |
670 | } | |
491d03d7 | 671 | } |
b226e8fa | 672 | catch (const std::exception& e) { |
ad5f04f1 | 673 | g_log << Logger::Error << "Caught unhandled exception in question thread: " << e.what() << endl; |
491d03d7 | 674 | } |
619e8acc | 675 | } |
619e8acc | 676 | } |
ad5f04f1 CH |
677 | catch (PDNSException& pe) { |
678 | g_log << Logger::Error << "Fatal error in question thread: " << pe.reason << endl; | |
26d75dc2 | 679 | _exit(1); |
680 | } | |
619e8acc | 681 | |
9092419e | 682 | static void dummyThread() |
2250bafa | 683 | { |
2250bafa | 684 | } |
685 | ||
686 | static void triggerLoadOfLibraries() | |
687 | { | |
9092419e RG |
688 | std::thread dummy(dummyThread); |
689 | dummy.join(); | |
2250bafa | 690 | } |
691 | ||
8de02315 | 692 | static void mainthread() |
619e8acc | 693 | { |
ad5f04f1 CH |
694 | gid_t newgid = 0; |
695 | if (!::arg()["setgid"].empty()) | |
696 | newgid = strToGID(::arg()["setgid"]); | |
697 | uid_t newuid = 0; | |
698 | if (!::arg()["setuid"].empty()) | |
699 | newuid = strToUID(::arg()["setuid"]); | |
700 | ||
701 | g_anyToTcp = ::arg().mustDo("any-to-tcp"); | |
702 | g_8bitDNS = ::arg().mustDo("8bit-dns"); | |
8900e2e3 | 703 | #ifdef HAVE_LUA_RECORDS |
ad5f04f1 CH |
704 | g_doLuaRecord = ::arg().mustDo("enable-lua-records"); |
705 | g_LuaRecordSharedState = (::arg()["enable-lua-records"] == "shared"); | |
706 | g_luaRecordExecLimit = ::arg().asNum("lua-records-exec-limit"); | |
707 | g_luaHealthChecksInterval = ::arg().asNum("lua-health-checks-interval"); | |
a6be268f CHB |
708 | g_luaConsistentHashesExpireDelay = ::arg().asNum("lua-consistent-hashes-expire-delay"); |
709 | g_luaConsistentHashesCleanupInterval = ::arg().asNum("lua-consistent-hashes-cleanup-interval"); | |
ad5f04f1 | 710 | g_luaHealthChecksExpireDelay = ::arg().asNum("lua-health-checks-expire-delay"); |
8900e2e3 | 711 | #endif |
c113acc3 | 712 | #ifdef ENABLE_GSS_TSIG |
fd7bbdb7 | 713 | g_doGssTSIG = ::arg().mustDo("enable-gss-tsig"); |
c113acc3 | 714 | #endif |
8dee0750 | 715 | |
ad5f04f1 CH |
716 | DNSPacket::s_udpTruncationThreshold = std::max(512, ::arg().asNum("udp-truncation-threshold")); |
717 | DNSPacket::s_doEDNSSubnetProcessing = ::arg().mustDo("edns-subnet-processing"); | |
718 | PacketHandler::s_SVCAutohints = ::arg().mustDo("svc-autohints"); | |
199631c6 | 719 | |
ad5f04f1 CH |
720 | g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]); |
721 | g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size"); | |
4172a5b2 | 722 | |
ad5f04f1 CH |
723 | if (::arg()["edns-cookie-secret"].size() != 0) { |
724 | // User wants cookie processing | |
37063755 | 725 | #ifdef HAVE_CRYPTO_SHORTHASH // we can do siphash-based cookies |
ad5f04f1 CH |
726 | DNSPacket::s_doEDNSCookieProcessing = true; |
727 | try { | |
728 | if (::arg()["edns-cookie-secret"].size() != EDNSCookiesOpt::EDNSCookieSecretSize) { | |
729 | throw std::range_error("wrong size (" + std::to_string(::arg()["edns-cookie-secret"].size()) + "), must be " + std::to_string(EDNSCookiesOpt::EDNSCookieSecretSize)); | |
730 | } | |
731 | DNSPacket::s_EDNSCookieKey = makeBytesFromHex(::arg()["edns-cookie-secret"]); | |
732 | } | |
733 | catch (const std::range_error& e) { | |
734 | g_log << Logger::Error << "edns-cookie-secret invalid: " << e.what() << endl; | |
735 | exit(1); | |
736 | } | |
37063755 | 737 | #else |
ad5f04f1 CH |
738 | 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; |
739 | exit(1); | |
37063755 | 740 | #endif |
ad5f04f1 | 741 | } |
37063755 | 742 | |
ad5f04f1 CH |
743 | PC.setTTL(::arg().asNum("cache-ttl")); |
744 | PC.setMaxEntries(::arg().asNum("max-packet-cache-entries")); | |
745 | QC.setMaxEntries(::arg().asNum("max-cache-entries")); | |
746 | DNSSECKeeper::setMaxEntries(::arg().asNum("max-cache-entries")); | |
bf269e28 | 747 | |
ad5f04f1 CH |
748 | if (!PC.enabled() && ::arg().mustDo("log-dns-queries")) { |
749 | g_log << Logger::Warning << "Packet cache disabled, logging queries without HIT/MISS" << endl; | |
750 | } | |
d786e4d4 CH |
751 | if (::arg()["outgoing-axfr-expand-alias"] == "ignore-errors") { |
752 | g_log << Logger::Error << "Ignoring ALIAS resolve failures on outgoing AXFR transfers, see option \"outgoing-axfr-expand-alias\"" << endl; | |
753 | } | |
1b16851b | 754 | |
ad5f04f1 | 755 | stubParseResolveConf(); |
199631c6 | 756 | |
ad5f04f1 | 757 | if (!::arg()["chroot"].empty()) { |
75336810 | 758 | #ifdef HAVE_SYSTEMD |
ad5f04f1 CH |
759 | char* ns; |
760 | ns = getenv("NOTIFY_SOCKET"); | |
761 | if (ns != nullptr) { | |
762 | g_log << Logger::Error << "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'" << endl; | |
763 | exit(1); | |
764 | } | |
75336810 | 765 | #endif |
ad5f04f1 CH |
766 | triggerLoadOfLibraries(); |
767 | if (::arg().mustDo("primary") || ::arg().mustDo("secondary")) | |
768 | gethostbyname("a.root-servers.net"); // this forces all lookup libraries to be loaded | |
769 | Utility::dropGroupPrivs(newuid, newgid); | |
770 | if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) { | |
771 | g_log << Logger::Error << "Unable to chroot to '" + ::arg()["chroot"] + "': " << stringerror() << ", exiting" << endl; | |
772 | exit(1); | |
773 | } | |
774 | else | |
775 | g_log << Logger::Error << "Chrooted to '" << ::arg()["chroot"] << "'" << endl; | |
776 | } | |
777 | else { | |
778 | Utility::dropGroupPrivs(newuid, newgid); | |
779 | } | |
357f6a75 | 780 | |
dea47634 | 781 | AuthWebServer webserver; |
f1d6a7ce | 782 | Utility::dropUserPrivs(newuid); |
619e8acc | 783 | |
ad5f04f1 | 784 | if (::arg().mustDo("resolver")) { |
2bbc9eb0 | 785 | DP = std::make_unique<DNSProxy>(::arg()["resolver"]); |
619e8acc BH |
786 | DP->go(); |
787 | } | |
e6402e23 PL |
788 | |
789 | try { | |
790 | doSecPoll(true); | |
791 | } | |
ad5f04f1 CH |
792 | catch (...) { |
793 | } | |
e6402e23 | 794 | |
efc10591 PL |
795 | { |
796 | // Some sanity checking on default key settings | |
797 | bool hadKeyError = false; | |
798 | int kskAlgo{0}, zskAlgo{0}; | |
844535e5 | 799 | for (const string algotype : {"ksk", "zsk"}) { |
efc10591 | 800 | int algo, size; |
ad5f04f1 | 801 | if (::arg()["default-" + algotype + "-algorithm"].empty()) |
efc10591 | 802 | continue; |
ad5f04f1 CH |
803 | algo = DNSSECKeeper::shorthand2algorithm(::arg()["default-" + algotype + "-algorithm"]); |
804 | size = ::arg().asNum("default-" + algotype + "-size"); | |
efc10591 | 805 | if (algo == -1) { |
ad5f04f1 | 806 | g_log << Logger::Error << "Error: default-" << algotype << "-algorithm set to unknown algorithm: " << ::arg()["default-" + algotype + "-algorithm"] << endl; |
efc10591 PL |
807 | hadKeyError = true; |
808 | } | |
809 | else if (algo <= 10 && size == 0) { | |
ad5f04f1 | 810 | 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; |
efc10591 PL |
811 | hadKeyError = true; |
812 | } | |
813 | if (algotype == "ksk") { | |
814 | kskAlgo = algo; | |
ad5f04f1 CH |
815 | } |
816 | else { | |
efc10591 PL |
817 | zskAlgo = algo; |
818 | } | |
8b8e8670 | 819 | } |
efc10591 PL |
820 | if (hadKeyError) { |
821 | exit(1); | |
822 | } | |
823 | if (kskAlgo == 0 && zskAlgo != 0) { | |
ad5f04f1 | 824 | g_log << Logger::Error << "Error: default-zsk-algorithm is set, but default-ksk-algorithm is not set." << endl; |
efc10591 PL |
825 | exit(1); |
826 | } | |
827 | if (zskAlgo != 0 && zskAlgo != kskAlgo) { | |
ad5f04f1 | 828 | 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; |
efc10591 | 829 | exit(1); |
8b8e8670 | 830 | } |
82ee5aa5 PL |
831 | } |
832 | ||
20829585 | 833 | pdns::parseQueryLocalAddress(::arg()["query-local-address"]); |
20829585 | 834 | |
1886a4b3 PL |
835 | pdns::parseTrustedNotificationProxy(::arg()["trusted-notification-proxy"]); |
836 | ||
cfb35c2f KM |
837 | UeberBackend::go(); |
838 | ||
839 | // Setup the zone cache | |
840 | g_zoneCache.setRefreshInterval(::arg().asNum("zone-cache-refresh-interval")); | |
841 | try { | |
842 | UeberBackend B; | |
843 | B.updateZoneCache(); | |
844 | } | |
845 | catch (PDNSException& e) { | |
846 | g_log << Logger::Error << "PDNSException while filling the zone cache: " << e.reason << endl; | |
847 | exit(1); | |
848 | } | |
849 | catch (std::exception& e) { | |
850 | g_log << Logger::Error << "STL Exception while filling the zone cache: " << e.what() << endl; | |
851 | exit(1); | |
852 | } | |
853 | ||
619e8acc | 854 | // NOW SAFE TO CREATE THREADS! |
a270fbcb | 855 | s_dynListener->go(); |
619e8acc | 856 | |
a690b08c FM |
857 | if (::arg().mustDo("webserver") || ::arg().mustDo("api")) { |
858 | webserver.go(S); | |
859 | } | |
dea47634 | 860 | |
ad5f04f1 CH |
861 | if (::arg().mustDo("primary") || ::arg().mustDo("secondary") || !::arg()["forward-notify"].empty()) |
862 | Communicator.go(); | |
619e8acc | 863 | |
6738003e | 864 | s_tcpNameserver->go(); // tcp nameserver launch |
952d3fcb | 865 | |
ad5f04f1 | 866 | unsigned int max_rthreads = ::arg().asNum("receiver-threads", 1); |
346872ff | 867 | s_distributors.resize(max_rthreads); |
ad5f04f1 | 868 | for (unsigned int n = 0; n < max_rthreads; ++n) { |
c2826d2e RG |
869 | std::thread t(qthread, n); |
870 | t.detach(); | |
871 | } | |
619e8acc | 872 | |
ad5f04f1 | 873 | std::thread carbonThread(carbonDumpThread); // runs even w/o carbon, might change @ runtime |
ba98c6d7 | 874 | |
6988eae9 PL |
875 | #ifdef HAVE_SYSTEMD |
876 | /* If we are here, notify systemd that we are ay-ok! This might have some | |
877 | * timing issues with the backend-threads. e.g. if the initial MySQL connection | |
878 | * is slow and times out (leading to process termination through the backend) | |
879 | * We probably have told systemd already that we have started correctly. | |
880 | */ | |
ad5f04f1 | 881 | sd_notify(0, "READY=1"); |
6988eae9 PL |
882 | #endif |
883 | ||
d5385b35 CH |
884 | const uint32_t secpollInterval = 1800; |
885 | uint32_t secpollSince = 0; | |
1f0fb39a | 886 | uint32_t zoneCacheUpdateSince = 0; |
ad5f04f1 | 887 | for (;;) { |
1f0fb39a | 888 | const uint32_t sleeptime = g_zoneCache.getRefreshInterval() == 0 ? secpollInterval : std::min(secpollInterval, g_zoneCache.getRefreshInterval()); |
ad5f04f1 | 889 | sleep(sleeptime); // if any signals arrive, we might run more often than expected. |
d5385b35 | 890 | |
1f0fb39a CH |
891 | zoneCacheUpdateSince += sleeptime; |
892 | if (zoneCacheUpdateSince >= g_zoneCache.getRefreshInterval()) { | |
a65da20c CH |
893 | try { |
894 | UeberBackend B; | |
1f0fb39a CH |
895 | B.updateZoneCache(); |
896 | zoneCacheUpdateSince = 0; | |
a65da20c | 897 | } |
ad5f04f1 CH |
898 | catch (PDNSException& e) { |
899 | g_log << Logger::Error << "PDNSException while updating zone cache: " << e.reason << endl; | |
a65da20c | 900 | } |
ad5f04f1 CH |
901 | catch (std::exception& e) { |
902 | g_log << Logger::Error << "STL Exception while updating zone cache: " << e.what() << endl; | |
a65da20c | 903 | } |
d5385b35 CH |
904 | } |
905 | ||
6e97d53c | 906 | secpollSince += sleeptime; |
d5385b35 CH |
907 | if (secpollSince >= secpollInterval) { |
908 | secpollSince = 0; | |
909 | try { | |
910 | doSecPoll(false); | |
911 | } | |
ad5f04f1 CH |
912 | catch (...) { |
913 | } | |
ea272da2 | 914 | } |
199631c6 | 915 | } |
ad5f04f1 CH |
916 | |
917 | g_log << Logger::Error << "Mainthread exiting - should never happen" << endl; | |
619e8acc | 918 | } |
8cb70f23 CH |
919 | |
920 | static void daemonize() | |
921 | { | |
ad5f04f1 | 922 | if (fork()) |
8cb70f23 CH |
923 | exit(0); // bye bye |
924 | ||
925 | setsid(); | |
926 | ||
ad5f04f1 CH |
927 | int i = open("/dev/null", O_RDWR); /* open stdin */ |
928 | if (i < 0) | |
929 | g_log << Logger::Critical << "Unable to open /dev/null: " << stringerror() << endl; | |
8cb70f23 | 930 | else { |
ad5f04f1 CH |
931 | dup2(i, 0); /* stdin */ |
932 | dup2(i, 1); /* stderr */ | |
933 | dup2(i, 2); /* stderr */ | |
8cb70f23 CH |
934 | close(i); |
935 | } | |
936 | } | |
937 | ||
938 | static int cpid; | |
d73de874 | 939 | static void takedown(int /* i */) |
8cb70f23 | 940 | { |
ad5f04f1 CH |
941 | if (cpid) { |
942 | g_log << Logger::Error << "Guardian is killed, taking down children with us" << endl; | |
943 | kill(cpid, SIGKILL); | |
8cb70f23 CH |
944 | exit(0); |
945 | } | |
946 | } | |
947 | ||
948 | static void writePid() | |
949 | { | |
ad5f04f1 | 950 | if (!::arg().mustDo("write-pid")) |
8cb70f23 CH |
951 | return; |
952 | ||
ad5f04f1 | 953 | string fname = ::arg()["socket-dir"]; |
8cb70f23 CH |
954 | if (::arg()["socket-dir"].empty()) { |
955 | if (::arg()["chroot"].empty()) | |
956 | fname = std::string(LOCALSTATEDIR) + "/pdns"; | |
957 | else | |
958 | fname = ::arg()["chroot"] + "/"; | |
ad5f04f1 CH |
959 | } |
960 | else if (!::arg()["socket-dir"].empty() && !::arg()["chroot"].empty()) { | |
8cb70f23 CH |
961 | fname = ::arg()["chroot"] + ::arg()["socket-dir"]; |
962 | } | |
963 | ||
c509c9fa | 964 | fname += +"/" + g_programname + ".pid"; |
8cb70f23 | 965 | ofstream of(fname.c_str()); |
ad5f04f1 CH |
966 | if (of) |
967 | of << getpid() << endl; | |
8cb70f23 | 968 | else |
ad5f04f1 | 969 | g_log << Logger::Error << "Writing pid for " << getpid() << " to " << fname << " failed: " << stringerror() << endl; |
8cb70f23 CH |
970 | } |
971 | ||
972 | static int g_fd1[2], g_fd2[2]; | |
ad5f04f1 | 973 | static FILE* g_fp; |
8cb70f23 CH |
974 | static std::mutex g_guardian_lock; |
975 | ||
976 | // The next two methods are not in dynhandler.cc because they use a few items declared in this file. | |
d73de874 | 977 | static string DLCycleHandler(const vector<string>& /* parts */, pid_t /* ppid */) |
8cb70f23 CH |
978 | { |
979 | kill(cpid, SIGKILL); // why? | |
980 | kill(cpid, SIGKILL); // why? | |
981 | sleep(1); | |
982 | return "ok"; | |
983 | } | |
984 | ||
d73de874 | 985 | static string DLRestHandler(const vector<string>& parts, pid_t /* ppid */) |
8cb70f23 CH |
986 | { |
987 | string line; | |
988 | ||
ad5f04f1 CH |
989 | for (vector<string>::const_iterator i = parts.begin(); i != parts.end(); ++i) { |
990 | if (i != parts.begin()) | |
991 | line.append(1, ' '); | |
8cb70f23 CH |
992 | line.append(*i); |
993 | } | |
ad5f04f1 | 994 | line.append(1, '\n'); |
8cb70f23 CH |
995 | |
996 | std::lock_guard<std::mutex> l(g_guardian_lock); | |
997 | ||
998 | try { | |
ad5f04f1 | 999 | writen2(g_fd1[1], line.c_str(), line.size() + 1); |
8cb70f23 | 1000 | } |
ad5f04f1 CH |
1001 | catch (PDNSException& ae) { |
1002 | return "Error communicating with instance: " + ae.reason; | |
8cb70f23 CH |
1003 | } |
1004 | char mesg[512]; | |
1005 | string response; | |
ad5f04f1 CH |
1006 | while (fgets(mesg, sizeof(mesg), g_fp)) { |
1007 | if (*mesg == '\0') | |
8cb70f23 | 1008 | break; |
ad5f04f1 | 1009 | response += mesg; |
8cb70f23 CH |
1010 | } |
1011 | boost::trim_right(response); | |
1012 | return response; | |
1013 | } | |
1014 | ||
ad5f04f1 | 1015 | static int guardian(int argc, char** argv) |
8cb70f23 | 1016 | { |
ad5f04f1 | 1017 | if (isGuarded(argv)) |
8cb70f23 CH |
1018 | return 0; |
1019 | ||
ad5f04f1 | 1020 | int infd = 0, outfd = 1; |
8cb70f23 | 1021 | |
c509c9fa | 1022 | DynListener dlg(g_programname); |
ad5f04f1 CH |
1023 | dlg.registerFunc("QUIT", &DLQuitHandler, "quit daemon"); |
1024 | dlg.registerFunc("CYCLE", &DLCycleHandler, "restart instance"); | |
1025 | dlg.registerFunc("PING", &DLPingHandler, "ping guardian"); | |
1026 | dlg.registerFunc("STATUS", &DLStatusHandler, "get instance status from guardian"); | |
8cb70f23 CH |
1027 | dlg.registerRestFunc(&DLRestHandler); |
1028 | dlg.go(); | |
ad5f04f1 | 1029 | string progname = argv[0]; |
8cb70f23 | 1030 | |
ad5f04f1 CH |
1031 | bool first = true; |
1032 | cpid = 0; | |
8cb70f23 CH |
1033 | |
1034 | g_guardian_lock.lock(); | |
1035 | ||
ad5f04f1 | 1036 | for (;;) { |
8cb70f23 CH |
1037 | int pid; |
1038 | setStatus("Launching child"); | |
1039 | ||
ad5f04f1 CH |
1040 | if (pipe(g_fd1) < 0 || pipe(g_fd2) < 0) { |
1041 | g_log << Logger::Critical << "Unable to open pipe for coprocess: " << stringerror() << endl; | |
8cb70f23 CH |
1042 | exit(1); |
1043 | } | |
1044 | ||
ad5f04f1 CH |
1045 | if (!(g_fp = fdopen(g_fd2[0], "r"))) { |
1046 | g_log << Logger::Critical << "Unable to associate a file pointer with pipe: " << stringerror() << endl; | |
8cb70f23 CH |
1047 | exit(1); |
1048 | } | |
ad5f04f1 | 1049 | setbuf(g_fp, nullptr); // no buffering please, confuses select |
8cb70f23 | 1050 | |
ad5f04f1 | 1051 | if (!(pid = fork())) { // child |
8cb70f23 CH |
1052 | signal(SIGTERM, SIG_DFL); |
1053 | ||
1054 | signal(SIGHUP, SIG_DFL); | |
1055 | signal(SIGUSR1, SIG_DFL); | |
1056 | signal(SIGUSR2, SIG_DFL); | |
1057 | ||
ad5f04f1 | 1058 | char** const newargv = new char*[argc + 2]; |
8cb70f23 CH |
1059 | int n; |
1060 | ||
ad5f04f1 CH |
1061 | if (::arg()["config-name"] != "") { |
1062 | progname += "-" + ::arg()["config-name"]; | |
1063 | g_log << Logger::Error << "Virtual configuration name: " << ::arg()["config-name"] << endl; | |
8cb70f23 CH |
1064 | } |
1065 | ||
ad5f04f1 CH |
1066 | newargv[0] = strdup(const_cast<char*>((progname + "-instance").c_str())); |
1067 | for (n = 1; n < argc; n++) { | |
1068 | newargv[n] = argv[n]; | |
8cb70f23 | 1069 | } |
ad5f04f1 | 1070 | newargv[n] = nullptr; |
8cb70f23 | 1071 | |
ad5f04f1 | 1072 | g_log << Logger::Error << "Guardian is launching an instance" << endl; |
8cb70f23 CH |
1073 | close(g_fd1[1]); |
1074 | fclose(g_fp); // this closes g_fd2[0] for us | |
1075 | ||
ad5f04f1 | 1076 | if (g_fd1[0] != infd) { |
8cb70f23 CH |
1077 | dup2(g_fd1[0], infd); |
1078 | close(g_fd1[0]); | |
1079 | } | |
1080 | ||
ad5f04f1 | 1081 | if (g_fd2[1] != outfd) { |
8cb70f23 CH |
1082 | dup2(g_fd2[1], outfd); |
1083 | close(g_fd2[1]); | |
1084 | } | |
ad5f04f1 CH |
1085 | if (execvp(argv[0], newargv) < 0) { |
1086 | g_log << Logger::Error << "Unable to execvp '" << argv[0] << "': " << stringerror() << endl; | |
1087 | char** p = newargv; | |
1088 | while (*p) | |
1089 | g_log << Logger::Error << *p++ << endl; | |
8cb70f23 CH |
1090 | |
1091 | exit(1); | |
1092 | } | |
ad5f04f1 | 1093 | g_log << Logger::Error << "execvp returned!!" << endl; |
8cb70f23 CH |
1094 | // never reached |
1095 | } | |
ad5f04f1 | 1096 | else if (pid > 0) { // parent |
8cb70f23 CH |
1097 | close(g_fd1[0]); |
1098 | close(g_fd2[1]); | |
1099 | ||
ad5f04f1 CH |
1100 | if (first) { |
1101 | first = false; | |
8cb70f23 CH |
1102 | signal(SIGTERM, takedown); |
1103 | ||
1104 | signal(SIGHUP, SIG_IGN); | |
1105 | signal(SIGUSR1, SIG_IGN); | |
1106 | signal(SIGUSR2, SIG_IGN); | |
1107 | ||
1108 | writePid(); | |
1109 | } | |
1110 | g_guardian_lock.unlock(); | |
1111 | int status; | |
ad5f04f1 CH |
1112 | cpid = pid; |
1113 | for (;;) { | |
1114 | int ret = waitpid(pid, &status, WNOHANG); | |
8cb70f23 | 1115 | |
ad5f04f1 CH |
1116 | if (ret < 0) { |
1117 | g_log << Logger::Error << "In guardian loop, waitpid returned error: " << stringerror() << endl; | |
1118 | g_log << Logger::Error << "Dying" << endl; | |
8cb70f23 CH |
1119 | exit(1); |
1120 | } | |
ad5f04f1 | 1121 | else if (ret) // something exited |
8cb70f23 CH |
1122 | break; |
1123 | else { // child is alive | |
1124 | // execute some kind of ping here | |
ad5f04f1 | 1125 | if (DLQuitPlease()) |
8cb70f23 | 1126 | takedown(1); // needs a parameter.. |
564db8c5 | 1127 | setStatus("Child running on pid " + std::to_string(pid)); |
8cb70f23 CH |
1128 | sleep(1); |
1129 | } | |
1130 | } | |
1131 | ||
1132 | g_guardian_lock.lock(); | |
1133 | close(g_fd1[1]); | |
1134 | fclose(g_fp); | |
ad5f04f1 | 1135 | g_fp = nullptr; |
8cb70f23 | 1136 | |
ad5f04f1 CH |
1137 | if (WIFEXITED(status)) { |
1138 | int ret = WEXITSTATUS(status); | |
8cb70f23 | 1139 | |
ad5f04f1 CH |
1140 | if (ret == 99) { |
1141 | g_log << Logger::Error << "Child requested a stop, exiting" << endl; | |
8cb70f23 CH |
1142 | exit(1); |
1143 | } | |
564db8c5 | 1144 | setStatus("Child died with code " + std::to_string(ret)); |
ad5f04f1 | 1145 | g_log << Logger::Error << "Our pdns instance exited with code " << ret << ", respawning" << endl; |
8cb70f23 CH |
1146 | |
1147 | sleep(1); | |
1148 | continue; | |
1149 | } | |
ad5f04f1 CH |
1150 | if (WIFSIGNALED(status)) { |
1151 | int sig = WTERMSIG(status); | |
564db8c5 | 1152 | setStatus("Child died because of signal " + std::to_string(sig)); |
ad5f04f1 | 1153 | g_log << Logger::Error << "Our pdns instance (" << pid << ") exited after signal " << sig << endl; |
8cb70f23 | 1154 | #ifdef WCOREDUMP |
ad5f04f1 CH |
1155 | if (WCOREDUMP(status)) |
1156 | g_log << Logger::Error << "Dumped core" << endl; | |
8cb70f23 CH |
1157 | #endif |
1158 | ||
ad5f04f1 | 1159 | g_log << Logger::Error << "Respawning" << endl; |
8cb70f23 CH |
1160 | sleep(1); |
1161 | continue; | |
1162 | } | |
ad5f04f1 | 1163 | g_log << Logger::Error << "No clue what happened! Respawning" << endl; |
8cb70f23 CH |
1164 | } |
1165 | else { | |
ad5f04f1 | 1166 | g_log << Logger::Error << "Unable to fork: " << stringerror() << endl; |
8cb70f23 CH |
1167 | exit(1); |
1168 | } | |
1169 | } | |
1170 | } | |
1171 | ||
1172 | #if defined(__GLIBC__) && !defined(__UCLIBC__) | |
1173 | #include <execinfo.h> | |
1174 | static void tbhandler(int num) | |
1175 | { | |
ad5f04f1 CH |
1176 | g_log << Logger::Critical << "Got a signal " << num << ", attempting to print trace: " << endl; |
1177 | void* array[20]; // only care about last 17 functions (3 taken with tracing support) | |
8cb70f23 | 1178 | size_t size; |
ad5f04f1 | 1179 | char** strings; |
8cb70f23 CH |
1180 | size_t i; |
1181 | ||
ad5f04f1 CH |
1182 | size = backtrace(array, 20); |
1183 | strings = backtrace_symbols(array, size); // Need -rdynamic gcc (linker) flag for this to work | |
8cb70f23 CH |
1184 | |
1185 | for (i = 0; i < size; i++) // skip useless functions | |
ad5f04f1 | 1186 | g_log << Logger::Error << strings[i] << endl; |
8cb70f23 CH |
1187 | |
1188 | signal(SIGABRT, SIG_DFL); | |
1189 | abort(); // hopefully will give core | |
1190 | } | |
1191 | #endif | |
1192 | ||
460a6daa RG |
1193 | #ifdef COVERAGE |
1194 | static void sigTermHandler([[maybe_unused]] int signal) | |
1195 | { | |
1196 | pdns::coverage::dumpCoverageData(); | |
1197 | _exit(EXIT_SUCCESS); | |
1198 | } | |
1199 | #endif /* COVERAGE */ | |
1200 | ||
8cb70f23 | 1201 | //! The main function of pdns, the pdns process |
ad5f04f1 | 1202 | int main(int argc, char** argv) |
8cb70f23 CH |
1203 | { |
1204 | versionSetProduct(ProductAuthoritative); | |
1205 | reportAllTypes(); // init MOADNSParser | |
1206 | ||
c509c9fa CH |
1207 | g_programname = "pdns"; |
1208 | g_starttime = time(nullptr); | |
8cb70f23 CH |
1209 | |
1210 | #if defined(__GLIBC__) && !defined(__UCLIBC__) | |
ad5f04f1 CH |
1211 | signal(SIGSEGV, tbhandler); |
1212 | signal(SIGFPE, tbhandler); | |
1213 | signal(SIGABRT, tbhandler); | |
1214 | signal(SIGILL, tbhandler); | |
8cb70f23 CH |
1215 | #endif |
1216 | ||
1217 | std::ios_base::sync_with_stdio(false); | |
1218 | ||
1219 | g_log.toConsole(Logger::Warning); | |
1220 | try { | |
1221 | declareArguments(); | |
1222 | ||
ad5f04f1 | 1223 | ::arg().laxParse(argc, argv); // do a lax parse |
8cb70f23 | 1224 | |
ad5f04f1 | 1225 | if (::arg().mustDo("version")) { |
8cb70f23 CH |
1226 | showProductVersion(); |
1227 | showBuildConfiguration(); | |
fd5d471f | 1228 | return 0; |
8cb70f23 CH |
1229 | } |
1230 | ||
ad5f04f1 | 1231 | if (::arg()["config-name"] != "") |
c509c9fa | 1232 | g_programname += "-" + ::arg()["config-name"]; |
8cb70f23 | 1233 | |
c509c9fa | 1234 | g_log.setName(g_programname); |
8cb70f23 | 1235 | |
c509c9fa | 1236 | string configname = ::arg()["config-dir"] + "/" + g_programname + ".conf"; |
8cb70f23 CH |
1237 | cleanSlashes(configname); |
1238 | ||
ad5f04f1 | 1239 | if (::arg()["config"] != "default" && !::arg().mustDo("no-config")) // "config" == print a configuration file |
8cb70f23 CH |
1240 | ::arg().laxFile(configname.c_str()); |
1241 | ||
ad5f04f1 CH |
1242 | ::arg().laxParse(argc, argv); // reparse so the commandline still wins |
1243 | if (!::arg()["logging-facility"].empty()) { | |
1244 | int val = logFacilityToLOG(::arg().asNum("logging-facility")); | |
1245 | if (val >= 0) | |
8cb70f23 CH |
1246 | g_log.setFacility(val); |
1247 | else | |
ad5f04f1 | 1248 | g_log << Logger::Error << "Unknown logging facility " << ::arg().asNum("logging-facility") << endl; |
8cb70f23 CH |
1249 | } |
1250 | ||
8cb70f23 CH |
1251 | if (!::arg().isEmpty("domain-metadata-cache-ttl")) |
1252 | ::arg().set("zone-metadata-cache-ttl") = ::arg()["domain-metadata-cache-ttl"]; | |
1253 | ||
1254 | // this mirroring back is on purpose, so that config dumps reflect the actual setting on both names | |
8cb70f23 CH |
1255 | ::arg().set("domain-metadata-cache-ttl") = ::arg()["zone-metadata-cache-ttl"]; |
1256 | ||
1257 | g_log.setLoglevel((Logger::Urgency)(::arg().asNum("loglevel"))); | |
46eec001 | 1258 | g_log.setPrefixed(::arg().mustDo("loglevel-show")); |
8cb70f23 CH |
1259 | g_log.disableSyslog(::arg().mustDo("disable-syslog")); |
1260 | g_log.setTimestamps(::arg().mustDo("log-timestamp")); | |
1261 | g_log.toConsole((Logger::Urgency)(::arg().asNum("loglevel"))); | |
1262 | ||
ad5f04f1 CH |
1263 | if (::arg().mustDo("help") || ::arg().mustDo("config")) { |
1264 | ::arg().set("daemon") = "no"; | |
1265 | ::arg().set("guardian") = "no"; | |
8cb70f23 CH |
1266 | } |
1267 | ||
ad5f04f1 CH |
1268 | if (::arg().mustDo("guardian") && !isGuarded(argv)) { |
1269 | if (::arg().mustDo("daemon")) { | |
8cb70f23 CH |
1270 | g_log.toConsole(Logger::Critical); |
1271 | daemonize(); | |
1272 | } | |
1273 | guardian(argc, argv); | |
1274 | // never get here, guardian will reinvoke process | |
ad5f04f1 | 1275 | cerr << "Um, we did get here!" << endl; |
8cb70f23 CH |
1276 | } |
1277 | ||
460a6daa RG |
1278 | #ifdef COVERAGE |
1279 | if (!::arg().mustDo("guardian") && !::arg().mustDo("daemon")) { | |
1280 | signal(SIGTERM, sigTermHandler); | |
1281 | } | |
1282 | #endif | |
1283 | ||
8cb70f23 CH |
1284 | // we really need to do work - either standalone or as an instance |
1285 | ||
1286 | #if defined(__GLIBC__) && !defined(__UCLIBC__) | |
ad5f04f1 CH |
1287 | if (!::arg().mustDo("traceback-handler")) { |
1288 | g_log << Logger::Warning << "Disabling traceback handler" << endl; | |
1289 | signal(SIGSEGV, SIG_DFL); | |
1290 | signal(SIGFPE, SIG_DFL); | |
1291 | signal(SIGABRT, SIG_DFL); | |
1292 | signal(SIGILL, SIG_DFL); | |
8cb70f23 CH |
1293 | } |
1294 | #endif | |
1295 | ||
1296 | #ifdef HAVE_LIBSODIUM | |
ad5f04f1 CH |
1297 | if (sodium_init() == -1) { |
1298 | cerr << "Unable to initialize sodium crypto library" << endl; | |
1299 | exit(99); | |
1300 | } | |
8cb70f23 CH |
1301 | #endif |
1302 | ||
1303 | openssl_thread_setup(); | |
1304 | openssl_seed(); | |
8cb70f23 CH |
1305 | |
1306 | #ifdef HAVE_LUA_RECORDS | |
1307 | MiniCurl::init(); | |
1308 | #endif /* HAVE_LUA_RECORDS */ | |
1309 | ||
ad5f04f1 | 1310 | if (!::arg()["load-modules"].empty()) { |
8cb70f23 CH |
1311 | vector<string> modules; |
1312 | ||
ad5f04f1 | 1313 | stringtok(modules, ::arg()["load-modules"], ", "); |
8cb70f23 CH |
1314 | if (!UeberBackend::loadModules(modules, ::arg()["module-dir"])) { |
1315 | exit(1); | |
1316 | } | |
1317 | } | |
1318 | ||
1319 | BackendMakers().launch(::arg()["launch"]); // vrooooom! | |
1320 | ||
ad5f04f1 CH |
1321 | if (!::arg().getCommands().empty()) { |
1322 | cerr << "Fatal: non-option"; | |
8cb70f23 | 1323 | if (::arg().getCommands().size() > 1) { |
ad5f04f1 | 1324 | cerr << "s"; |
8cb70f23 | 1325 | } |
ad5f04f1 | 1326 | cerr << " ("; |
8cb70f23 CH |
1327 | bool first = true; |
1328 | for (const auto& c : ::arg().getCommands()) { | |
1329 | if (!first) { | |
ad5f04f1 | 1330 | cerr << ", "; |
8cb70f23 CH |
1331 | } |
1332 | first = false; | |
ad5f04f1 | 1333 | cerr << c; |
8cb70f23 | 1334 | } |
ad5f04f1 | 1335 | cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl; |
8cb70f23 CH |
1336 | exit(99); |
1337 | } | |
1338 | ||
ad5f04f1 CH |
1339 | if (::arg().mustDo("help")) { |
1340 | cout << "syntax:" << endl | |
1341 | << endl; | |
1342 | cout << ::arg().helpstring(::arg()["help"]) << endl; | |
8cb70f23 CH |
1343 | exit(0); |
1344 | } | |
1345 | ||
ad5f04f1 | 1346 | if (::arg().mustDo("config")) { |
8cb70f23 CH |
1347 | string config = ::arg()["config"]; |
1348 | if (config == "default") { | |
ad5f04f1 CH |
1349 | cout << ::arg().configstring(false, true); |
1350 | } | |
1351 | else if (config == "diff") { | |
1352 | cout << ::arg().configstring(true, false); | |
1353 | } | |
1354 | else if (config == "check") { | |
8cb70f23 | 1355 | try { |
ad5f04f1 | 1356 | if (!::arg().mustDo("no-config")) |
8cb70f23 | 1357 | ::arg().file(configname.c_str()); |
ad5f04f1 | 1358 | ::arg().parse(argc, argv); |
8cb70f23 CH |
1359 | exit(0); |
1360 | } | |
ad5f04f1 CH |
1361 | catch (const ArgException& A) { |
1362 | cerr << "Fatal error: " << A.reason << endl; | |
8cb70f23 CH |
1363 | exit(1); |
1364 | } | |
ad5f04f1 CH |
1365 | } |
1366 | else { | |
1367 | cout << ::arg().configstring(true, true); | |
8cb70f23 CH |
1368 | } |
1369 | exit(0); | |
1370 | } | |
1371 | ||
ad5f04f1 | 1372 | if (::arg().mustDo("list-modules")) { |
8cb70f23 | 1373 | auto modules = BackendMakers().getModules(); |
ad5f04f1 CH |
1374 | cout << "Modules available:" << endl; |
1375 | for (const auto& m : modules) | |
1376 | cout << m << endl; | |
8cb70f23 CH |
1377 | |
1378 | _exit(99); | |
1379 | } | |
1380 | ||
ad5f04f1 CH |
1381 | if (!::arg().asNum("local-port")) { |
1382 | g_log << Logger::Error << "Unable to launch, binding to no port or port 0 makes no sense" << endl; | |
8cb70f23 CH |
1383 | exit(99); // this isn't going to fix itself either |
1384 | } | |
ad5f04f1 CH |
1385 | if (!BackendMakers().numLauncheable()) { |
1386 | g_log << Logger::Error << "Unable to launch, no backends configured for querying" << endl; | |
8cb70f23 CH |
1387 | exit(99); // this isn't going to fix itself either |
1388 | } | |
ad5f04f1 | 1389 | if (::arg().mustDo("daemon")) { |
8cb70f23 | 1390 | g_log.toConsole(Logger::None); |
ad5f04f1 | 1391 | if (!isGuarded(argv)) |
8cb70f23 CH |
1392 | daemonize(); |
1393 | } | |
1394 | ||
ad5f04f1 CH |
1395 | if (isGuarded(argv)) { |
1396 | g_log << Logger::Warning << "This is a guarded instance of pdns" << endl; | |
a270fbcb | 1397 | s_dynListener = std::make_unique<DynListener>(); // listens on stdin |
8cb70f23 CH |
1398 | } |
1399 | else { | |
ad5f04f1 | 1400 | g_log << Logger::Warning << "This is a standalone pdns" << endl; |
8cb70f23 | 1401 | |
ad5f04f1 | 1402 | if (::arg().mustDo("control-console")) |
a270fbcb | 1403 | s_dynListener = std::make_unique<DynListener>(); |
8cb70f23 | 1404 | else |
a270fbcb | 1405 | s_dynListener = std::make_unique<DynListener>(g_programname); |
8cb70f23 CH |
1406 | |
1407 | writePid(); | |
1408 | } | |
ad5f04f1 CH |
1409 | DynListener::registerFunc("SHOW", &DLShowHandler, "show a specific statistic or * to get a list", "<statistic>"); |
1410 | DynListener::registerFunc("RPING", &DLPingHandler, "ping instance"); | |
1411 | DynListener::registerFunc("QUIT", &DLRQuitHandler, "quit daemon"); | |
1412 | DynListener::registerFunc("UPTIME", &DLUptimeHandler, "get instance uptime"); | |
8cb70f23 CH |
1413 | DynListener::registerFunc("NOTIFY-HOST", &DLNotifyHostHandler, "notify host for specific zone", "<zone> <host>"); |
1414 | DynListener::registerFunc("NOTIFY", &DLNotifyHandler, "queue a notification", "<zone>"); | |
ad5f04f1 CH |
1415 | DynListener::registerFunc("RELOAD", &DLReloadHandler, "reload all zones"); |
1416 | DynListener::registerFunc("REDISCOVER", &DLRediscoverHandler, "discover any new zones"); | |
1417 | DynListener::registerFunc("VERSION", &DLVersionHandler, "get instance version"); | |
1418 | DynListener::registerFunc("PURGE", &DLPurgeHandler, "purge entries from packet cache", "[<record>]"); | |
1419 | DynListener::registerFunc("CCOUNTS", &DLCCHandler, "get cache statistics"); | |
8cb70f23 CH |
1420 | DynListener::registerFunc("QTYPES", &DLQTypesHandler, "get QType statistics"); |
1421 | DynListener::registerFunc("RESPSIZES", &DLRSizesHandler, "get histogram of response sizes"); | |
1422 | DynListener::registerFunc("REMOTES", &DLRemotesHandler, "get top remotes"); | |
ad5f04f1 | 1423 | DynListener::registerFunc("SET", &DLSettingsHandler, "set config variables", "<var> <value>"); |
c02c999b | 1424 | DynListener::registerFunc("RETRIEVE", &DLNotifyRetrieveHandler, "retrieve secondary zone", "<zone> [<ip>]"); |
ad5f04f1 | 1425 | DynListener::registerFunc("CURRENT-CONFIG", &DLCurrentConfigHandler, "retrieve the current configuration", "[diff]"); |
d5540ff1 | 1426 | DynListener::registerFunc("LIST-ZONES", &DLListZones, "show list of zones", "[primary|secondary|native|consumer|producer]"); |
8cb70f23 CH |
1427 | DynListener::registerFunc("TOKEN-LOGIN", &DLTokenLogin, "Login to a PKCS#11 token", "<module> <slot> <pin>"); |
1428 | DynListener::registerFunc("XFR-QUEUE", &DLSuckRequests, "Get all requests for XFR in queue"); | |
1429 | ||
ad5f04f1 CH |
1430 | if (!::arg()["tcp-control-address"].empty()) { |
1431 | DynListener* dlTCP = new DynListener(ComboAddress(::arg()["tcp-control-address"], ::arg().asNum("tcp-control-port"))); | |
8cb70f23 CH |
1432 | dlTCP->go(); |
1433 | } | |
1434 | ||
1435 | // reparse, with error checking | |
ad5f04f1 | 1436 | if (!::arg().mustDo("no-config")) |
8cb70f23 | 1437 | ::arg().file(configname.c_str()); |
ad5f04f1 | 1438 | ::arg().parse(argc, argv); |
8cb70f23 | 1439 | |
ad5f04f1 | 1440 | if (::arg()["server-id"].empty()) { |
8cb70f23 | 1441 | char tmp[128]; |
ad5f04f1 CH |
1442 | if (gethostname(tmp, sizeof(tmp) - 1) == 0) { |
1443 | ::arg().set("server-id") = tmp; | |
1444 | } | |
1445 | else { | |
1446 | g_log << Logger::Warning << "Unable to get the hostname, NSID and id.server values will be empty: " << stringerror() << endl; | |
8cb70f23 CH |
1447 | } |
1448 | } | |
1449 | ||
dd81fb3b | 1450 | s_udpNameserver = std::make_shared<UDPNameserver>(); // this fails when we are not root, throws exception |
fcab180d | 1451 | s_udpReceivers.push_back(s_udpNameserver); |
8cb70f23 CH |
1452 | |
1453 | size_t rthreads = ::arg().asNum("receiver-threads", 1); | |
dd81fb3b | 1454 | if (rthreads > 1 && s_udpNameserver->canReusePort()) { |
fcab180d | 1455 | s_udpReceivers.resize(rthreads); |
8cb70f23 CH |
1456 | |
1457 | for (size_t idx = 1; idx < rthreads; idx++) { | |
1458 | try { | |
fcab180d | 1459 | s_udpReceivers[idx] = std::make_shared<UDPNameserver>(true); |
8cb70f23 | 1460 | } |
ad5f04f1 CH |
1461 | catch (const PDNSException& e) { |
1462 | g_log << Logger::Error << "Unable to reuse port, falling back to original bind" << endl; | |
8cb70f23 CH |
1463 | break; |
1464 | } | |
1465 | } | |
1466 | } | |
1467 | ||
6738003e | 1468 | s_tcpNameserver = make_unique<TCPNameserver>(); |
8cb70f23 | 1469 | } |
ad5f04f1 CH |
1470 | catch (const ArgException& A) { |
1471 | g_log << Logger::Error << "Fatal error: " << A.reason << endl; | |
8cb70f23 CH |
1472 | exit(1); |
1473 | } | |
0d757bd2 RG |
1474 | catch (const std::exception& e) { |
1475 | g_log << Logger::Error << "Fatal error: " << e.what() << endl; | |
1476 | exit(1); | |
1477 | } | |
8cb70f23 CH |
1478 | |
1479 | try { | |
1480 | declareStats(); | |
1481 | } | |
0d757bd2 | 1482 | catch (const PDNSException& PE) { |
ad5f04f1 | 1483 | g_log << Logger::Error << "Exiting because: " << PE.reason << endl; |
8cb70f23 CH |
1484 | exit(1); |
1485 | } | |
c8734ecd PD |
1486 | |
1487 | try { | |
1488 | auto defaultCatalog = ::arg()["default-catalog-zone"]; | |
1489 | if (!defaultCatalog.empty()) { | |
1490 | auto defCatalog = DNSName(defaultCatalog); | |
1491 | } | |
1492 | } | |
1493 | catch (const std::exception& e) { | |
1494 | g_log << Logger::Error << "Invalid value '" << ::arg()["default-catalog-zone"] << "' for default-catalog-zone: " << e.what() << endl; | |
1495 | exit(1); | |
1496 | } | |
8cb70f23 CH |
1497 | S.blacklist("special-memory-usage"); |
1498 | ||
ad5f04f1 | 1499 | DLOG(g_log << Logger::Warning << "Verbose logging in effect" << endl); |
8cb70f23 CH |
1500 | |
1501 | showProductVersion(); | |
1502 | ||
1503 | try { | |
1504 | mainthread(); | |
1505 | } | |
0d757bd2 RG |
1506 | catch (const PDNSException& e) { |
1507 | try { | |
1508 | if (!::arg().mustDo("daemon")) { | |
1509 | cerr << "Exiting because: " << e.reason << endl; | |
1510 | } | |
1511 | } | |
1512 | catch (const ArgException& A) { | |
1513 | } | |
ad5f04f1 | 1514 | g_log << Logger::Error << "Exiting because: " << e.reason << endl; |
8cb70f23 | 1515 | } |
0d757bd2 RG |
1516 | catch (const std::exception& e) { |
1517 | try { | |
1518 | if (!::arg().mustDo("daemon")) { | |
1519 | cerr << "Exiting because of STL error: " << e.what() << endl; | |
1520 | } | |
1521 | } | |
1522 | catch (const ArgException& A) { | |
1523 | } | |
ad5f04f1 | 1524 | g_log << Logger::Error << "Exiting because of STL error: " << e.what() << endl; |
8cb70f23 | 1525 | } |
ad5f04f1 CH |
1526 | catch (...) { |
1527 | cerr << "Uncaught exception of unknown type - sorry" << endl; | |
8cb70f23 CH |
1528 | } |
1529 | ||
1530 | exit(1); | |
1531 | } |