#ifdef HAVE_LIBSODIUM
#include <sodium.h>
+
+#include <cstddef>
#endif
#ifdef HAVE_SYSTEMD
func();
}
- unsigned int n = 0;
+ unsigned int thread = 0;
for (const auto& threadInfo : RecThreadInfo::infos()) {
- if (n++ == RecThreadInfo::id()) {
+ if (thread++ == RecThreadInfo::id()) {
func(); // don't write to ourselves!
continue;
}
- ThreadMSG* tmsg = new ThreadMSG();
+ ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
tmsg->func = func;
tmsg->wantAnswer = true;
- if (write(threadInfo.pipes.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) {
- delete tmsg;
+ if (write(threadInfo.pipes.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: sizeof correct
+ delete tmsg; // NOLINT: manual ownership handling
unixDie("write to thread pipe returned wrong size or error");
}
string* resp = nullptr;
- if (read(threadInfo.pipes.readFromThread, &resp, sizeof(resp)) != sizeof(resp))
+ if (read(threadInfo.pipes.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) { // NOLINT: sizeof correct
unixDie("read from thread pipe returned wrong size or error");
+ }
- if (resp) {
- delete resp;
+ if (resp != nullptr) {
+ delete resp; // NOLINT: manual ownership handling
resp = nullptr;
}
// coverity[leaked_storage]
_exit(1);
}
- unsigned int n = 0;
+ unsigned int thread = 0;
T ret = T();
for (const auto& threadInfo : RecThreadInfo::infos()) {
- if (n++ == RecThreadInfo::id()) {
+ if (thread++ == RecThreadInfo::id()) {
continue;
}
const auto& tps = threadInfo.pipes;
- ThreadMSG* tmsg = new ThreadMSG();
+ ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
tmsg->func = [func] { return voider<T>(func); };
tmsg->wantAnswer = true;
- if (write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) {
- delete tmsg;
+ if (write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT:: sizeof correct
+ delete tmsg; // NOLINT: manual ownership handling
unixDie("write to thread pipe returned wrong size or error");
}
T* resp = nullptr;
- if (read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp))
+ if (read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) // NOLINT: sizeof correct
unixDie("read from thread pipe returned wrong size or error");
if (resp) {
ret += *resp;
- delete resp;
+ delete resp; // NOLINT: manual ownership handling
resp = nullptr;
}
// coverity[leaked_storage]
template ProxyMappingStats_t broadcastAccFunction(const std::function<ProxyMappingStats_t*()>& fun);
template RemoteLoggerStats_t broadcastAccFunction(const std::function<RemoteLoggerStats_t*()>& fun);
-static int serviceMain(int /* argc */, char* /* argv */[], Logr::log_t log)
+static int initNet(Logr::log_t log)
{
- g_log.setName(g_programname);
- g_log.disableSyslog(::arg().mustDo("disable-syslog"));
- g_log.setTimestamps(::arg().mustDo("log-timestamp"));
- g_regressionTestMode = ::arg().mustDo("devonly-regression-test-mode");
-
- if (!::arg()["logging-facility"].empty()) {
- int val = logFacilityToLOG(::arg().asNum("logging-facility"));
- if (val >= 0)
- g_log.setFacility(val);
- else {
- SLOG(g_log << Logger::Error << "Unknown logging facility " << ::arg().asNum("logging-facility") << endl,
- log->info(Logr::Error, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility"))));
- }
- }
-
- showProductVersion();
-
- g_disthashseed = dns_random(0xffffffff);
-
checkLinuxIPv6Limits(log);
try {
pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
catch (std::exception& e) {
SLOG(g_log << Logger::Error << "Assigning local query addresses: " << e.what(),
log->error(Logr::Error, e.what(), "Unable to assign local query address"));
- exit(99);
+ return 99;
}
if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
if (!SyncRes::s_doIPv6 && !SyncRes::s_doIPv4) {
SLOG(g_log << Logger::Error << "No outgoing addresses configured! Can not continue" << endl,
log->info(Logr::Error, "No outgoing addresses configured! Can not continue"));
- exit(99);
+ return 99;
}
+ return 0;
+}
- // keep this ABOVE loadRecursorLuaConfig!
- if (::arg()["dnssec"] == "off")
+static int initDNSSEC(Logr::log_t log)
+{
+ if (::arg()["dnssec"] == "off") {
g_dnssecmode = DNSSECMode::Off;
- else if (::arg()["dnssec"] == "process-no-validate")
+ }
+ else if (::arg()["dnssec"] == "process-no-validate") {
g_dnssecmode = DNSSECMode::ProcessNoValidate;
- else if (::arg()["dnssec"] == "process")
+ }
+ else if (::arg()["dnssec"] == "process") {
g_dnssecmode = DNSSECMode::Process;
- else if (::arg()["dnssec"] == "validate")
+ }
+ else if (::arg()["dnssec"] == "validate") {
g_dnssecmode = DNSSECMode::ValidateAll;
- else if (::arg()["dnssec"] == "log-fail")
+ }
+ else if (::arg()["dnssec"] == "log-fail") {
g_dnssecmode = DNSSECMode::ValidateForLog;
+ }
else {
SLOG(g_log << Logger::Error << "Unknown DNSSEC mode " << ::arg()["dnssec"] << endl,
log->info(Logr::Error, "Unknown DNSSEC mode", "dnssec", Logging::Loggable(::arg()["dnssec"])));
- exit(1);
+ return 1;
}
g_signatureInceptionSkew = ::arg().asNum("signature-inception-skew");
if (g_signatureInceptionSkew < 0) {
SLOG(g_log << Logger::Error << "A negative value for 'signature-inception-skew' is not allowed" << endl,
log->info(Logr::Error, "A negative value for 'signature-inception-skew' is not allowed"));
- exit(1);
+ return 1;
}
g_dnssecLogBogus = ::arg().mustDo("dnssec-log-bogus");
g_maxNSEC3Iterations = ::arg().asNum("nsec3-max-iterations");
+ return 0;
+}
- g_maxCacheEntries = ::arg().asNum("max-cache-entries");
-
- luaConfigDelayedThreads delayedLuaThreads;
- try {
- ProxyMapping proxyMapping;
- loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads, proxyMapping);
- // Initial proxy mapping
- g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_unique<ProxyMapping>(proxyMapping);
- }
- catch (PDNSException& e) {
- SLOG(g_log << Logger::Error << "Cannot load Lua configuration: " << e.reason << endl,
- log->error(Logr::Error, e.reason, "Cannot load Lua configuration"));
- exit(1);
- }
-
- parseACLs();
- initPublicSuffixList(::arg()["public-suffix-list-file"]);
-
+static void initDontQuery(Logr::log_t log)
+{
if (!::arg()["dont-query"].empty()) {
vector<string> ips;
stringtok(ips, ::arg()["dont-query"], ", ");
- ips.push_back("0.0.0.0");
- ips.push_back("::");
+ ips.emplace_back("0.0.0.0");
+ ips.emplace_back("::");
- for (const auto& ip : ips) {
- SyncRes::addDontQuery(ip);
+ for (const auto& anIP : ips) {
+ SyncRes::addDontQuery(anIP);
}
if (!g_slogStructured) {
g_log << Logger::Warning << "Will not send queries to: ";
- for (vector<string>::const_iterator i = ips.begin(); i != ips.end(); ++i) {
- if (i != ips.begin())
+ for (auto i = ips.begin(); i != ips.end(); ++i) {
+ if (i != ips.begin()) {
g_log << Logger::Warning << ", ";
+ }
g_log << Logger::Warning << *i;
}
g_log << Logger::Warning << endl;
log->info(Logr::Notice, "Will not send queries to", "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
}
}
+}
- /* this needs to be done before parseACLs(), which call broadcastFunction() */
- RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
- if (RecThreadInfo::weDistributeQueries()) {
- SLOG(g_log << Logger::Warning << "PowerDNS Recursor itself will distribute queries over threads" << endl,
- log->info(Logr::Notice, "PowerDNS Recursor itself will distribute queries over threads"));
- }
-
- g_outgoingEDNSBufsize = ::arg().asNum("edns-outgoing-bufsize");
-
- if (::arg()["trace"] == "fail") {
- SyncRes::setDefaultLogMode(SyncRes::Store);
- }
- else if (::arg().mustDo("trace")) {
- SyncRes::setDefaultLogMode(SyncRes::Log);
- ::arg().set("quiet") = "no";
- g_quiet = false;
- }
- auto myHostname = getHostname();
- if (!myHostname.has_value()) {
- SLOG(g_log << Logger::Warning << "Unable to get the hostname, NSID and id.server values will be empty" << endl,
- log->info(Logr::Warning, "Unable to get the hostname, NSID and id.server values will be empty"));
- }
-
+static int initSyncRes(Logr::log_t log, const std::optional<std::string>& myHostname)
+{
SyncRes::s_minimumTTL = ::arg().asNum("minimum-ttl-override");
SyncRes::s_minimumECSTTL = ::arg().asNum("ecs-minimum-ttl-override");
SyncRes::s_maxnegttl = ::arg().asNum("max-negative-ttl");
if (sse > std::numeric_limits<uint16_t>::max()) {
SLOG(g_log << Logger::Error << "Illegal serve-stale-extensions value: " << sse << "; range = 0..65536" << endl,
log->info(Logr::Error, "Illegal serve-stale-extensions value; range = 0..65536", "value", Logging::Loggable(sse)));
- exit(1);
+ return 1;
}
MemRecursorCache::s_maxServedStaleExtensions = sse;
NegCache::s_maxServedStaleExtensions = sse;
else if (value != "dnssec") {
SLOG(g_log << Logger::Error << "Unknown nothing-below-nxdomain mode: " << value << endl,
log->info(Logr::Error, "Unknown nothing-below-nxdomain mode", "mode", Logging::Loggable(value)));
- exit(1);
+ return 1;
}
if (!::arg().isEmpty("ecs-scope-zero-address")) {
SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128));
}
else {
- Netmask nm;
+ Netmask netmask;
bool done = false;
auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET);
- if (addr.sin4.sin_family != 0) {
- nm = Netmask(addr, 32);
+ if (addr.sin4.sin_family != 0) { // NOLINT: union access
+ netmask = Netmask(addr, 32);
done = true;
}
if (!done) {
addr = pdns::getNonAnyQueryLocalAddress(AF_INET6);
- if (addr.sin4.sin_family != 0) {
- nm = Netmask(addr, 128);
+ if (addr.sin4.sin_family != 0) { // NOLINT: union access
+ netmask = Netmask(addr, 128);
done = true;
}
}
if (!done) {
- nm = Netmask(ComboAddress("127.0.0.1"), 32);
+ netmask = Netmask(ComboAddress("127.0.0.1"), 32);
}
- SyncRes::setECSScopeZeroAddress(nm);
+ SyncRes::setECSScopeZeroAddress(netmask);
}
SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]);
SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]);
SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]);
g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet");
+ return 0;
+}
- g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]);
- g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
-
- if (!::arg()["dns64-prefix"].empty()) {
- try {
- auto dns64Prefix = Netmask(::arg()["dns64-prefix"]);
- if (dns64Prefix.getBits() != 96) {
- SLOG(g_log << Logger::Error << "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl,
- log->info(Logr::Error, "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes", "prefix", Logging::Loggable(::arg()["dns64-prefix"])));
- exit(1);
- }
- g_dns64Prefix = dns64Prefix.getNetwork();
- g_dns64PrefixReverse = reverseNameFromIP(*g_dns64Prefix);
- /* /96 is 24 nibbles + 2 for "ip6.arpa." */
- while (g_dns64PrefixReverse.countLabels() > 26) {
- g_dns64PrefixReverse.chopOff();
- }
- }
- catch (const NetmaskException& ne) {
- SLOG(g_log << Logger::Error << "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne.reason << endl,
- log->info(Logr::Error, "Invalid prefix", "dns64-prefix", Logging::Loggable(::arg()["dns64-prefix"])));
- exit(1);
- }
- }
-
- g_networkTimeoutMsec = ::arg().asNum("network-timeout");
-
- std::tie(g_initialDomainMap, g_initialAllowNotifyFor) = parseZoneConfiguration();
-
- g_latencyStatSize = ::arg().asNum("latency-statistic-size");
-
- g_logCommonErrors = ::arg().mustDo("log-common-errors");
- g_logRPZChanges = ::arg().mustDo("log-rpz-changes");
-
- g_anyToTcp = ::arg().mustDo("any-to-tcp");
- g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold");
-
- g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing");
-
- g_paddingFrom.toMasks(::arg()["edns-padding-from"]);
- if (::arg()["edns-padding-mode"] == "always") {
- g_paddingMode = PaddingMode::Always;
- }
- else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
- g_paddingMode = PaddingMode::PaddedQueries;
- }
- else {
- SLOG(g_log << Logger::Error << "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl,
- log->info(Logr::Error, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"])));
- exit(1);
- }
- g_paddingTag = ::arg().asNum("edns-padding-tag");
- g_paddingOutgoing = ::arg().mustDo("edns-padding-out");
-
- RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
- RecThreadInfo::setNumWorkerThreads(::arg().asNum("threads"));
- if (RecThreadInfo::numWorkers() < 1) {
- SLOG(g_log << Logger::Warning << "Asked to run with 0 threads, raising to 1 instead" << endl,
- log->info(Logr::Warning, "Asked to run with 0 threads, raising to 1 instead"));
- RecThreadInfo::setNumWorkerThreads(1);
- }
-
- g_maxMThreads = ::arg().asNum("max-mthreads");
-
- int64_t maxInFlight = ::arg().asNum("max-concurrent-requests-per-tcp-connection");
- if (maxInFlight < 1 || maxInFlight > USHRT_MAX || maxInFlight >= g_maxMThreads) {
- SLOG(g_log << Logger::Warning << "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)" << endl,
- log->info(Logr::Warning, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)"));
- TCPConnection::s_maxInFlight = 10;
- }
- else {
- TCPConnection::s_maxInFlight = maxInFlight;
- }
-
- int64_t millis = ::arg().asNum("tcp-out-max-idle-ms");
- TCPOutConnectionManager::s_maxIdleTime = timeval{millis / 1000, (static_cast<suseconds_t>(millis) % 1000) * 1000};
- TCPOutConnectionManager::s_maxIdlePerAuth = ::arg().asNum("tcp-out-max-idle-per-auth");
- TCPOutConnectionManager::s_maxQueries = ::arg().asNum("tcp-out-max-queries");
- TCPOutConnectionManager::s_maxIdlePerThread = ::arg().asNum("tcp-out-max-idle-per-thread");
-
- g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options");
-
- s_statisticsInterval = ::arg().asNum("statistics-interval");
-
- SyncRes::s_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors");
-
- if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
- if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) {
- g_aggressiveNSECCache = make_unique<AggressiveNSECCache>(::arg().asNum("aggressive-nsec-cache-size"));
- }
- else {
- SLOG(g_log << Logger::Warning << "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring" << endl,
- log->info(Logr::Warning, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring"));
- }
- }
-
- AggressiveNSECCache::s_maxNSEC3CommonPrefix = static_cast<uint8_t>(std::round(std::log2(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio"))));
- SLOG(g_log << Logger::Debug << "NSEC3 aggressive cache tuning: aggressive-cache-min-nsec3-hit-ratio: " << ::arg().asNum("aggressive-cache-min-nsec3-hit-ratio") << " max common prefix bits: " << std::to_string(AggressiveNSECCache::s_maxNSEC3CommonPrefix) << endl,
- log->info(Logr::Debug, "NSEC3 aggressive cache tuning", "aggressive-cache-min-nsec3-hit-ratio", Logging::Loggable(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio")), "maxCommonPrefixBits", Logging::Loggable(AggressiveNSECCache::s_maxNSEC3CommonPrefix)));
-
- {
- SuffixMatchNode dontThrottleNames;
- vector<string> parts;
- stringtok(parts, ::arg()["dont-throttle-names"], " ,");
- for (const auto& p : parts) {
- dontThrottleNames.add(DNSName(p));
- }
- g_dontThrottleNames.setState(std::move(dontThrottleNames));
-
- parts.clear();
- NetmaskGroup dontThrottleNetmasks;
- stringtok(parts, ::arg()["dont-throttle-netmasks"], " ,");
- for (const auto& p : parts) {
- dontThrottleNetmasks.addMask(Netmask(p));
- }
- g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks));
- }
-
- {
- SuffixMatchNode xdnssecNames;
- vector<string> parts;
- stringtok(parts, ::arg()["x-dnssec-names"], " ,");
- for (const auto& p : parts) {
- xdnssecNames.add(DNSName(p));
- }
- g_xdnssec.setState(std::move(xdnssecNames));
- }
-
- {
- SuffixMatchNode dotauthNames;
- vector<string> parts;
- stringtok(parts, ::arg()["dot-to-auth-names"], " ,");
-#ifndef HAVE_DNS_OVER_TLS
- if (parts.size()) {
- SLOG(g_log << Logger::Error << "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored." << endl,
- log->info(Logr::Error, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored"));
- }
-#endif
- for (const auto& p : parts) {
- dotauthNames.add(DNSName(p));
- }
- g_DoTToAuthNames.setState(std::move(dotauthNames));
- }
-
- {
- CarbonConfig config;
- stringtok(config.servers, arg()["carbon-server"], ", ");
- config.hostname = arg()["carbon-ourname"];
- config.instance_name = arg()["carbon-instance"];
- config.namespace_name = arg()["carbon-namespace"];
- g_carbonConfig.setState(std::move(config));
- }
-
+static void initDistribution(Logr::log_t log)
+{
g_balancingFactor = ::arg().asDouble("distribution-load-factor");
if (g_balancingFactor != 0.0 && g_balancingFactor < 1.0) {
g_balancingFactor = 0.0;
}
}
}
+}
-#ifdef NOD_ENABLED
- // Setup newly observed domain globals
- setupNODGlobal();
-#endif /* NOD_ENABLED */
-
- int forks;
- for (forks = 0; forks < ::arg().asNum("processes") - 1; ++forks) {
- if (!fork()) // we are child
+static int initForks(Logr::log_t log)
+{
+ int forks = 0;
+ for (; forks < ::arg().asNum("processes") - 1; ++forks) {
+ if (fork() == 0) { // we are child
break;
+ }
}
if (::arg().mustDo("daemon")) {
g_log.toConsole(Logger::Critical);
daemonize(log);
}
+
if (Utility::getpid() == 1) {
/* We are running as pid 1, register sigterm and sigint handler
signal(SIGUSR1, usr1Handler);
signal(SIGUSR2, usr2Handler);
- signal(SIGPIPE, SIG_IGN);
-
- checkOrFixFDS(log);
+ signal(SIGPIPE, SIG_IGN); // NOLINT: Posix API
+ return forks;
+}
-#ifdef HAVE_LIBSODIUM
- if (sodium_init() == -1) {
- SLOG(g_log << Logger::Error << "Unable to initialize sodium crypto library" << endl,
- log->info(Logr::Error, "Unable to initialize sodium crypto library"));
- exit(99);
+static int initPorts(Logr::log_t log)
+{
+ int port = ::arg().asNum("udp-source-port-min");
+ if (port < 1024 || port > 65535) {
+ SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-min is not a valid port number" << endl,
+ log->info(Logr::Error, "Unable to launch, udp-source-port-min is not a valid port number"));
+ return 99; // this isn't going to fix itself either
}
-#endif
-
- openssl_thread_setup();
- openssl_seed();
- /* setup rng before chroot */
- dns_random_init();
-
- if (::arg()["server-id"].empty()) {
- ::arg().set("server-id") = myHostname.has_value() ? *myHostname : "";
+ g_minUdpSourcePort = port;
+ port = ::arg().asNum("udp-source-port-max");
+ if (port < 1024 || port > 65535 || port < g_minUdpSourcePort) {
+ SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-max is not a valid port number or is smaller than udp-source-port-min" << endl,
+ log->info(Logr::Error, "Unable to launch, udp-source-port-max is not a valid port number or is smaller than udp-source-port-min"));
+ return 99; // this isn't going to fix itself either
}
-
- int newgid = 0;
- if (!::arg()["setgid"].empty())
- newgid = strToGID(::arg()["setgid"]);
- int newuid = 0;
- if (!::arg()["setuid"].empty())
- newuid = strToUID(::arg()["setuid"]);
-
- Utility::dropGroupPrivs(newuid, newgid);
-
- if (!::arg()["chroot"].empty()) {
-#ifdef HAVE_SYSTEMD
+ g_maxUdpSourcePort = port;
+ std::vector<string> parts{};
+ stringtok(parts, ::arg()["udp-source-port-avoid"], ", ");
+ for (const auto& part : parts) {
+ port = std::stoi(part);
+ if (port < 1024 || port > 65535) {
+ SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part << endl,
+ log->info(Logr::Error, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part)));
+ return 99; // this isn't going to fix itself either
+ }
+ g_avoidUdpSourcePorts.insert(port);
+ }
+ return 0;
+}
+
+static void initSNMP([[maybe_unused]] Logr::log_t log)
+{
+ if (::arg().mustDo("snmp-agent")) {
+#ifdef HAVE_NET_SNMP
+ string setting = ::arg()["snmp-daemon-socket"];
+ if (setting.empty()) {
+ setting = ::arg()["snmp-master-socket"];
+ }
+ g_snmpAgent = std::make_shared<RecursorSNMPAgent>("recursor", setting);
+ g_snmpAgent->run();
+#else
+ const std::string msg = "snmp-agent set but SNMP support not compiled in";
+ SLOG(g_log << Logger::Error << msg << endl,
+ log->info(Logr::Error, msg));
+#endif // HAVE_NET_SNMP
+ }
+}
+
+static int initControl(Logr::log_t log, uid_t newuid, int forks) // NOLINT(bugprone-easily-swappable-parameter*) #12791 Remove NOLINT(readability-function-cognitive-complexity) omoerbeek
+{
+ if (!::arg()["chroot"].empty()) {
+#ifdef HAVE_SYSTEMD
char* ns;
ns = getenv("NOTIFY_SOCKET");
if (ns != nullptr) {
SLOG(g_log << Logger::Error << "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'" << endl,
log->info(Logr::Error, "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'"));
- exit(1);
+ return 1;
}
#endif
if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
int err = errno;
- SLOG(g_log << Logger::Error << "Unable to chroot to '" + ::arg()["chroot"] + "': " << strerror(err) << ", exiting" << endl,
+ SLOG(g_log << Logger::Error << "Unable to chroot to '" + ::arg()["chroot"] + "': " << stringerror(err) << ", exiting" << endl,
log->error(Logr::Error, err, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"])));
- exit(1);
- }
- else {
- SLOG(g_log << Logger::Info << "Chrooted to '" << ::arg()["chroot"] << "'" << endl,
- log->info(Logr::Info, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"])));
+ return 1;
}
+ SLOG(g_log << Logger::Info << "Chrooted to '" << ::arg()["chroot"] << "'" << endl,
+ log->info(Logr::Info, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"])));
}
checkSocketDir(log);
g_pidfname = ::arg()["socket-dir"] + "/" + g_programname + ".pid";
- if (!g_pidfname.empty())
+ if (!g_pidfname.empty()) {
unlink(g_pidfname.c_str()); // remove possible old pid file
+ }
writePid(log);
makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks : -1);
SLOG(g_log << Logger::Warning << e.what() << endl,
log->error(Logr::Warning, e.what(), "Could not drop capabilities"));
}
+ return 0;
+}
+
+static void initSuffixMatchNodes([[maybe_unused]] Logr::log_t log)
+{
+ {
+ SuffixMatchNode dontThrottleNames;
+ vector<string> parts;
+ stringtok(parts, ::arg()["dont-throttle-names"], " ,");
+ for (const auto& part : parts) {
+ dontThrottleNames.add(DNSName(part));
+ }
+ g_dontThrottleNames.setState(std::move(dontThrottleNames));
+
+ parts.clear();
+ NetmaskGroup dontThrottleNetmasks;
+ stringtok(parts, ::arg()["dont-throttle-netmasks"], " ,");
+ for (const auto& part : parts) {
+ dontThrottleNetmasks.addMask(Netmask(part));
+ }
+ g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks));
+ }
+
+ {
+ SuffixMatchNode xdnssecNames;
+ vector<string> parts;
+ stringtok(parts, ::arg()["x-dnssec-names"], " ,");
+ for (const auto& part : parts) {
+ xdnssecNames.add(DNSName(part));
+ }
+ g_xdnssec.setState(std::move(xdnssecNames));
+ }
+
+ {
+ SuffixMatchNode dotauthNames;
+ vector<string> parts;
+ stringtok(parts, ::arg()["dot-to-auth-names"], " ,");
+#ifndef HAVE_DNS_OVER_TLS
+ if (parts.size()) {
+ SLOG(g_log << Logger::Error << "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored." << endl,
+ log->info(Logr::Error, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored"));
+ }
+#endif
+ for (const auto& part : parts) {
+ dotauthNames.add(DNSName(part));
+ }
+ g_DoTToAuthNames.setState(std::move(dotauthNames));
+ }
+}
+
+static void initCarbon()
+{
+ CarbonConfig config;
+ stringtok(config.servers, arg()["carbon-server"], ", ");
+ config.hostname = arg()["carbon-ourname"];
+ config.instance_name = arg()["carbon-instance"];
+ config.namespace_name = arg()["carbon-namespace"];
+ g_carbonConfig.setState(std::move(config));
+}
+
+static int initDNS64(Logr::log_t log)
+{
+ if (!::arg()["dns64-prefix"].empty()) {
+ try {
+ auto dns64Prefix = Netmask(::arg()["dns64-prefix"]);
+ if (dns64Prefix.getBits() != 96) {
+ SLOG(g_log << Logger::Error << "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl,
+ log->info(Logr::Error, "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes", "prefix", Logging::Loggable(::arg()["dns64-prefix"])));
+ return 1;
+ }
+ g_dns64Prefix = dns64Prefix.getNetwork();
+ g_dns64PrefixReverse = reverseNameFromIP(*g_dns64Prefix);
+ /* /96 is 24 nibbles + 2 for "ip6.arpa." */
+ while (g_dns64PrefixReverse.countLabels() > 26) {
+ g_dns64PrefixReverse.chopOff();
+ }
+ }
+ catch (const NetmaskException& ne) {
+ SLOG(g_log << Logger::Error << "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne.reason << endl,
+ log->info(Logr::Error, "Invalid prefix", "dns64-prefix", Logging::Loggable(::arg()["dns64-prefix"])));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int serviceMain(Logr::log_t log) // NOLINT(readability-function-cognitive-complexity) #12791 Remove NOLINT(readability-function-cognitive-complexity) omoerbeek
+{
+ g_log.setName(g_programname);
+ g_log.disableSyslog(::arg().mustDo("disable-syslog"));
+ g_log.setTimestamps(::arg().mustDo("log-timestamp"));
+ g_regressionTestMode = ::arg().mustDo("devonly-regression-test-mode");
+
+ if (!::arg()["logging-facility"].empty()) {
+ int val = logFacilityToLOG(::arg().asNum("logging-facility"));
+ if (val >= 0) {
+ g_log.setFacility(val);
+ }
+ else {
+ SLOG(g_log << Logger::Error << "Unknown logging facility " << ::arg().asNum("logging-facility") << endl,
+ log->info(Logr::Error, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility"))));
+ }
+ }
+
+ showProductVersion();
+
+ g_disthashseed = dns_random(0xffffffff);
+
+ int ret = initNet(log);
+ if (ret != 0) {
+ return ret;
+ }
+ // keep this ABOVE loadRecursorLuaConfig!
+ ret = initDNSSEC(log);
+ if (ret != 0) {
+ return ret;
+ }
+ g_maxCacheEntries = ::arg().asNum("max-cache-entries");
+
+ luaConfigDelayedThreads delayedLuaThreads;
+ try {
+ ProxyMapping proxyMapping;
+ loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads, proxyMapping);
+ // Initial proxy mapping
+ g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_unique<ProxyMapping>(proxyMapping);
+ }
+ catch (PDNSException& e) {
+ SLOG(g_log << Logger::Error << "Cannot load Lua configuration: " << e.reason << endl,
+ log->error(Logr::Error, e.reason, "Cannot load Lua configuration"));
+ return 1;
+ }
+
+ parseACLs();
+ initPublicSuffixList(::arg()["public-suffix-list-file"]);
+
+ initDontQuery(log);
+
+ RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
+ if (RecThreadInfo::weDistributeQueries()) {
+ SLOG(g_log << Logger::Warning << "PowerDNS Recursor itself will distribute queries over threads" << endl,
+ log->info(Logr::Notice, "PowerDNS Recursor itself will distribute queries over threads"));
+ }
+
+ g_outgoingEDNSBufsize = ::arg().asNum("edns-outgoing-bufsize");
+
+ if (::arg()["trace"] == "fail") {
+ SyncRes::setDefaultLogMode(SyncRes::Store);
+ }
+ else if (::arg().mustDo("trace")) {
+ SyncRes::setDefaultLogMode(SyncRes::Log);
+ ::arg().set("quiet") = "no";
+ g_quiet = false;
+ }
+ auto myHostname = getHostname();
+ if (!myHostname.has_value()) {
+ SLOG(g_log << Logger::Warning << "Unable to get the hostname, NSID and id.server values will be empty" << endl,
+ log->info(Logr::Warning, "Unable to get the hostname, NSID and id.server values will be empty"));
+ }
+
+ ret = initSyncRes(log, myHostname);
+ if (ret != 0) {
+ return ret;
+ }
+
+ g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]);
+ g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
+
+ ret = initDNS64(log);
+ if (ret != 0) {
+ return ret;
+ }
+ g_networkTimeoutMsec = ::arg().asNum("network-timeout");
+
+ std::tie(g_initialDomainMap, g_initialAllowNotifyFor) = parseZoneConfiguration();
+
+ g_latencyStatSize = ::arg().asNum("latency-statistic-size");
+
+ g_logCommonErrors = ::arg().mustDo("log-common-errors");
+ g_logRPZChanges = ::arg().mustDo("log-rpz-changes");
+
+ g_anyToTcp = ::arg().mustDo("any-to-tcp");
+ g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold");
+
+ g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing");
+
+ g_paddingFrom.toMasks(::arg()["edns-padding-from"]);
+ if (::arg()["edns-padding-mode"] == "always") {
+ g_paddingMode = PaddingMode::Always;
+ }
+ else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
+ g_paddingMode = PaddingMode::PaddedQueries;
+ }
+ else {
+ SLOG(g_log << Logger::Error << "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl,
+ log->info(Logr::Error, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"])));
+ return 1;
+ }
+ g_paddingTag = ::arg().asNum("edns-padding-tag");
+ g_paddingOutgoing = ::arg().mustDo("edns-padding-out");
+
+ RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
+ RecThreadInfo::setNumWorkerThreads(::arg().asNum("threads"));
+ if (RecThreadInfo::numWorkers() < 1) {
+ SLOG(g_log << Logger::Warning << "Asked to run with 0 threads, raising to 1 instead" << endl,
+ log->info(Logr::Warning, "Asked to run with 0 threads, raising to 1 instead"));
+ RecThreadInfo::setNumWorkerThreads(1);
+ }
+
+ g_maxMThreads = ::arg().asNum("max-mthreads");
+
+ int64_t maxInFlight = ::arg().asNum("max-concurrent-requests-per-tcp-connection");
+ if (maxInFlight < 1 || maxInFlight > USHRT_MAX || maxInFlight >= g_maxMThreads) {
+ SLOG(g_log << Logger::Warning << "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)" << endl,
+ log->info(Logr::Warning, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)"));
+ TCPConnection::s_maxInFlight = 10;
+ }
+ else {
+ TCPConnection::s_maxInFlight = maxInFlight;
+ }
+
+ int64_t millis = ::arg().asNum("tcp-out-max-idle-ms");
+ TCPOutConnectionManager::s_maxIdleTime = timeval{millis / 1000, (static_cast<suseconds_t>(millis) % 1000) * 1000};
+ TCPOutConnectionManager::s_maxIdlePerAuth = ::arg().asNum("tcp-out-max-idle-per-auth");
+ TCPOutConnectionManager::s_maxQueries = ::arg().asNum("tcp-out-max-queries");
+ TCPOutConnectionManager::s_maxIdlePerThread = ::arg().asNum("tcp-out-max-idle-per-thread");
+
+ g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options");
+
+ s_statisticsInterval = ::arg().asNum("statistics-interval");
+
+ SyncRes::s_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors");
+
+ if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
+ if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) {
+ g_aggressiveNSECCache = make_unique<AggressiveNSECCache>(::arg().asNum("aggressive-nsec-cache-size"));
+ }
+ else {
+ SLOG(g_log << Logger::Warning << "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring" << endl,
+ log->info(Logr::Warning, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring"));
+ }
+ }
+
+ AggressiveNSECCache::s_maxNSEC3CommonPrefix = static_cast<uint8_t>(std::round(std::log2(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio"))));
+ SLOG(g_log << Logger::Debug << "NSEC3 aggressive cache tuning: aggressive-cache-min-nsec3-hit-ratio: " << ::arg().asNum("aggressive-cache-min-nsec3-hit-ratio") << " max common prefix bits: " << std::to_string(AggressiveNSECCache::s_maxNSEC3CommonPrefix) << endl,
+ log->info(Logr::Debug, "NSEC3 aggressive cache tuning", "aggressive-cache-min-nsec3-hit-ratio", Logging::Loggable(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio")), "maxCommonPrefixBits", Logging::Loggable(AggressiveNSECCache::s_maxNSEC3CommonPrefix)));
+
+ initSuffixMatchNodes(log);
+ initCarbon();
+ initDistribution(log);
+
+#ifdef NOD_ENABLED
+ // Setup newly observed domain globals
+ setupNODGlobal();
+#endif /* NOD_ENABLED */
+
+ auto forks = initForks(log);
+
+ checkOrFixFDS(log);
+
+#ifdef HAVE_LIBSODIUM
+ if (sodium_init() == -1) {
+ SLOG(g_log << Logger::Error << "Unable to initialize sodium crypto library" << endl,
+ log->info(Logr::Error, "Unable to initialize sodium crypto library"));
+ return 99;
+ }
+#endif
+
+ openssl_thread_setup();
+ openssl_seed();
+ /* setup rng before chroot */
+ dns_random_init();
+
+ if (::arg()["server-id"].empty()) {
+ ::arg().set("server-id") = myHostname.has_value() ? *myHostname : "";
+ }
+
+ gid_t newgid = 0;
+ if (!::arg()["setgid"].empty()) {
+ newgid = strToGID(::arg()["setgid"]);
+ }
+ uid_t newuid = 0;
+ if (!::arg()["setuid"].empty()) {
+ newuid = strToUID(::arg()["setuid"]);
+ }
+
+ Utility::dropGroupPrivs(newuid, newgid);
+
+ ret = initControl(log, newuid, forks);
+ if (ret != 0) {
+ return ret;
+ }
startLuaConfigDelayedThreads(delayedLuaThreads, g_luaconfs.getCopy().generation);
delayedLuaThreads.rpzPrimaryThreads.clear(); // no longer needed
disableStats(StatComponent::API, ::arg()["stats-api-disabled-list"]);
disableStats(StatComponent::Carbon, ::arg()["stats-carbon-disabled-list"]);
disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-disabled-list"]);
- disableStats(StatComponent::SNMP, ::arg()["stats-snmp-disabled-list"]);
-
- // Run before any thread doing stats related things
- registerAllStats();
-
- if (::arg().mustDo("snmp-agent")) {
-#ifdef HAVE_NET_SNMP
- string setting = ::arg()["snmp-daemon-socket"];
- if (setting.empty()) {
- setting = ::arg()["snmp-master-socket"];
- }
- g_snmpAgent = std::make_shared<RecursorSNMPAgent>("recursor", setting);
- g_snmpAgent->run();
-#else
- const std::string msg = "snmp-agent set but SNMP support not compiled in";
- SLOG(g_log << Logger::Error << msg << endl,
- log->info(Logr::Error, msg));
-#endif // HAVE_NET_SNMP
- }
+ disableStats(StatComponent::SNMP, ::arg()["stats-snmp-disabled-list"]);
- int port = ::arg().asNum("udp-source-port-min");
- if (port < 1024 || port > 65535) {
- SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-min is not a valid port number" << endl,
- log->info(Logr::Error, "Unable to launch, udp-source-port-min is not a valid port number"));
- exit(99); // this isn't going to fix itself either
- }
- g_minUdpSourcePort = port;
- port = ::arg().asNum("udp-source-port-max");
- if (port < 1024 || port > 65535 || port < g_minUdpSourcePort) {
- SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-max is not a valid port number or is smaller than udp-source-port-min" << endl,
- log->info(Logr::Error, "Unable to launch, udp-source-port-max is not a valid port number or is smaller than udp-source-port-min"));
- exit(99); // this isn't going to fix itself either
- }
- g_maxUdpSourcePort = port;
- std::vector<string> parts{};
- stringtok(parts, ::arg()["udp-source-port-avoid"], ", ");
- for (const auto& part : parts) {
- port = std::stoi(part);
- if (port < 1024 || port > 65535) {
- SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part << endl,
- log->info(Logr::Error, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part)));
- exit(99); // this isn't going to fix itself either
- }
- g_avoidUdpSourcePorts.insert(port);
+ // Run before any thread doing stats related things
+ registerAllStats();
+
+ initSNMP(log);
+
+ ret = initPorts(log);
+ if (ret != 0) {
+ return ret;
}
return RecThreadInfo::runThreads(log);
}
-static void handlePipeRequest(int fd, FDMultiplexer::funcparam_t& /* var */)
+static void handlePipeRequest(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
{
ThreadMSG* tmsg = nullptr;
- if (read(fd, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread || fd == readQueriesToThread
+ if (read(fileDesc, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread || fd == readQueriesToThread NOLINT: sizeof correct
unixDie("read from thread pipe returned wrong size or error");
}
- void* resp = 0;
+ void* resp = nullptr;
try {
resp = tmsg->func();
}
}
if (tmsg->wantAnswer) {
if (write(RecThreadInfo::self().pipes.writeFromThread, &resp, sizeof(resp)) != sizeof(resp)) {
- delete tmsg;
+ delete tmsg; // NOLINT: manual ownership handling
unixDie("write to thread pipe returned wrong size or error");
}
}
- delete tmsg;
+ delete tmsg; // NOLINT: manual ownership handling
}
static void handleRCC(int fd, FDMultiplexer::funcparam_t& /* var */)
const string name;
};
-static void houseKeeping(void*)
+static void houseKeepingWork(Logr::log_t log) // NOLINT(readability-function-cognitive-complexity) #12791 Remove NOLINT(readability-function-cognitive-complexity) omoerbeek
{
- auto log = g_slog->withName("housekeeping");
- static thread_local bool t_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
-
- try {
- if (t_running) {
- return;
- }
- t_running = true;
-
- struct timeval now;
- Utility::gettimeofday(&now);
- t_Counters.updateSnap(now, g_regressionTestMode);
+ struct timeval now
+ {
+ };
+ Utility::gettimeofday(&now);
+ t_Counters.updateSnap(now, g_regressionTestMode);
- // Below are the tasks that run for every recursorThread, including handler and taskThread
+ // Below are the tasks that run for every recursorThread, including handler and taskThread
- static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5};
- pruneTCPTask.runIfDue(now, [now]() {
- t_tcp_manager.cleanup(now);
- });
+ static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5};
+ pruneTCPTask.runIfDue(now, [now]() {
+ t_tcp_manager.cleanup(now);
+ });
- const auto& info = RecThreadInfo::self();
+ const auto& info = RecThreadInfo::self();
- // Threads handling packets process config changes in the input path, but not all threads process input packets
- // distr threads only process TCP, so that may not happenn very often. So do all periodically.
- static thread_local PeriodicTask exportConfigTask{"exportConfigTask", 30};
- auto luaconfsLocal = g_luaconfs.getLocal();
- exportConfigTask.runIfDue(now, [&luaconfsLocal]() {
- checkProtobufExport(luaconfsLocal);
- checkOutgoingProtobufExport(luaconfsLocal);
+ // Threads handling packets process config changes in the input path, but not all threads process input packets
+ // distr threads only process TCP, so that may not happenn very often. So do all periodically.
+ static thread_local PeriodicTask exportConfigTask{"exportConfigTask", 30};
+ auto luaconfsLocal = g_luaconfs.getLocal();
+ exportConfigTask.runIfDue(now, [&luaconfsLocal]() {
+ checkProtobufExport(luaconfsLocal);
+ checkOutgoingProtobufExport(luaconfsLocal);
#ifdef HAVE_FSTRM
- checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
- checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
+ checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
+ checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
#endif
- });
+ });
- // Below are the thread specific tasks for the handler and the taskThread
- // Likley a few handler tasks could be moved to the taskThread
- if (info.isTaskThread()) {
- // TaskQueue is run always
- runTasks(10, g_logCommonErrors);
-
- static PeriodicTask ztcTask{"ZTC", 60};
- static map<DNSName, RecZoneToCache::State> ztcStates;
- ztcTask.runIfDue(now, [&luaconfsLocal]() {
- RecZoneToCache::maintainStates(luaconfsLocal->ztcConfigs, ztcStates, luaconfsLocal->generation);
- for (auto& ztc : luaconfsLocal->ztcConfigs) {
- RecZoneToCache::ZoneToCache(ztc.second, ztcStates.at(ztc.first));
- }
- });
- }
- else if (info.isHandler()) {
- if (g_packetCache) {
- static PeriodicTask packetCacheTask{"packetCacheTask", 5};
- packetCacheTask.runIfDue(now, []() {
- g_packetCache->doPruneTo(g_maxPacketCacheEntries);
- });
+ // Below are the thread specific tasks for the handler and the taskThread
+ // Likley a few handler tasks could be moved to the taskThread
+ if (info.isTaskThread()) {
+ // TaskQueue is run always
+ runTasks(10, g_logCommonErrors);
+
+ static PeriodicTask ztcTask{"ZTC", 60};
+ static map<DNSName, RecZoneToCache::State> ztcStates;
+ ztcTask.runIfDue(now, [&luaconfsLocal]() {
+ RecZoneToCache::maintainStates(luaconfsLocal->ztcConfigs, ztcStates, luaconfsLocal->generation);
+ for (const auto& ztc : luaconfsLocal->ztcConfigs) {
+ RecZoneToCache::ZoneToCache(ztc.second, ztcStates.at(ztc.first));
}
- static PeriodicTask recordCachePruneTask{"RecordCachePruneTask", 5};
- recordCachePruneTask.runIfDue(now, []() {
- g_recCache->doPrune(g_maxCacheEntries);
+ });
+ }
+ else if (info.isHandler()) {
+ if (g_packetCache) {
+ static PeriodicTask packetCacheTask{"packetCacheTask", 5};
+ packetCacheTask.runIfDue(now, []() {
+ g_packetCache->doPruneTo(g_maxPacketCacheEntries);
});
+ }
+ static PeriodicTask recordCachePruneTask{"RecordCachePruneTask", 5};
+ recordCachePruneTask.runIfDue(now, []() {
+ g_recCache->doPrune(g_maxCacheEntries);
+ });
- static PeriodicTask negCachePruneTask{"NegCachePrunteTask", 5};
- negCachePruneTask.runIfDue(now, []() {
- g_negCache->prune(g_maxCacheEntries / 8);
- });
+ static PeriodicTask negCachePruneTask{"NegCachePrunteTask", 5};
+ negCachePruneTask.runIfDue(now, []() {
+ g_negCache->prune(g_maxCacheEntries / 8);
+ });
- static PeriodicTask aggrNSECPruneTask{"AggrNSECPruneTask", 5};
- aggrNSECPruneTask.runIfDue(now, [now]() {
- if (g_aggressiveNSECCache) {
- g_aggressiveNSECCache->prune(now.tv_sec);
- }
- });
+ static PeriodicTask aggrNSECPruneTask{"AggrNSECPruneTask", 5};
+ aggrNSECPruneTask.runIfDue(now, [now]() {
+ if (g_aggressiveNSECCache) {
+ g_aggressiveNSECCache->prune(now.tv_sec);
+ }
+ });
- static PeriodicTask pruneNSpeedTask{"pruneNSSpeedTask", 30};
- pruneNSpeedTask.runIfDue(now, [now]() {
- SyncRes::pruneNSSpeeds(now.tv_sec - 300);
- });
+ static PeriodicTask pruneNSpeedTask{"pruneNSSpeedTask", 30};
+ pruneNSpeedTask.runIfDue(now, [now]() {
+ SyncRes::pruneNSSpeeds(now.tv_sec - 300);
+ });
+
+ static PeriodicTask pruneEDNSTask{"pruneEDNSTask", 60};
+ pruneEDNSTask.runIfDue(now, [now]() {
+ SyncRes::pruneEDNSStatuses(now.tv_sec);
+ });
- static PeriodicTask pruneEDNSTask{"pruneEDNSTask", 60};
- pruneEDNSTask.runIfDue(now, [now]() {
- SyncRes::pruneEDNSStatuses(now.tv_sec);
+ if (SyncRes::s_max_busy_dot_probes > 0) {
+ static PeriodicTask pruneDoTProbeMap{"pruneDoTProbeMapTask", 60};
+ pruneDoTProbeMap.runIfDue(now, [now]() {
+ SyncRes::pruneDoTProbeMap(now.tv_sec);
});
+ }
- if (SyncRes::s_max_busy_dot_probes > 0) {
- static PeriodicTask pruneDoTProbeMap{"pruneDoTProbeMapTask", 60};
- pruneDoTProbeMap.runIfDue(now, [now]() {
- SyncRes::pruneDoTProbeMap(now.tv_sec);
- });
- }
+ static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
+ pruneThrottledTask.runIfDue(now, [now]() {
+ SyncRes::pruneThrottledServers(now.tv_sec);
+ });
- static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
- pruneThrottledTask.runIfDue(now, [now]() {
- SyncRes::pruneThrottledServers(now.tv_sec);
- });
+ static PeriodicTask pruneFailedServersTask{"pruneFailedServerTask", 5};
+ pruneFailedServersTask.runIfDue(now, [now]() {
+ SyncRes::pruneFailedServers(now.tv_sec - static_cast<time_t>(SyncRes::s_serverdownthrottletime * 10));
+ });
- static PeriodicTask pruneFailedServersTask{"pruneFailedServerTask", 5};
- pruneFailedServersTask.runIfDue(now, [now]() {
- SyncRes::pruneFailedServers(now.tv_sec - SyncRes::s_serverdownthrottletime * 10);
- });
+ static PeriodicTask pruneNonResolvingTask{"pruneNonResolvingTask", 5};
+ pruneNonResolvingTask.runIfDue(now, [now]() {
+ SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime);
+ });
- static PeriodicTask pruneNonResolvingTask{"pruneNonResolvingTask", 5};
- pruneNonResolvingTask.runIfDue(now, [now]() {
- SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime);
- });
+ static PeriodicTask pruneSaveParentSetTask{"pruneSaveParentSetTask", 60};
+ pruneSaveParentSetTask.runIfDue(now, [now]() {
+ SyncRes::pruneSaveParentsNSSets(now.tv_sec);
+ });
- static PeriodicTask pruneSaveParentSetTask{"pruneSaveParentSetTask", 60};
- pruneSaveParentSetTask.runIfDue(now, [now]() {
- SyncRes::pruneSaveParentsNSSets(now.tv_sec);
- });
+ // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s
+ const unsigned int minRootRefreshInterval = 10;
+ static PeriodicTask rootUpdateTask{"rootUpdateTask", std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval)};
+ rootUpdateTask.runIfDue(now, [now, &log, minRootRefreshInterval]() {
+ int res = 0;
+ if (!g_regressionTestMode) {
+ res = SyncRes::getRootNS(now, nullptr, 0, log);
+ }
+ if (res == 0) {
+ // Success, go back to the defaut period
+ rootUpdateTask.setPeriod(std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval));
+ }
+ else {
+ // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each
+ // failure by dividing the existing interval by 8, keeping the minimum interval at 10s.
+ // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ...
+ rootUpdateTask.setPeriod(std::max<time_t>(rootUpdateTask.getPeriod() / 8, minRootRefreshInterval));
+ }
+ });
- // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s
- const unsigned int minRootRefreshInterval = 10;
- static PeriodicTask rootUpdateTask{"rootUpdateTask", std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval)};
- rootUpdateTask.runIfDue(now, [now, &log, minRootRefreshInterval]() {
- int res = 0;
- if (!g_regressionTestMode) {
- res = SyncRes::getRootNS(now, nullptr, 0, log);
- }
- if (res == 0) {
- // Success, go back to the defaut period
- rootUpdateTask.setPeriod(std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval));
- }
- else {
- // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each
- // failure by dividing the existing interval by 8, keeping the minimum interval at 10s.
- // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ...
- rootUpdateTask.setPeriod(std::max<time_t>(rootUpdateTask.getPeriod() / 8, minRootRefreshInterval));
- }
- });
+ static PeriodicTask secpollTask{"secpollTask", 3600};
+ static time_t t_last_secpoll;
+ secpollTask.runIfDue(now, [&log]() {
+ try {
+ doSecPoll(&t_last_secpoll, log);
+ }
+ catch (const std::exception& e) {
+ SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.what() << endl,
+ log->error(Logr::Error, e.what(), "Exception while performing security poll"));
+ }
+ catch (const PDNSException& e) {
+ SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
+ log->error(Logr::Error, e.reason, "Exception while performing security poll"));
+ }
+ catch (const ImmediateServFailException& e) {
+ SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
+ log->error(Logr::Error, e.reason, "Exception while performing security poll"));
+ }
+ catch (const PolicyHitException& e) {
+ SLOG(g_log << Logger::Error << "Policy hit while performing security poll" << endl,
+ log->info(Logr::Error, "Policy hit while performing security poll"));
+ }
+ catch (...) {
+ SLOG(g_log << Logger::Error << "Exception while performing security poll" << endl,
+ log->info(Logr::Error, "Exception while performing security poll"));
+ }
+ });
- static PeriodicTask secpollTask{"secpollTask", 3600};
- static time_t t_last_secpoll;
- secpollTask.runIfDue(now, [&log]() {
+ const time_t taInterval = std::max(1, static_cast<int>(luaconfsLocal->trustAnchorFileInfo.interval) * 3600);
+ static PeriodicTask trustAnchorTask{"trustAnchorTask", taInterval};
+ if (!trustAnchorTask.hasRun()) {
+ // Loading the Lua config file already "refreshed" the TAs
+ trustAnchorTask.updateLastRun();
+ }
+ // interval might have ben updated
+ trustAnchorTask.setPeriod(taInterval);
+ trustAnchorTask.runIfDue(now, [&luaconfsLocal, &log]() {
+ if (!luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) {
+ SLOG(g_log << Logger::Debug << "Refreshing Trust Anchors from file" << endl,
+ log->info(Logr::Debug, "Refreshing Trust Anchors from file"));
try {
- doSecPoll(&t_last_secpoll, log);
- }
- catch (const std::exception& e) {
- SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.what() << endl,
- log->error(Logr::Error, e.what(), "Exception while performing security poll"));
- }
- catch (const PDNSException& e) {
- SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
- log->error(Logr::Error, e.reason, "Exception while performing security poll"));
- }
- catch (const ImmediateServFailException& e) {
- SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
- log->error(Logr::Error, e.reason, "Exception while performing security poll"));
- }
- catch (const PolicyHitException& e) {
- SLOG(g_log << Logger::Error << "Policy hit while performing security poll" << endl,
- log->info(Logr::Error, "Policy hit while performing security poll"));
+ map<DNSName, dsmap_t> dsAnchors;
+ if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors, log)) {
+ g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) {
+ lci.dsAnchors = dsAnchors;
+ });
+ }
}
- catch (...) {
- SLOG(g_log << Logger::Error << "Exception while performing security poll" << endl,
- log->info(Logr::Error, "Exception while performing security poll"));
+ catch (const PDNSException& pe) {
+ SLOG(g_log << Logger::Error << "Unable to update Trust Anchors: " << pe.reason << endl,
+ log->error(Logr::Error, pe.reason, "Unable to update Trust Anchors"));
}
- });
-
- static PeriodicTask trustAnchorTask{"trustAnchorTask", std::max(1U, luaconfsLocal->trustAnchorFileInfo.interval) * 3600};
- if (!trustAnchorTask.hasRun()) {
- // Loading the Lua config file already "refreshed" the TAs
- trustAnchorTask.updateLastRun();
}
- // interval might have ben updated
- trustAnchorTask.setPeriod(std::max(1U, luaconfsLocal->trustAnchorFileInfo.interval) * 3600);
- trustAnchorTask.runIfDue(now, [&luaconfsLocal, &log]() {
- if (!luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) {
- SLOG(g_log << Logger::Debug << "Refreshing Trust Anchors from file" << endl,
- log->info(Logr::Debug, "Refreshing Trust Anchors from file"));
- try {
- map<DNSName, dsmap_t> dsAnchors;
- if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors, log)) {
- g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) {
- lci.dsAnchors = dsAnchors;
- });
- }
- }
- catch (const PDNSException& pe) {
- SLOG(g_log << Logger::Error << "Unable to update Trust Anchors: " << pe.reason << endl,
- log->error(Logr::Error, pe.reason, "Unable to update Trust Anchors"));
- }
- }
- });
+ });
+ }
+ t_Counters.updateSnap(g_regressionTestMode);
+}
+
+static void houseKeeping(void* /* ignored */)
+{
+ auto log = g_slog->withName("housekeeping");
+ static thread_local bool t_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
+
+ try {
+ if (t_running) {
+ return;
}
- t_Counters.updateSnap(g_regressionTestMode);
+ t_running = true;
+ houseKeepingWork(log);
t_running = false;
}
catch (const PDNSException& ae) {
}
}
-static void recursorThread()
+static void runLuaMaintenance(RecThreadInfo& threadInfo, time_t& last_lua_maintenance, time_t luaMaintenanceInterval)
+{
+ if (t_pdl != nullptr) {
+ // lua-dns-script directive is present, call the maintenance callback if needed
+ /* remember that the listener threads handle TCP queries */
+ if (threadInfo.isWorker() || threadInfo.isListener()) {
+ // Only on threads processing queries
+ if (g_now.tv_sec - last_lua_maintenance >= luaMaintenanceInterval) {
+ struct timeval start
+ {
+ };
+ Utility::gettimeofday(&start);
+ t_pdl->maintenance();
+ last_lua_maintenance = g_now.tv_sec;
+ struct timeval stop
+ {
+ };
+ Utility::gettimeofday(&stop);
+ t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
+ ++t_Counters.at(rec::Counter::maintenanceCalls);
+ }
+ }
+ }
+}
+
+static void runTCPMaintenance(RecThreadInfo& threadInfo, bool& listenOnTCP, unsigned int maxTcpClients)
+{
+ if (threadInfo.isListener()) {
+ if (listenOnTCP) {
+ if (TCPConnection::getCurrentConnections() > maxTcpClients) { // shutdown, too many connections
+ for (const auto fileDesc : threadInfo.tcpSockets) {
+ t_fdm->removeReadFD(fileDesc);
+ }
+ listenOnTCP = false;
+ }
+ }
+ else {
+ if (TCPConnection::getCurrentConnections() <= maxTcpClients) { // reenable
+ for (const auto fileDesc : threadInfo.tcpSockets) {
+ t_fdm->addReadFD(fileDesc, handleNewTCPQuestion);
+ }
+ listenOnTCP = true;
+ }
+ }
+ }
+}
+
+static void recLoop()
+{
+ unsigned int maxTcpClients = ::arg().asNum("max-tcp-clients");
+ bool listenOnTCP{true};
+ time_t last_stat = 0;
+ time_t last_carbon = 0;
+ time_t last_lua_maintenance = 0;
+ time_t carbonInterval = ::arg().asNum("carbon-interval");
+ time_t luaMaintenanceInterval = ::arg().asNum("lua-maintenance-interval");
+
+ auto& threadInfo = RecThreadInfo::self();
+
+ while (!RecursorControlChannel::stop) {
+ while (MT->schedule(&g_now)) {
+ ; // MTasker letting the mthreads do their thing
+ }
+
+ // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
+ // We want to call handler thread often, it gets scheduled about 2 times per second
+ if (((threadInfo.isHandler() || threadInfo.isTaskThread()) && s_counter % 11 == 0) || s_counter % 499 == 0) {
+ struct timeval start
+ {
+ };
+ Utility::gettimeofday(&start);
+ MT->makeThread(houseKeeping, nullptr);
+ if (!threadInfo.isTaskThread()) {
+ struct timeval stop
+ {
+ };
+ Utility::gettimeofday(&stop);
+ t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
+ ++t_Counters.at(rec::Counter::maintenanceCalls);
+ }
+ }
+
+ if (s_counter % 55 == 0) {
+ auto expired = t_fdm->getTimeouts(g_now);
+
+ for (const auto& exp : expired) {
+ auto conn = boost::any_cast<shared_ptr<TCPConnection>>(exp.second);
+ if (g_logCommonErrors) {
+ SLOG(g_log << Logger::Warning << "Timeout from remote TCP client " << conn->d_remote.toStringWithPort() << endl, // NOLINT: union access
+ g_slogtcpin->info(Logr::Warning, "Timeout from remote TCP client", "remote", Logging::Loggable(conn->d_remote)));
+ }
+ t_fdm->removeReadFD(exp.first);
+ }
+ }
+
+ s_counter++;
+
+ if (threadInfo.isHandler()) {
+ if (statsWanted || (s_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= s_statisticsInterval)) {
+ doStats();
+ last_stat = g_now.tv_sec;
+ }
+
+ Utility::gettimeofday(&g_now, nullptr);
+
+ if ((g_now.tv_sec - last_carbon) >= carbonInterval) {
+ MT->makeThread(doCarbonDump, nullptr);
+ last_carbon = g_now.tv_sec;
+ }
+ }
+ runLuaMaintenance(threadInfo, last_lua_maintenance, luaMaintenanceInterval);
+
+ t_fdm->run(&g_now);
+ // 'run' updates g_now for us
+
+ runTCPMaintenance(threadInfo, listenOnTCP, maxTcpClients);
+ }
+}
+
+static void recursorThread() // NOLINT(readability-function-cognitive-complexity) #12791 Remove NOLINT(readability-function-cognitive-complexity) omoerbeek
{
auto log = g_slog->withName("runtime");
t_Counters.updateSnap(true);
if (threadInfo.isHandler()) {
if (!primeHints()) {
threadInfo.setExitCode(EXIT_FAILURE);
- RecursorControlChannel::stop = 1;
+ RecursorControlChannel::stop = true;
SLOG(g_log << Logger::Critical << "Priming cache failed, stopping" << endl,
log->info(Logr::Critical, "Priming cache failed, stopping"));
}
}
#ifdef NOD_ENABLED
- if (threadInfo.isWorker())
+ if (threadInfo.isWorker()) {
setupNODThread(log);
+ }
#endif /* NOD_ENABLED */
/* the listener threads handle TCP queries */
}
unsigned int ringsize = ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numWorkers();
- if (ringsize) {
+ if (ringsize != 0) {
t_remotes = std::make_unique<addrringbuf_t>();
- if (RecThreadInfo::weDistributeQueries())
+ if (RecThreadInfo::weDistributeQueries()) {
t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors());
- else
+ }
+ else {
t_remotes->set_capacity(ringsize);
+ }
t_servfailremotes = std::make_unique<addrringbuf_t>();
t_servfailremotes->set_capacity(ringsize);
t_bogusremotes = std::make_unique<addrringbuf_t>();
t_fdm->addReadFD(g_rcc.d_fd, handleRCC); // control channel
}
- unsigned int maxTcpClients = ::arg().asNum("max-tcp-clients");
-
- bool listenOnTCP{true};
-
- time_t last_stat = 0;
- time_t last_carbon = 0, last_lua_maintenance = 0;
- time_t carbonInterval = ::arg().asNum("carbon-interval");
- time_t luaMaintenanceInterval = ::arg().asNum("lua-maintenance-interval");
-
#ifdef HAVE_SYSTEMD
if (threadInfo.isHandler()) {
// There is a race, as some threads might not be ready yet to do work.
sd_notify(0, "READY=1");
}
#endif
- while (!RecursorControlChannel::stop) {
- while (MT->schedule(&g_now))
- ; // MTasker letting the mthreads do their thing
-
- // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
- // We want to call handler thread often, it gets scheduled about 2 times per second
- if (((threadInfo.isHandler() || threadInfo.isTaskThread()) && s_counter % 11 == 0) || s_counter % 499 == 0) {
- struct timeval start;
- Utility::gettimeofday(&start);
- MT->makeThread(houseKeeping, nullptr);
- if (!threadInfo.isTaskThread()) {
- struct timeval stop;
- Utility::gettimeofday(&stop);
- t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
- ++t_Counters.at(rec::Counter::maintenanceCalls);
- }
- }
-
- if (!(s_counter % 55)) {
- typedef vector<pair<int, FDMultiplexer::funcparam_t>> expired_t;
- expired_t expired = t_fdm->getTimeouts(g_now);
-
- for (expired_t::iterator i = expired.begin(); i != expired.end(); ++i) {
- shared_ptr<TCPConnection> conn = boost::any_cast<shared_ptr<TCPConnection>>(i->second);
- if (g_logCommonErrors)
- SLOG(g_log << Logger::Warning << "Timeout from remote TCP client " << conn->d_remote.toStringWithPort() << endl,
- g_slogtcpin->info(Logr::Warning, "Timeout from remote TCP client", "remote", Logging::Loggable(conn->d_remote)));
- t_fdm->removeReadFD(i->first);
- }
- }
-
- s_counter++;
-
- if (threadInfo.isHandler()) {
- if (statsWanted || (s_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= s_statisticsInterval)) {
- doStats();
- last_stat = g_now.tv_sec;
- }
-
- Utility::gettimeofday(&g_now, nullptr);
- if ((g_now.tv_sec - last_carbon) >= carbonInterval) {
- MT->makeThread(doCarbonDump, 0);
- last_carbon = g_now.tv_sec;
- }
- }
- if (t_pdl != nullptr) {
- // lua-dns-script directive is present, call the maintenance callback if needed
- /* remember that the listener threads handle TCP queries */
- if (threadInfo.isWorker() || threadInfo.isListener()) {
- // Only on threads processing queries
- if (g_now.tv_sec - last_lua_maintenance >= luaMaintenanceInterval) {
- struct timeval start;
- Utility::gettimeofday(&start);
- t_pdl->maintenance();
- last_lua_maintenance = g_now.tv_sec;
- struct timeval stop;
- Utility::gettimeofday(&stop);
- t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
- ++t_Counters.at(rec::Counter::maintenanceCalls);
- }
- }
- }
-
- t_fdm->run(&g_now);
- // 'run' updates g_now for us
-
- if (threadInfo.isListener()) {
- if (listenOnTCP) {
- if (TCPConnection::getCurrentConnections() > maxTcpClients) { // shutdown, too many connections
- for (const auto fd : threadInfo.tcpSockets) {
- t_fdm->removeReadFD(fd);
- }
- listenOnTCP = false;
- }
- }
- else {
- if (TCPConnection::getCurrentConnections() <= maxTcpClients) { // reenable
- for (const auto fd : threadInfo.tcpSockets) {
- t_fdm->addReadFD(fd, handleNewTCPQuestion);
- }
- listenOnTCP = true;
- }
- }
- }
- }
+ recLoop();
}
catch (PDNSException& ae) {
SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
}
}
-int main(int argc, char** argv)
+static void initArgs()
{
- g_argc = argc;
- g_argv = argv;
- Utility::srandom();
- versionSetProduct(ProductRecursor);
- reportBasicTypes();
- reportOtherTypes();
-
- int ret = EXIT_SUCCESS;
-
- try {
#if HAVE_FIBER_SANITIZER
- // Asan needs more stack
- ::arg().set("stack-size", "stack size per mthread") = "600000";
+ // Asan needs more stack
+ ::arg().set("stack-size", "stack size per mthread") = "600000";
#else
- ::arg().set("stack-size", "stack size per mthread") = "200000";
+ ::arg().set("stack-size", "stack size per mthread") = "200000";
#endif
- ::arg().set("stack-cache-size", "Size of the stack cache, per mthread") = "100";
- // This mode forces metrics snap updates and disable root-refresh, to get consistent counters
- ::arg().setSwitch("devonly-regression-test-mode", "internal use only") = "no";
- ::arg().set("soa-minimum-ttl", "Don't change") = "0";
- ::arg().set("no-shuffle", "Don't change") = "off";
- ::arg().set("local-port", "port to listen on") = "53";
- ::arg().set("local-address", "IP addresses to listen on, separated by spaces or commas. Also accepts ports.") = "127.0.0.1";
- ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options") = "no";
- ::arg().set("trace", "if we should output heaps of logging. set to 'fail' to only log failing domains") = "off";
- ::arg().set("dnssec", "DNSSEC mode: off/process-no-validate/process (default)/log-fail/validate") = "process";
- ::arg().set("dnssec-log-bogus", "Log DNSSEC bogus validations") = "no";
- ::arg().set("signature-inception-skew", "Allow the signature inception to be off by this number of seconds") = "60";
- ::arg().set("daemon", "Operate as a daemon") = "no";
- ::arg().setSwitch("write-pid", "Write a PID file") = "yes";
- ::arg().set("loglevel", "Amount of logging. Higher is more. Do not set below 3") = "6";
- ::arg().set("disable-syslog", "Disable logging to syslog, useful when running inside a supervisor that logs stdout") = "no";
- ::arg().set("log-timestamp", "Print timestamps in log lines, useful to disable when running with a tool that timestamps stdout already") = "yes";
- ::arg().set("log-common-errors", "If we should log rather common errors") = "no";
- ::arg().set("chroot", "switch to chroot jail") = "";
- ::arg().set("setgid", "If set, change group id to this gid for more security"
+ ::arg().set("stack-cache-size", "Size of the stack cache, per mthread") = "100";
+ // This mode forces metrics snap updates and disable root-refresh, to get consistent counters
+ ::arg().setSwitch("devonly-regression-test-mode", "internal use only") = "no";
+ ::arg().set("soa-minimum-ttl", "Don't change") = "0";
+ ::arg().set("no-shuffle", "Don't change") = "off";
+ ::arg().set("local-port", "port to listen on") = "53";
+ ::arg().set("local-address", "IP addresses to listen on, separated by spaces or commas. Also accepts ports.") = "127.0.0.1";
+ ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options") = "no";
+ ::arg().set("trace", "if we should output heaps of logging. set to 'fail' to only log failing domains") = "off";
+ ::arg().set("dnssec", "DNSSEC mode: off/process-no-validate/process (default)/log-fail/validate") = "process";
+ ::arg().set("dnssec-log-bogus", "Log DNSSEC bogus validations") = "no";
+ ::arg().set("signature-inception-skew", "Allow the signature inception to be off by this number of seconds") = "60";
+ ::arg().set("daemon", "Operate as a daemon") = "no";
+ ::arg().setSwitch("write-pid", "Write a PID file") = "yes";
+ ::arg().set("loglevel", "Amount of logging. Higher is more. Do not set below 3") = "6";
+ ::arg().set("disable-syslog", "Disable logging to syslog, useful when running inside a supervisor that logs stdout") = "no";
+ ::arg().set("log-timestamp", "Print timestamps in log lines, useful to disable when running with a tool that timestamps stdout already") = "yes";
+ ::arg().set("log-common-errors", "If we should log rather common errors") = "no";
+ ::arg().set("chroot", "switch to chroot jail") = "";
+ ::arg().set("setgid", "If set, change group id to this gid for more security"
#ifdef HAVE_SYSTEMD
#define SYSTEMD_SETID_MSG ". When running inside systemd, use the User and Group settings in the unit-file!"
- SYSTEMD_SETID_MSG
+ SYSTEMD_SETID_MSG
#endif
- )
- = "";
- ::arg().set("setuid", "If set, change user id to this uid for more security"
+ )
+ = "";
+ ::arg().set("setuid", "If set, change user id to this uid for more security"
#ifdef HAVE_SYSTEMD
- SYSTEMD_SETID_MSG
+ SYSTEMD_SETID_MSG
#endif
- )
- = "";
- ::arg().set("network-timeout", "Wait this number of milliseconds for network i/o") = "1500";
- ::arg().set("threads", "Launch this number of threads") = "2";
- ::arg().set("distributor-threads", "Launch this number of distributor threads, distributing queries to other threads") = "0";
- ::arg().set("processes", "Launch this number of processes (EXPERIMENTAL, DO NOT CHANGE)") = "1"; // if we un-experimental this, need to fix openssl rand seeding for multiple PIDs!
- ::arg().set("config-name", "Name of this virtual configuration - will rename the binary image") = "";
- ::arg().set("api-config-dir", "Directory where REST API stores config and zones") = "";
- ::arg().set("api-key", "Static pre-shared authentication key for access to the REST API") = "";
- ::arg().setSwitch("webserver", "Start a webserver (for REST API)") = "no";
- ::arg().set("webserver-address", "IP Address of webserver to listen on") = "127.0.0.1";
- ::arg().set("webserver-port", "Port of webserver to listen on") = "8082";
- ::arg().set("webserver-password", "Password required for accessing the webserver") = "";
- ::arg().set("webserver-allow-from", "Webserver access is only allowed from these subnets") = "127.0.0.1,::1";
- ::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
- ::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";
- ::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats") = "";
- ::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server IP address") = "";
- ::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates") = "30";
- ::arg().set("carbon-namespace", "If set overwrites the first part of the carbon string") = "pdns";
- ::arg().set("carbon-instance", "If set overwrites the instance name default") = "recursor";
-
- ::arg().set("statistics-interval", "Number of seconds between printing of recursor statistics, 0 to disable") = "1800";
- ::arg().set("quiet", "Suppress logging of questions and answers") = "";
- ::arg().set("logging-facility", "Facility to log messages as. 0 corresponds to local0") = "";
- ::arg().set("config-dir", "Location of configuration directory (recursor.conf)") = SYSCONFDIR;
- ::arg().set("socket-owner", "Owner of socket") = "";
- ::arg().set("socket-group", "Group of socket") = "";
- ::arg().set("socket-mode", "Permissions for socket") = "";
-
- ::arg().set("socket-dir", string("Where the controlsocket will live, ") + LOCALSTATEDIR + "/pdns-recursor when unset and not chrooted"
+ )
+ = "";
+ ::arg().set("network-timeout", "Wait this number of milliseconds for network i/o") = "1500";
+ ::arg().set("threads", "Launch this number of threads") = "2";
+ ::arg().set("distributor-threads", "Launch this number of distributor threads, distributing queries to other threads") = "0";
+ ::arg().set("processes", "Launch this number of processes (EXPERIMENTAL, DO NOT CHANGE)") = "1"; // if we un-experimental this, need to fix openssl rand seeding for multiple PIDs!
+ ::arg().set("config-name", "Name of this virtual configuration - will rename the binary image") = "";
+ ::arg().set("api-config-dir", "Directory where REST API stores config and zones") = "";
+ ::arg().set("api-key", "Static pre-shared authentication key for access to the REST API") = "";
+ ::arg().setSwitch("webserver", "Start a webserver (for REST API)") = "no";
+ ::arg().set("webserver-address", "IP Address of webserver to listen on") = "127.0.0.1";
+ ::arg().set("webserver-port", "Port of webserver to listen on") = "8082";
+ ::arg().set("webserver-password", "Password required for accessing the webserver") = "";
+ ::arg().set("webserver-allow-from", "Webserver access is only allowed from these subnets") = "127.0.0.1,::1";
+ ::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
+ ::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";
+ ::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats") = "";
+ ::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server IP address") = "";
+ ::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates") = "30";
+ ::arg().set("carbon-namespace", "If set overwrites the first part of the carbon string") = "pdns";
+ ::arg().set("carbon-instance", "If set overwrites the instance name default") = "recursor";
+
+ ::arg().set("statistics-interval", "Number of seconds between printing of recursor statistics, 0 to disable") = "1800";
+ ::arg().set("quiet", "Suppress logging of questions and answers") = "";
+ ::arg().set("logging-facility", "Facility to log messages as. 0 corresponds to local0") = "";
+ ::arg().set("config-dir", "Location of configuration directory (recursor.conf)") = SYSCONFDIR;
+ ::arg().set("socket-owner", "Owner of socket") = "";
+ ::arg().set("socket-group", "Group of socket") = "";
+ ::arg().set("socket-mode", "Permissions for socket") = "";
+
+ ::arg().set("socket-dir", string("Where the controlsocket will live, ") + LOCALSTATEDIR + "/pdns-recursor when unset and not chrooted"
#ifdef HAVE_SYSTEMD
- + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")
- = "";
- auto runtimeDir = getenv("RUNTIME_DIRECTORY");
- if (runtimeDir != nullptr) {
- ::arg().set("socket-dir") = runtimeDir;
- }
+ + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")
+ = "";
+ auto* runtimeDir = getenv("RUNTIME_DIRECTORY"); // NOLINT(concurrency-mt-unsafe,cppcoreguidelines-pro-type-vararg)
+ if (runtimeDir != nullptr) {
+ ::arg().set("socket-dir") = runtimeDir;
+ }
#else
- )
- = "";
+ )
+ = "";
#endif
- ::arg().set("query-local-address", "Source IP address for sending queries") = "0.0.0.0";
- ::arg().set("client-tcp-timeout", "Timeout in seconds when talking to TCP clients") = "2";
- ::arg().set("max-mthreads", "Maximum number of simultaneous Mtasker threads") = "2048";
- ::arg().set("max-tcp-clients", "Maximum number of simultaneous TCP clients") = "128";
- ::arg().set("max-concurrent-requests-per-tcp-connection", "Maximum number of requests handled concurrently per TCP connection") = "10";
- ::arg().set("server-down-max-fails", "Maximum number of consecutive timeouts (and unreachables) to mark a server as down ( 0 => disabled )") = "64";
- ::arg().set("server-down-throttle-time", "Number of seconds to throttle all queries to a server after being marked as down") = "60";
- ::arg().set("dont-throttle-names", "Do not throttle nameservers with this name or suffix") = "";
- ::arg().set("dont-throttle-netmasks", "Do not throttle nameservers with this IP netmask") = "";
- ::arg().set("non-resolving-ns-max-fails", "Number of failed address resolves of a nameserver to start throttling it, 0 is disabled") = "5";
- ::arg().set("non-resolving-ns-throttle-time", "Number of seconds to throttle a nameserver with a name failing to resolve") = "60";
-
- ::arg().set("hint-file", "If set, load root hints from this file") = "";
- ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache") = "1000000";
- ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory") = "3600";
- ::arg().set("max-cache-bogus-ttl", "maximum number of seconds to keep a Bogus (positive or negative) cached entry in memory") = "3600";
- ::arg().set("max-cache-ttl", "maximum number of seconds to keep a cached entry in memory") = "86400";
- ::arg().set("packetcache-ttl", "maximum number of seconds to keep a cached entry in packetcache") = "86400";
- ::arg().set("max-packetcache-entries", "maximum number of entries to keep in the packetcache") = "500000";
- ::arg().set("packetcache-servfail-ttl", "maximum number of seconds to keep a cached servfail entry in packetcache") = "60";
- ::arg().set("packetcache-negative-ttl", "maximum number of seconds to keep a cached NxDomain or NoData entry in packetcache") = "60";
- ::arg().set("server-id", "Returned when queried for 'id.server' TXT or NSID, defaults to hostname, set custom or 'disabled'") = "";
- ::arg().set("stats-ringbuffer-entries", "maximum number of packets to store statistics for") = "10000";
- ::arg().set("version-string", "string reported on version.pdns or version.bind") = fullVersionString();
- ::arg().set("allow-from", "If set, only allow these comma separated netmasks to recurse") = LOCAL_NETS;
- ::arg().set("allow-from-file", "If set, load allowed netmasks from this file") = "";
- ::arg().set("allow-notify-for", "If set, NOTIFY requests for these zones will be allowed") = "";
- ::arg().set("allow-notify-for-file", "If set, load NOTIFY-allowed zones from this file") = "";
- ::arg().set("allow-notify-from", "If set, NOTIFY requests from these comma separated netmasks will be allowed") = "";
- ::arg().set("allow-notify-from-file", "If set, load NOTIFY-allowed netmasks from this file") = "";
- ::arg().set("entropy-source", "If set, read entropy from this file") = "/dev/urandom";
- ::arg().set("dont-query", "If set, do not query these netmasks for DNS data") = DONT_QUERY;
- ::arg().set("max-tcp-per-client", "If set, maximum number of TCP sessions per client (IP address)") = "0";
- ::arg().set("max-tcp-queries-per-connection", "If set, maximum number of TCP queries in a TCP connection") = "0";
- ::arg().set("spoof-nearmiss-max", "If non-zero, assume spoofing after this many near misses") = "1";
- ::arg().set("single-socket", "If set, only use a single socket for outgoing queries") = "off";
- ::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ") = "";
- ::arg().set("lua-config-file", "More powerful configuration options") = "";
- ::arg().setSwitch("allow-trust-anchor-query", "Allow queries for trustanchor.server CH TXT and negativetrustanchor.server CH TXT") = "no";
-
- ::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs") = "";
- ::arg().set("forward-zones-recurse", "Zones for which we forward queries with recursion bit, comma separated domain=ip pairs") = "";
- ::arg().set("forward-zones-file", "File with (+)domain=ip pairs for forwarding") = "";
- ::arg().set("export-etc-hosts", "If we should serve up contents from /etc/hosts") = "off";
- ::arg().set("export-etc-hosts-search-suffix", "Also serve up the contents of /etc/hosts with this suffix") = "";
- ::arg().set("etc-hosts-file", "Path to 'hosts' file") = "/etc/hosts";
- ::arg().set("serve-rfc1918", "If we should be authoritative for RFC 1918 private IP space") = "yes";
- ::arg().set("lua-dns-script", "Filename containing an optional 'lua' script that will be used to modify dns answers") = "";
- ::arg().set("lua-maintenance-interval", "Number of seconds between calls to the lua user defined maintenance() function") = "1";
- ::arg().set("latency-statistic-size", "Number of latency values to calculate the qa-latency average") = "10000";
- ::arg().setSwitch("disable-packetcache", "Disable packetcache") = "no";
- ::arg().set("ecs-ipv4-bits", "Number of bits of IPv4 address to pass for EDNS Client Subnet") = "24";
- ::arg().set("ecs-ipv4-cache-bits", "Maximum number of bits of IPv4 mask to cache ECS response") = "24";
- ::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet") = "56";
- ::arg().set("ecs-ipv6-cache-bits", "Maximum number of bits of IPv6 mask to cache ECS response") = "56";
- ::arg().setSwitch("ecs-ipv4-never-cache", "If we should never cache IPv4 ECS responses") = "no";
- ::arg().setSwitch("ecs-ipv6-never-cache", "If we should never cache IPv6 ECS responses") = "no";
- ::arg().set("ecs-minimum-ttl-override", "The minimum TTL for records in ECS-specific answers") = "1";
- ::arg().set("ecs-cache-limit-ttl", "Minimum TTL to cache ECS response") = "0";
- ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for (deprecated)") = "";
- ::arg().set("edns-subnet-allow-list", "List of netmasks and domains that we should enable EDNS subnet for") = "";
- ::arg().set("ecs-add-for", "List of client netmasks for which EDNS Client Subnet will be added") = "0.0.0.0/0, ::/0, " LOCAL_NETS_INVERSE;
- ::arg().set("ecs-scope-zero-address", "Address to send to allow-listed authoritative servers for incoming queries with ECS prefix-length source of 0") = "";
- ::arg().setSwitch("use-incoming-edns-subnet", "Pass along received EDNS Client Subnet information") = "no";
- ::arg().setSwitch("pdns-distributes-queries", "If PowerDNS itself should distribute queries over threads") = "no";
- ::arg().setSwitch("root-nx-trust", "If set, believe that an NXDOMAIN from the root means the TLD does not exist") = "yes";
- ::arg().setSwitch("any-to-tcp", "Answer ANY queries with tc=1, shunting to TCP") = "no";
- ::arg().setSwitch("lowercase-outgoing", "Force outgoing questions to lowercase") = "no";
- ::arg().setSwitch("gettag-needs-edns-options", "If EDNS Options should be extracted before calling the gettag() hook") = "no";
- ::arg().set("udp-truncation-threshold", "Maximum UDP response size before we truncate") = "1232";
- ::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size") = "1232";
- ::arg().set("minimum-ttl-override", "The minimum TTL") = "1";
- ::arg().set("max-qperq", "Maximum outgoing queries per query") = "60";
- ::arg().set("max-ns-per-resolve", "Maximum number of NS records to consider to resolve a name, 0 is no limit") = "13";
- ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query") = "10";
- ::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited") = "7000";
- ::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited") = "40";
- ::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing") = "10000";
- ::arg().set("protobuf-use-kernel-timestamp", "Compute the latency of queries in protobuf messages by using the timestamp set by the kernel when the query was received (when available)") = "";
- ::arg().set("distribution-pipe-buffer-size", "Size in bytes of the internal buffer of the pipe used by the distributor to pass incoming queries to a worker thread") = "0";
-
- ::arg().set("include-dir", "Include *.conf files from this directory") = "";
- ::arg().set("security-poll-suffix", "Domain name from which to query security update notifications") = "secpoll.powerdns.com.";
+ ::arg().set("query-local-address", "Source IP address for sending queries") = "0.0.0.0";
+ ::arg().set("client-tcp-timeout", "Timeout in seconds when talking to TCP clients") = "2";
+ ::arg().set("max-mthreads", "Maximum number of simultaneous Mtasker threads") = "2048";
+ ::arg().set("max-tcp-clients", "Maximum number of simultaneous TCP clients") = "128";
+ ::arg().set("max-concurrent-requests-per-tcp-connection", "Maximum number of requests handled concurrently per TCP connection") = "10";
+ ::arg().set("server-down-max-fails", "Maximum number of consecutive timeouts (and unreachables) to mark a server as down ( 0 => disabled )") = "64";
+ ::arg().set("server-down-throttle-time", "Number of seconds to throttle all queries to a server after being marked as down") = "60";
+ ::arg().set("dont-throttle-names", "Do not throttle nameservers with this name or suffix") = "";
+ ::arg().set("dont-throttle-netmasks", "Do not throttle nameservers with this IP netmask") = "";
+ ::arg().set("non-resolving-ns-max-fails", "Number of failed address resolves of a nameserver to start throttling it, 0 is disabled") = "5";
+ ::arg().set("non-resolving-ns-throttle-time", "Number of seconds to throttle a nameserver with a name failing to resolve") = "60";
+
+ ::arg().set("hint-file", "If set, load root hints from this file") = "";
+ ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache") = "1000000";
+ ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory") = "3600";
+ ::arg().set("max-cache-bogus-ttl", "maximum number of seconds to keep a Bogus (positive or negative) cached entry in memory") = "3600";
+ ::arg().set("max-cache-ttl", "maximum number of seconds to keep a cached entry in memory") = "86400";
+ ::arg().set("packetcache-ttl", "maximum number of seconds to keep a cached entry in packetcache") = "86400";
+ ::arg().set("max-packetcache-entries", "maximum number of entries to keep in the packetcache") = "500000";
+ ::arg().set("packetcache-servfail-ttl", "maximum number of seconds to keep a cached servfail entry in packetcache") = "60";
+ ::arg().set("packetcache-negative-ttl", "maximum number of seconds to keep a cached NxDomain or NoData entry in packetcache") = "60";
+ ::arg().set("server-id", "Returned when queried for 'id.server' TXT or NSID, defaults to hostname, set custom or 'disabled'") = "";
+ ::arg().set("stats-ringbuffer-entries", "maximum number of packets to store statistics for") = "10000";
+ ::arg().set("version-string", "string reported on version.pdns or version.bind") = fullVersionString();
+ ::arg().set("allow-from", "If set, only allow these comma separated netmasks to recurse") = LOCAL_NETS;
+ ::arg().set("allow-from-file", "If set, load allowed netmasks from this file") = "";
+ ::arg().set("allow-notify-for", "If set, NOTIFY requests for these zones will be allowed") = "";
+ ::arg().set("allow-notify-for-file", "If set, load NOTIFY-allowed zones from this file") = "";
+ ::arg().set("allow-notify-from", "If set, NOTIFY requests from these comma separated netmasks will be allowed") = "";
+ ::arg().set("allow-notify-from-file", "If set, load NOTIFY-allowed netmasks from this file") = "";
+ ::arg().set("entropy-source", "If set, read entropy from this file") = "/dev/urandom";
+ ::arg().set("dont-query", "If set, do not query these netmasks for DNS data") = DONT_QUERY;
+ ::arg().set("max-tcp-per-client", "If set, maximum number of TCP sessions per client (IP address)") = "0";
+ ::arg().set("max-tcp-queries-per-connection", "If set, maximum number of TCP queries in a TCP connection") = "0";
+ ::arg().set("spoof-nearmiss-max", "If non-zero, assume spoofing after this many near misses") = "1";
+ ::arg().set("single-socket", "If set, only use a single socket for outgoing queries") = "off";
+ ::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ") = "";
+ ::arg().set("lua-config-file", "More powerful configuration options") = "";
+ ::arg().setSwitch("allow-trust-anchor-query", "Allow queries for trustanchor.server CH TXT and negativetrustanchor.server CH TXT") = "no";
+
+ ::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs") = "";
+ ::arg().set("forward-zones-recurse", "Zones for which we forward queries with recursion bit, comma separated domain=ip pairs") = "";
+ ::arg().set("forward-zones-file", "File with (+)domain=ip pairs for forwarding") = "";
+ ::arg().set("export-etc-hosts", "If we should serve up contents from /etc/hosts") = "off";
+ ::arg().set("export-etc-hosts-search-suffix", "Also serve up the contents of /etc/hosts with this suffix") = "";
+ ::arg().set("etc-hosts-file", "Path to 'hosts' file") = "/etc/hosts";
+ ::arg().set("serve-rfc1918", "If we should be authoritative for RFC 1918 private IP space") = "yes";
+ ::arg().set("lua-dns-script", "Filename containing an optional 'lua' script that will be used to modify dns answers") = "";
+ ::arg().set("lua-maintenance-interval", "Number of seconds between calls to the lua user defined maintenance() function") = "1";
+ ::arg().set("latency-statistic-size", "Number of latency values to calculate the qa-latency average") = "10000";
+ ::arg().setSwitch("disable-packetcache", "Disable packetcache") = "no";
+ ::arg().set("ecs-ipv4-bits", "Number of bits of IPv4 address to pass for EDNS Client Subnet") = "24";
+ ::arg().set("ecs-ipv4-cache-bits", "Maximum number of bits of IPv4 mask to cache ECS response") = "24";
+ ::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet") = "56";
+ ::arg().set("ecs-ipv6-cache-bits", "Maximum number of bits of IPv6 mask to cache ECS response") = "56";
+ ::arg().setSwitch("ecs-ipv4-never-cache", "If we should never cache IPv4 ECS responses") = "no";
+ ::arg().setSwitch("ecs-ipv6-never-cache", "If we should never cache IPv6 ECS responses") = "no";
+ ::arg().set("ecs-minimum-ttl-override", "The minimum TTL for records in ECS-specific answers") = "1";
+ ::arg().set("ecs-cache-limit-ttl", "Minimum TTL to cache ECS response") = "0";
+ ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for (deprecated)") = "";
+ ::arg().set("edns-subnet-allow-list", "List of netmasks and domains that we should enable EDNS subnet for") = "";
+ ::arg().set("ecs-add-for", "List of client netmasks for which EDNS Client Subnet will be added") = "0.0.0.0/0, ::/0, " LOCAL_NETS_INVERSE;
+ ::arg().set("ecs-scope-zero-address", "Address to send to allow-listed authoritative servers for incoming queries with ECS prefix-length source of 0") = "";
+ ::arg().setSwitch("use-incoming-edns-subnet", "Pass along received EDNS Client Subnet information") = "no";
+ ::arg().setSwitch("pdns-distributes-queries", "If PowerDNS itself should distribute queries over threads") = "no";
+ ::arg().setSwitch("root-nx-trust", "If set, believe that an NXDOMAIN from the root means the TLD does not exist") = "yes";
+ ::arg().setSwitch("any-to-tcp", "Answer ANY queries with tc=1, shunting to TCP") = "no";
+ ::arg().setSwitch("lowercase-outgoing", "Force outgoing questions to lowercase") = "no";
+ ::arg().setSwitch("gettag-needs-edns-options", "If EDNS Options should be extracted before calling the gettag() hook") = "no";
+ ::arg().set("udp-truncation-threshold", "Maximum UDP response size before we truncate") = "1232";
+ ::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size") = "1232";
+ ::arg().set("minimum-ttl-override", "The minimum TTL") = "1";
+ ::arg().set("max-qperq", "Maximum outgoing queries per query") = "60";
+ ::arg().set("max-ns-per-resolve", "Maximum number of NS records to consider to resolve a name, 0 is no limit") = "13";
+ ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query") = "10";
+ ::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited") = "7000";
+ ::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited") = "40";
+ ::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing") = "10000";
+ ::arg().set("protobuf-use-kernel-timestamp", "Compute the latency of queries in protobuf messages by using the timestamp set by the kernel when the query was received (when available)") = "";
+ ::arg().set("distribution-pipe-buffer-size", "Size in bytes of the internal buffer of the pipe used by the distributor to pass incoming queries to a worker thread") = "0";
+
+ ::arg().set("include-dir", "Include *.conf files from this directory") = "";
+ ::arg().set("security-poll-suffix", "Domain name from which to query security update notifications") = "secpoll.powerdns.com.";
#ifdef SO_REUSEPORT
- ::arg().setSwitch("reuseport", "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address") = "yes";
+ ::arg().setSwitch("reuseport", "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address") = "yes";
#else
- ::arg().setSwitch("reuseport", "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address") = "no";
+ ::arg().setSwitch("reuseport", "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address") = "no";
#endif
- ::arg().setSwitch("snmp-agent", "If set, register as an SNMP agent") = "no";
- ::arg().set("snmp-master-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon (deprecated)") = "";
- ::arg().set("snmp-daemon-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon") = "";
+ ::arg().setSwitch("snmp-agent", "If set, register as an SNMP agent") = "no";
+ ::arg().set("snmp-master-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon (deprecated)") = "";
+ ::arg().set("snmp-daemon-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon") = "";
- std::string defaultAPIDisabledStats = "cache-bytes, packetcache-bytes, special-memory-usage";
- for (size_t idx = 0; idx < 32; idx++) {
- defaultAPIDisabledStats += ", ecs-v4-response-bits-" + std::to_string(idx + 1);
- }
- for (size_t idx = 0; idx < 128; idx++) {
- defaultAPIDisabledStats += ", ecs-v6-response-bits-" + std::to_string(idx + 1);
- }
- std::string defaultDisabledStats = defaultAPIDisabledStats + ", cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count";
+ std::string defaultAPIDisabledStats = "cache-bytes, packetcache-bytes, special-memory-usage";
+ for (size_t idx = 0; idx < 32; idx++) {
+ defaultAPIDisabledStats += ", ecs-v4-response-bits-" + std::to_string(idx + 1);
+ }
+ for (size_t idx = 0; idx < 128; idx++) {
+ defaultAPIDisabledStats += ", ecs-v6-response-bits-" + std::to_string(idx + 1);
+ }
+ std::string defaultDisabledStats = defaultAPIDisabledStats + ", cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count";
- ::arg().set("stats-api-blacklist", "List of statistics that are disabled when retrieving the complete list of statistics via the API (deprecated)") = defaultAPIDisabledStats;
- ::arg().set("stats-carbon-blacklist", "List of statistics that are prevented from being exported via Carbon (deprecated)") = defaultDisabledStats;
- ::arg().set("stats-rec-control-blacklist", "List of statistics that are prevented from being exported via rec_control get-all (deprecated)") = defaultDisabledStats;
- ::arg().set("stats-snmp-blacklist", "List of statistics that are prevented from being exported via SNMP (deprecated)") = defaultDisabledStats;
+ ::arg().set("stats-api-blacklist", "List of statistics that are disabled when retrieving the complete list of statistics via the API (deprecated)") = defaultAPIDisabledStats;
+ ::arg().set("stats-carbon-blacklist", "List of statistics that are prevented from being exported via Carbon (deprecated)") = defaultDisabledStats;
+ ::arg().set("stats-rec-control-blacklist", "List of statistics that are prevented from being exported via rec_control get-all (deprecated)") = defaultDisabledStats;
+ ::arg().set("stats-snmp-blacklist", "List of statistics that are prevented from being exported via SNMP (deprecated)") = defaultDisabledStats;
- ::arg().set("stats-api-disabled-list", "List of statistics that are disabled when retrieving the complete list of statistics via the API") = defaultAPIDisabledStats;
- ::arg().set("stats-carbon-disabled-list", "List of statistics that are prevented from being exported via Carbon") = defaultDisabledStats;
- ::arg().set("stats-rec-control-disabled-list", "List of statistics that are prevented from being exported via rec_control get-all") = defaultDisabledStats;
- ::arg().set("stats-snmp-disabled-list", "List of statistics that are prevented from being exported via SNMP") = defaultDisabledStats;
+ ::arg().set("stats-api-disabled-list", "List of statistics that are disabled when retrieving the complete list of statistics via the API") = defaultAPIDisabledStats;
+ ::arg().set("stats-carbon-disabled-list", "List of statistics that are prevented from being exported via Carbon") = defaultDisabledStats;
+ ::arg().set("stats-rec-control-disabled-list", "List of statistics that are prevented from being exported via rec_control get-all") = defaultDisabledStats;
+ ::arg().set("stats-snmp-disabled-list", "List of statistics that are prevented from being exported via SNMP") = defaultDisabledStats;
- ::arg().set("tcp-fast-open", "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size") = "0";
- ::arg().set("tcp-fast-open-connect", "Enable TCP Fast Open support on outgoing sockets") = "no";
- ::arg().set("nsec3-max-iterations", "Maximum number of iterations allowed for an NSEC3 record") = "150";
+ ::arg().set("tcp-fast-open", "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size") = "0";
+ ::arg().set("tcp-fast-open-connect", "Enable TCP Fast Open support on outgoing sockets") = "no";
+ ::arg().set("nsec3-max-iterations", "Maximum number of iterations allowed for an NSEC3 record") = "150";
- ::arg().set("cpu-map", "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs") = "";
+ ::arg().set("cpu-map", "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs") = "";
- ::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level") = "no";
+ ::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level") = "no";
- ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets") = "";
- ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values") = "512";
+ ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets") = "";
+ ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values") = "512";
- ::arg().set("dns64-prefix", "DNS64 prefix") = "";
+ ::arg().set("dns64-prefix", "DNS64 prefix") = "";
- ::arg().set("udp-source-port-min", "Minimum UDP port to bind on") = "1024";
- ::arg().set("udp-source-port-max", "Maximum UDP port to bind on") = "65535";
- ::arg().set("udp-source-port-avoid", "List of comma separated UDP port number to avoid") = "11211";
- ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.") = "auto";
- ::arg().set("public-suffix-list-file", "Path to the Public Suffix List file, if any") = "";
- ::arg().set("distribution-load-factor", "The load factor used when PowerDNS is distributing queries to worker threads") = "0.0";
+ ::arg().set("udp-source-port-min", "Minimum UDP port to bind on") = "1024";
+ ::arg().set("udp-source-port-max", "Maximum UDP port to bind on") = "65535";
+ ::arg().set("udp-source-port-avoid", "List of comma separated UDP port number to avoid") = "11211";
+ ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.") = "auto";
+ ::arg().set("public-suffix-list-file", "Path to the Public Suffix List file, if any") = "";
+ ::arg().set("distribution-load-factor", "The load factor used when PowerDNS is distributing queries to worker threads") = "0.0";
- ::arg().setSwitch("qname-minimization", "Use Query Name Minimization") = "yes";
- ::arg().setSwitch("nothing-below-nxdomain", "When an NXDOMAIN exists in cache for a name with fewer labels than the qname, send NXDOMAIN without doing a lookup (see RFC 8020)") = "dnssec";
- ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file") = "0";
- ::arg().set("max-include-depth", "Maximum nested $INCLUDE depth when loading a zone from a file") = "20";
+ ::arg().setSwitch("qname-minimization", "Use Query Name Minimization") = "yes";
+ ::arg().setSwitch("nothing-below-nxdomain", "When an NXDOMAIN exists in cache for a name with fewer labels than the qname, send NXDOMAIN without doing a lookup (see RFC 8020)") = "dnssec";
+ ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file") = "0";
+ ::arg().set("max-include-depth", "Maximum nested $INCLUDE depth when loading a zone from a file") = "20";
- ::arg().set("record-cache-shards", "Number of shards in the record cache") = "1024";
- ::arg().set("packetcache-shards", "Number of shards in the packet cache") = "1024";
+ ::arg().set("record-cache-shards", "Number of shards in the record cache") = "1024";
+ ::arg().set("packetcache-shards", "Number of shards in the packet cache") = "1024";
- ::arg().set("refresh-on-ttl-perc", "If a record is requested from the cache and only this % of original TTL remains, refetch") = "0";
- ::arg().set("record-cache-locked-ttl-perc", "Replace records in record cache only after this % of original TTL has passed") = "0";
+ ::arg().set("refresh-on-ttl-perc", "If a record is requested from the cache and only this % of original TTL remains, refetch") = "0";
+ ::arg().set("record-cache-locked-ttl-perc", "Replace records in record cache only after this % of original TTL has passed") = "0";
- ::arg().set("x-dnssec-names", "Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters") = "";
+ ::arg().set("x-dnssec-names", "Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters") = "";
#ifdef NOD_ENABLED
- ::arg().set("new-domain-tracking", "Track newly observed domains (i.e. never seen before).") = "no";
- ::arg().set("new-domain-log", "Log newly observed domains.") = "yes";
- ::arg().set("new-domain-lookup", "Perform a DNS lookup newly observed domains as a subdomain of the configured domain") = "";
- ::arg().set("new-domain-history-dir", "Persist new domain tracking data here to persist between restarts") = string(NODCACHEDIR) + "/nod";
- ::arg().set("new-domain-whitelist", "List of domains (and implicitly all subdomains) which will never be considered a new domain (deprecated)") = "";
- ::arg().set("new-domain-ignore-list", "List of domains (and implicitly all subdomains) which will never be considered a new domain") = "";
- ::arg().set("new-domain-db-size", "Size of the DB used to track new domains in terms of number of cells. Defaults to 67108864") = "67108864";
- ::arg().set("new-domain-pb-tag", "If protobuf is configured, the tag to use for messages containing newly observed domains. Defaults to 'pdns-nod'") = "pdns-nod";
- ::arg().set("unique-response-tracking", "Track unique responses (tuple of query name, type and RR).") = "no";
- ::arg().set("unique-response-log", "Log unique responses") = "yes";
- ::arg().set("unique-response-history-dir", "Persist unique response tracking data here to persist between restarts") = string(NODCACHEDIR) + "/udr";
- ::arg().set("unique-response-db-size", "Size of the DB used to track unique responses in terms of number of cells. Defaults to 67108864") = "67108864";
- ::arg().set("unique-response-pb-tag", "If protobuf is configured, the tag to use for messages containing unique DNS responses. Defaults to 'pdns-udr'") = "pdns-udr";
+ ::arg().set("new-domain-tracking", "Track newly observed domains (i.e. never seen before).") = "no";
+ ::arg().set("new-domain-log", "Log newly observed domains.") = "yes";
+ ::arg().set("new-domain-lookup", "Perform a DNS lookup newly observed domains as a subdomain of the configured domain") = "";
+ ::arg().set("new-domain-history-dir", "Persist new domain tracking data here to persist between restarts") = string(NODCACHEDIR) + "/nod";
+ ::arg().set("new-domain-whitelist", "List of domains (and implicitly all subdomains) which will never be considered a new domain (deprecated)") = "";
+ ::arg().set("new-domain-ignore-list", "List of domains (and implicitly all subdomains) which will never be considered a new domain") = "";
+ ::arg().set("new-domain-db-size", "Size of the DB used to track new domains in terms of number of cells. Defaults to 67108864") = "67108864";
+ ::arg().set("new-domain-pb-tag", "If protobuf is configured, the tag to use for messages containing newly observed domains. Defaults to 'pdns-nod'") = "pdns-nod";
+ ::arg().set("unique-response-tracking", "Track unique responses (tuple of query name, type and RR).") = "no";
+ ::arg().set("unique-response-log", "Log unique responses") = "yes";
+ ::arg().set("unique-response-history-dir", "Persist unique response tracking data here to persist between restarts") = string(NODCACHEDIR) + "/udr";
+ ::arg().set("unique-response-db-size", "Size of the DB used to track unique responses in terms of number of cells. Defaults to 67108864") = "67108864";
+ ::arg().set("unique-response-pb-tag", "If protobuf is configured, the tag to use for messages containing unique DNS responses. Defaults to 'pdns-udr'") = "pdns-udr";
#endif /* NOD_ENABLED */
- ::arg().setSwitch("extended-resolution-errors", "If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors") = "no";
-
- ::arg().set("aggressive-nsec-cache-size", "The number of records to cache in the aggressive cache. If set to a value greater than 0, and DNSSEC processing or validation is enabled, the recursor will cache NSEC and NSEC3 records to generate negative answers, as defined in rfc8198") = "100000";
- ::arg().set("aggressive-cache-min-nsec3-hit-ratio", "The minimum expected hit ratio to store NSEC3 records into the aggressive cache") = "2000";
-
- ::arg().set("edns-padding-from", "List of netmasks (proxy IP in case of proxy-protocol presence, client IP otherwise) for which EDNS padding will be enabled in responses, provided that 'edns-padding-mode' applies") = "";
- ::arg().set("edns-padding-mode", "Whether to add EDNS padding to all responses ('always') or only to responses for queries containing the EDNS padding option ('padded-queries-only', the default). In both modes, padding will only be added to responses for queries coming from `edns-padding-from`_ sources") = "padded-queries-only";
- ::arg().set("edns-padding-tag", "Packetcache tag associated to responses sent with EDNS padding, to prevent sending these to clients for which padding is not enabled.") = "7830";
- ::arg().setSwitch("edns-padding-out", "Whether to add EDNS padding to outgoing DoT messages") = "yes";
-
- ::arg().setSwitch("dot-to-port-853", "Force DoT connection to target port 853 if DoT compiled in") = "yes";
- ::arg().set("dot-to-auth-names", "Use DoT to authoritative servers with these names or suffixes") = "";
- ::arg().set("event-trace-enabled", "If set, event traces are collected and send out via protobuf logging (1), logfile (2) or both(3)") = "0";
-
- ::arg().set("tcp-out-max-idle-ms", "Time TCP/DoT connections are left idle in milliseconds or 0 if no limit") = "10000";
- ::arg().set("tcp-out-max-idle-per-auth", "Maximum number of idle TCP/DoT connections to a specific IP per thread, 0 means do not keep idle connections open") = "10";
- ::arg().set("tcp-out-max-queries", "Maximum total number of queries per TCP/DoT connection, 0 means no limit") = "0";
- ::arg().set("tcp-out-max-idle-per-thread", "Maximum number of idle TCP/DoT connections per thread") = "100";
- ::arg().setSwitch("structured-logging", "Prefer structured logging") = "yes";
- ::arg().set("structured-logging-backend", "Structured logging backend") = "default";
- ::arg().setSwitch("save-parent-ns-set", "Save parent NS set to be used if child NS set fails") = "yes";
- ::arg().set("max-busy-dot-probes", "Maximum number of concurrent DoT probes") = "0";
- ::arg().set("serve-stale-extensions", "Number of times a record's ttl is extended by 30s to be served stale") = "0";
-
- ::arg().setCmd("help", "Provide a helpful message");
- ::arg().setCmd("version", "Print version string");
- ::arg().setCmd("config", "Output blank configuration. You can use --config=check to test the config file and command line arguments.");
- ::arg().setDefaults();
- g_log.toConsole(Logger::Info);
+ ::arg().setSwitch("extended-resolution-errors", "If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors") = "no";
+
+ ::arg().set("aggressive-nsec-cache-size", "The number of records to cache in the aggressive cache. If set to a value greater than 0, and DNSSEC processing or validation is enabled, the recursor will cache NSEC and NSEC3 records to generate negative answers, as defined in rfc8198") = "100000";
+ ::arg().set("aggressive-cache-min-nsec3-hit-ratio", "The minimum expected hit ratio to store NSEC3 records into the aggressive cache") = "2000";
+
+ ::arg().set("edns-padding-from", "List of netmasks (proxy IP in case of proxy-protocol presence, client IP otherwise) for which EDNS padding will be enabled in responses, provided that 'edns-padding-mode' applies") = "";
+ ::arg().set("edns-padding-mode", "Whether to add EDNS padding to all responses ('always') or only to responses for queries containing the EDNS padding option ('padded-queries-only', the default). In both modes, padding will only be added to responses for queries coming from `edns-padding-from`_ sources") = "padded-queries-only";
+ ::arg().set("edns-padding-tag", "Packetcache tag associated to responses sent with EDNS padding, to prevent sending these to clients for which padding is not enabled.") = "7830";
+ ::arg().setSwitch("edns-padding-out", "Whether to add EDNS padding to outgoing DoT messages") = "yes";
+
+ ::arg().setSwitch("dot-to-port-853", "Force DoT connection to target port 853 if DoT compiled in") = "yes";
+ ::arg().set("dot-to-auth-names", "Use DoT to authoritative servers with these names or suffixes") = "";
+ ::arg().set("event-trace-enabled", "If set, event traces are collected and send out via protobuf logging (1), logfile (2) or both(3)") = "0";
+
+ ::arg().set("tcp-out-max-idle-ms", "Time TCP/DoT connections are left idle in milliseconds or 0 if no limit") = "10000";
+ ::arg().set("tcp-out-max-idle-per-auth", "Maximum number of idle TCP/DoT connections to a specific IP per thread, 0 means do not keep idle connections open") = "10";
+ ::arg().set("tcp-out-max-queries", "Maximum total number of queries per TCP/DoT connection, 0 means no limit") = "0";
+ ::arg().set("tcp-out-max-idle-per-thread", "Maximum number of idle TCP/DoT connections per thread") = "100";
+ ::arg().setSwitch("structured-logging", "Prefer structured logging") = "yes";
+ ::arg().set("structured-logging-backend", "Structured logging backend") = "default";
+ ::arg().setSwitch("save-parent-ns-set", "Save parent NS set to be used if child NS set fails") = "yes";
+ ::arg().set("max-busy-dot-probes", "Maximum number of concurrent DoT probes") = "0";
+ ::arg().set("serve-stale-extensions", "Number of times a record's ttl is extended by 30s to be served stale") = "0";
+
+ ::arg().setCmd("help", "Provide a helpful message");
+ ::arg().setCmd("version", "Print version string");
+ ::arg().setCmd("config", "Output blank configuration. You can use --config=check to test the config file and command line arguments.");
+ ::arg().setDefaults();
+ g_log.toConsole(Logger::Info);
+}
+
+static pair<int, bool> doConfig(Logr::log_t startupLog, const string& configname, int argc, char* argv[]) // NOLINT: Posix API
+{
+ if (::arg().mustDo("config")) {
+ string config = ::arg()["config"];
+ if (config == "check") {
+ try {
+ if (!::arg().file(configname.c_str())) {
+ SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
+ startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
+ return {1, true};
+ }
+ ::arg().parse(argc, argv);
+ return {0, true};
+ }
+ catch (const ArgException& argException) {
+ SLOG(g_log << Logger::Warning << "Unable to parse configuration file '" << configname << "': " << argException.reason << endl,
+ startupLog->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname), "reason", Logging::Loggable(argException.reason)));
+ return {1, true};
+ }
+ }
+ else if (config == "default" || config.empty()) {
+ cout << ::arg().configstring(false, true);
+ }
+ else if (config == "diff") {
+ if (!::arg().laxFile(configname.c_str())) {
+ SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
+ startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
+ return {1, true};
+ }
+ ::arg().laxParse(argc, argv);
+ cout << ::arg().configstring(true, false);
+ }
+ else {
+ if (!::arg().laxFile(configname.c_str())) {
+ SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
+ startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
+ return {1, true};
+ }
+ ::arg().laxParse(argc, argv);
+ cout << ::arg().configstring(true, true);
+ }
+ return {0, true};
+ }
+ return {0, false};
+}
+
+int main(int argc, char** argv) // NOLINT(readability-function-cognitive-complexity) #12791 Remove NOLINT(readability-function-cognitive-complexity) omoerbeek
+{
+ g_argc = argc;
+ g_argv = argv;
+ Utility::srandom();
+ versionSetProduct(ProductRecursor);
+ reportBasicTypes();
+ reportOtherTypes();
+
+ int ret = EXIT_SUCCESS;
+
+ try {
+ initArgs();
::arg().laxParse(argc, argv); // do a lax parse
if (::arg().mustDo("version")) {
showProductVersion();
showBuildConfiguration();
- exit(0);
+ return 0;
}
if (::arg().mustDo("help")) {
cout << "syntax:" << endl
<< endl;
cout << ::arg().helpstring(::arg()["help"]) << endl;
- exit(0);
+ return 0;
}
// Pick up options given on command line to setup logging asap.
}
cerr << " (";
bool first = true;
- for (const auto& c : ::arg().getCommands()) {
+ for (const auto& command : ::arg().getCommands()) {
if (!first) {
cerr << ", ";
}
first = false;
- cerr << c;
+ cerr << command;
}
cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl;
- exit(99);
+ return 99;
}
if (s_structured_logger_backend == "systemd-journal") {
::arg().setSLog(startupLog);
- if (::arg().mustDo("config")) {
- string config = ::arg()["config"];
- if (config == "check") {
- try {
- if (!::arg().file(configname.c_str())) {
- SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
- startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
- exit(1);
- }
- ::arg().parse(argc, argv);
- exit(0);
- }
- catch (const ArgException& argException) {
- SLOG(g_log << Logger::Warning << "Unable to parse configuration file '" << configname << "': " << argException.reason << endl,
- startupLog->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname), "reason", Logging::Loggable(argException.reason)));
- exit(1);
- }
- }
- else if (config == "default" || config.empty()) {
- cout << ::arg().configstring(false, true);
- }
- else if (config == "diff") {
- if (!::arg().laxFile(configname.c_str())) {
- SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
- startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
- exit(1);
- }
- ::arg().laxParse(argc, argv);
- cout << ::arg().configstring(true, false);
- }
- else {
- if (!::arg().laxFile(configname.c_str())) {
- SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
- startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
- exit(1);
- }
- ::arg().laxParse(argc, argv);
- cout << ::arg().configstring(true, true);
- }
- exit(0);
+ bool mustExit = false;
+ std::tie(ret, mustExit) = doConfig(startupLog, configname, argc, argv);
+ if (ret != 0 || mustExit) {
+ return ret;
}
if (!::arg().file(configname.c_str())) {
if (!::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty()) {
SLOG(g_log << Logger::Error << "Using chroot and enabling the API is not possible" << endl,
startupLog->info(Logr::Error, "Cannot use chroot and enable the API at the same time"));
- exit(EXIT_FAILURE);
+ return EXIT_FAILURE;
}
if (::arg()["socket-dir"].empty()) {
- if (::arg()["chroot"].empty())
+ if (::arg()["chroot"].empty()) {
::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor";
- else
+ }
+ else {
::arg().set("socket-dir") = "/";
+ }
}
if (::arg().asNum("threads") == 1) {
g_packetCache = std::make_unique<RecursorPacketCache>(g_maxPacketCacheEntries, ::arg().asNum("packetcache-shards"));
}
- ret = serviceMain(argc, argv, startupLog);
+ ret = serviceMain(startupLog);
}
catch (const PDNSException& ae) {
SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,