]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Split serviceMain() to reduce cognitive complexity
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 8 May 2023 07:51:19 +0000 (09:51 +0200)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 8 May 2023 07:51:19 +0000 (09:51 +0200)
pdns/recursordist/rec-main.cc

index b288a7cf47bf7196be4a5c46f0c5cba939c79ef4..dff35ea1a518434991cf46ca900a52a05ac5c922 100644 (file)
@@ -1362,27 +1362,8 @@ template ThreadTimes broadcastAccFunction(const std::function<ThreadTimes*()>& f
 template ProxyMappingStats_t broadcastAccFunction(const std::function<ProxyMappingStats_t*()>& fun);
 template RemoteLoggerStats_t broadcastAccFunction(const std::function<RemoteLoggerStats_t*()>& fun);
 
-static int serviceMain(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"]);
@@ -1418,8 +1399,11 @@ static int serviceMain(Logr::log_t log)
          log->info(Logr::Error, "No outgoing addresses configured! Can not continue"));
     return 99;
   }
+  return 0;
+}
 
-  // keep this ABOVE loadRecursorLuaConfig!
+static int initDNSSEC(Logr::log_t log)
+{
   if (::arg()["dnssec"] == "off") {
     g_dnssecmode = DNSSECMode::Off;
   } else if (::arg()["dnssec"] == "process-no-validate") {
@@ -1445,25 +1429,11 @@ static int serviceMain(Logr::log_t log)
 
   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"));
-    return 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"], ", ");
@@ -1487,30 +1457,10 @@ static int serviceMain(Logr::log_t log)
       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");
@@ -1624,160 +1574,11 @@ static int serviceMain(Logr::log_t log)
   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"])));
-        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;
-    }
-  }
-
-  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)));
-
-  {
-    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));
-  }
-
-  {
-    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;
@@ -1835,16 +1636,15 @@ static int serviceMain(Logr::log_t log)
       }
     }
   }
+}
 
-#ifdef NOD_ENABLED
-  // Setup newly observed domain globals
-  setupNODGlobal();
-#endif /* NOD_ENABLED */
-
+static int initForks(Logr::log_t log)
+{
   int forks = 0;
   for (; forks < ::arg().asNum("processes") - 1; ++forks) {
-    if (fork() == 0) // we are child
+    if (fork() == 0) // we are child
       break;
+    }
   }
 
   if (::arg().mustDo("daemon")) {
@@ -1853,6 +1653,7 @@ static int serviceMain(Logr::log_t log)
     g_log.toConsole(Logger::Critical);
     daemonize(log);
   }
+
   if (Utility::getpid() == 1) {
     /* We are running as pid 1, register sigterm and sigint handler
 
@@ -1874,37 +1675,59 @@ static int serviceMain(Logr::log_t log)
   signal(SIGUSR1, usr1Handler);
   signal(SIGUSR2, usr2Handler);
   signal(SIGPIPE, SIG_IGN); // NOLINT: Posix API
+  return forks;
+}
 
-  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;
+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
   }
-
-  gid_t newgid = 0;
-  if (!::arg()["setgid"].empty()) {
-    newgid = strToGID(::arg()["setgid"]);
-  }
-  uid_t newuid = 0;
-  if (!::arg()["setuid"].empty()) {
-    newuid = strToUID(::arg()["setuid"]);
+  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;
+}
 
-  Utility::dropGroupPrivs(newuid, newgid);
+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*)
+{
   if (!::arg()["chroot"].empty()) {
 #ifdef HAVE_SYSTEMD
     char* ns;
@@ -1947,6 +1770,297 @@ static int serviceMain(Logr::log_t log)
     SLOG(g_log << Logger::Warning << e.what() << endl,
          log->error(Logr::Warning, e.what(), "Could not drop capabilities"));
   }
+  return 0;
+}
+
+static void initSuffixMatchNodes()
+{
+  {
+    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)
+{
+  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);
+
+  /* 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"));
+  }
+
+  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();
+  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
@@ -1973,45 +2087,11 @@ static int serviceMain(Logr::log_t log)
   // 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
-  }
+  initSNMP(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
-  }
-  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
-  }
-  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);
+  ret = initPorts(log);
+  if (ret != 0) {
+    return ret;
   }
 
   return RecThreadInfo::runThreads(log);