diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp index ca28107d9..6bc418e4b 100644 --- a/src/seeder/main.cpp +++ b/src/seeder/main.cpp @@ -1,452 +1,450 @@ // Copyright (c) 2017-2020 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::function G_TRANSLATION_FUN = nullptr; extern "C" { #include } CAddrDb db; extern "C" void *ThreadCrawler(void *data) { int *nThreads = (int *)data; do { std::vector ips; int wait = 5; db.GetMany(ips, 16, wait); int64_t now = GetTime(); if (ips.empty()) { wait *= 1000; wait += rand() % (500 * *nThreads); UninterruptibleSleep(std::chrono::milliseconds(wait)); continue; } std::vector addr; for (size_t i = 0; i < ips.size(); i++) { CServiceResult &res = ips[i]; res.nBanTime = 0; res.nClientV = 0; res.nHeight = 0; res.strClientV = ""; res.services = 0; bool getaddr = res.ourLastSuccess + 86400 < now; try { CSeederNode node(res.service, getaddr ? &addr : nullptr); bool ret = node.Run(); if (!ret) { res.nBanTime = node.GetBan(); } else { res.nBanTime = 0; } res.nClientV = node.GetClientVersion(); res.strClientV = node.GetClientSubVersion(); res.nHeight = node.GetStartingHeight(); res.services = node.GetServices(); // tfm::format(std::cout, "%s: %s!!!\n", cip.ToString(), // ret ? "GOOD" : "BAD"); res.fGood = ret; } catch (std::ios_base::failure &e) { res.nBanTime = 0; res.fGood = false; } } db.ResultMany(ips); db.Add(addr); } while (1); return nullptr; } extern "C" uint32_t GetIPList(void *thread, char *requestedHostname, addr_t *addr, uint32_t max, uint32_t ipv4, uint32_t ipv6); class CDnsThread { public: struct FlagSpecificData { int nIPv4, nIPv6; std::vector cache; time_t cacheTime; unsigned int cacheHits; FlagSpecificData() : nIPv4(0), nIPv6(0), cacheTime(0), cacheHits(0) {} }; dns_opt_t dns_opt; // must be first const int id; std::map perflag; std::atomic dbQueries; std::set filterWhitelist; void cacheHit(uint64_t requestedFlags, bool force = false) { static bool nets[NET_MAX] = {}; if (!nets[NET_IPV4]) { nets[NET_IPV4] = true; nets[NET_IPV6] = true; } int64_t now = GetTime(); FlagSpecificData &thisflag = perflag[requestedFlags]; thisflag.cacheHits++; if (force || thisflag.cacheHits * 400 > (thisflag.cache.size() * thisflag.cache.size()) || (thisflag.cacheHits * thisflag.cacheHits * 20 > thisflag.cache.size() && (now - thisflag.cacheTime > 5))) { std::set ips; db.GetIPs(ips, requestedFlags, 1000, nets); dbQueries++; thisflag.cache.clear(); thisflag.nIPv4 = 0; thisflag.nIPv6 = 0; thisflag.cache.reserve(ips.size()); for (auto &ip : ips) { struct in_addr addr; struct in6_addr addr6; if (ip.GetInAddr(&addr)) { addr_t a; a.v = 4; memcpy(&a.data.v4, &addr, 4); thisflag.cache.push_back(a); thisflag.nIPv4++; } else if (ip.GetIn6Addr(&addr6)) { addr_t a; a.v = 6; memcpy(&a.data.v6, &addr6, 16); thisflag.cache.push_back(a); thisflag.nIPv6++; } } thisflag.cacheHits = 0; thisflag.cacheTime = now; } } CDnsThread(seeder::CDnsSeedOpts *opts, int idIn) : id(idIn) { dns_opt.host = opts->host.c_str(); dns_opt.ns = opts->ns.c_str(); dns_opt.mbox = opts->mbox.c_str(); dns_opt.datattl = 3600; dns_opt.nsttl = 40000; dns_opt.cb = GetIPList; dns_opt.addr = opts->ip_addr.c_str(); dns_opt.port = opts->nPort; dns_opt.nRequests = 0; dbQueries = 0; perflag.clear(); filterWhitelist = opts->filter_whitelist; } void run() { dnsserver(&dns_opt); } }; extern "C" uint32_t GetIPList(void *data, char *requestedHostname, addr_t *addr, uint32_t max, uint32_t ipv4, uint32_t ipv6) { CDnsThread *thread = (CDnsThread *)data; uint64_t requestedFlags = 0; int hostlen = strlen(requestedHostname); if (hostlen > 1 && requestedHostname[0] == 'x' && requestedHostname[1] != '0') { char *pEnd; uint64_t flags = (uint64_t)strtoull(requestedHostname + 1, &pEnd, 16); if (*pEnd == '.' && pEnd <= requestedHostname + 17 && std::find(thread->filterWhitelist.begin(), thread->filterWhitelist.end(), flags) != thread->filterWhitelist.end()) { requestedFlags = flags; } else { return 0; } } else if (strcasecmp(requestedHostname, thread->dns_opt.host)) { return 0; } thread->cacheHit(requestedFlags); auto &thisflag = thread->perflag[requestedFlags]; uint32_t size = thisflag.cache.size(); uint32_t maxmax = (ipv4 ? thisflag.nIPv4 : 0) + (ipv6 ? thisflag.nIPv6 : 0); if (max > size) { max = size; } if (max > maxmax) { max = maxmax; } uint32_t i = 0; while (i < max) { uint32_t j = i + (rand() % (size - i)); do { bool ok = (ipv4 && thisflag.cache[j].v == 4) || (ipv6 && thisflag.cache[j].v == 6); if (ok) { break; } j++; if (j == size) { j = i; } } while (1); addr[i] = thisflag.cache[j]; thisflag.cache[j] = thisflag.cache[i]; thisflag.cache[i] = addr[i]; i++; } return max; } std::vector dnsThread; extern "C" void *ThreadDNS(void *arg) { CDnsThread *thread = (CDnsThread *)arg; thread->run(); return nullptr; } int StatCompare(const CAddrReport &a, const CAddrReport &b) { if (a.uptime[4] == b.uptime[4]) { if (a.uptime[3] == b.uptime[3]) { return a.clientVersion > b.clientVersion; } else { return a.uptime[3] > b.uptime[3]; } } else { return a.uptime[4] > b.uptime[4]; } } -extern "C" void *ThreadDumper(void *) { - int count = 0; - do { - // First 100s, than 200s, 400s, 800s, 1600s, and then 3200s forever - UninterruptibleSleep(std::chrono::seconds(100 << count)); - if (count < 5) { - count++; - } +extern "C" void *ThreadDumper(void *data) { + assert(data); + const auto dumpInterval(*(const std::chrono::seconds *)data); - { - std::vector v = db.GetAll(); - sort(v.begin(), v.end(), StatCompare); - FILE *f = fsbridge::fopen("dnsseed.dat.new", "w+"); - if (f) { - { - CAutoFile cf(f, SER_DISK, CLIENT_VERSION); - cf << db; - } - rename("dnsseed.dat.new", "dnsseed.dat"); + // First dump should occur no later than 10 seconds. Successive dumps will + // occur every dump interval. + UninterruptibleSleep(std::min(10s, dumpInterval)); + do { + std::vector v = db.GetAll(); + sort(v.begin(), v.end(), StatCompare); + FILE *f = fsbridge::fopen("dnsseed.dat.new", "w+"); + if (f) { + { + CAutoFile cf(f, SER_DISK, CLIENT_VERSION); + cf << db; } - fsbridge::ofstream d{"dnsseed.dump"}; + rename("dnsseed.dat.new", "dnsseed.dat"); + } + fsbridge::ofstream d{"dnsseed.dump"}; + tfm::format(d, "# address good " + "lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) " + "%%(30d) blocks svcs version\n"); + double stat[5] = {0, 0, 0, 0, 0}; + for (CAddrReport rep : v) { tfm::format( - d, "# address good " - "lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) " - "%%(30d) blocks svcs version\n"); - double stat[5] = {0, 0, 0, 0, 0}; - for (CAddrReport rep : v) { - tfm::format( - d, - "%-47s %4d %11" PRId64 - " %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 - " %5i \"%s\"\n", - rep.ip.ToString(), (int)rep.fGood, rep.lastSuccess, - 100.0 * rep.uptime[0], 100.0 * rep.uptime[1], - 100.0 * rep.uptime[2], 100.0 * rep.uptime[3], - 100.0 * rep.uptime[4], rep.blocks, rep.services, - rep.clientVersion, rep.clientSubVersion); - stat[0] += rep.uptime[0]; - stat[1] += rep.uptime[1]; - stat[2] += rep.uptime[2]; - stat[3] += rep.uptime[3]; - stat[4] += rep.uptime[4]; - } - fsbridge::ofstream ff{"dnsstats.log", std::ios_base::app}; - tfm::format(ff, "%llu %g %g %g %g %g\n", GetTime(), stat[0], - stat[1], stat[2], stat[3], stat[4]); + d, + "%-47s %4d %11" PRId64 + " %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 + " %5i \"%s\"\n", + rep.ip.ToString(), (int)rep.fGood, rep.lastSuccess, + 100.0 * rep.uptime[0], 100.0 * rep.uptime[1], + 100.0 * rep.uptime[2], 100.0 * rep.uptime[3], + 100.0 * rep.uptime[4], rep.blocks, rep.services, + rep.clientVersion, rep.clientSubVersion); + stat[0] += rep.uptime[0]; + stat[1] += rep.uptime[1]; + stat[2] += rep.uptime[2]; + stat[3] += rep.uptime[3]; + stat[4] += rep.uptime[4]; } + fsbridge::ofstream ff{"dnsstats.log", std::ios_base::app}; + tfm::format(ff, "%llu %g %g %g %g %g\n", GetTime(), stat[0], stat[1], + stat[2], stat[3], stat[4]); + + UninterruptibleSleep(dumpInterval); } while (1); return nullptr; } extern "C" void *ThreadStats(void *) { bool first = true; do { char c[256]; time_t tim = time(nullptr); struct tm *tmp = localtime(&tim); strftime(c, 256, "[%y-%m-%d %H:%M:%S]", tmp); CAddrDbStats stats; db.GetStats(stats); if (first) { first = false; tfm::format(std::cout, "\n\n\n\x1b[3A"); } else { tfm::format(std::cout, "\x1b[2K\x1b[u"); } tfm::format(std::cout, "\x1b[s"); uint64_t requests = 0; uint64_t queries = 0; for (unsigned int i = 0; i < dnsThread.size(); i++) { requests += dnsThread[i]->dns_opt.nRequests; queries += dnsThread[i]->dbQueries; } tfm::format( std::cout, "%s %i/%i available (%i tried in %is, %i new, %i active), %i " "banned; %llu DNS requests, %llu db queries\n", c, stats.nGood, stats.nAvail, stats.nTracked, stats.nAge, stats.nNew, stats.nAvail - stats.nTracked - stats.nNew, stats.nBanned, (unsigned long long)requests, (unsigned long long)queries); UninterruptibleSleep(1s); } while (1); return nullptr; } const static unsigned int MAX_HOSTS_PER_SEED = 128; extern "C" void *ThreadSeeder(void *) { do { for (const std::string &seed : GetRandomizedDNSSeeds(Params())) { std::vector ips; LookupHost(seed.c_str(), ips, MAX_HOSTS_PER_SEED, true); for (auto &ip : ips) { db.Add(CAddress(CService(ip, GetDefaultPort()), ServiceFlags()), true); } } UninterruptibleSleep(30min); } while (1); return nullptr; } int main(int argc, char **argv) { // The logger dump everything on the console by default. LogInstance().m_print_to_console = true; signal(SIGPIPE, SIG_IGN); setbuf(stdout, nullptr); seeder::CDnsSeedOpts opts(&gArgs); opts.SetupSeederArgs(); int parseResults = opts.ParseCommandLine(argc, const_cast(argv)); if (parseResults != seeder::CONTINUE_EXECUTION) { return parseResults; } tfm::format(std::cout, "Supporting whitelisted filters: "); for (std::set::const_iterator it = opts.filter_whitelist.begin(); it != opts.filter_whitelist.end(); it++) { if (it != opts.filter_whitelist.begin()) { tfm::format(std::cout, ","); } tfm::format(std::cout, "0x%lx", (unsigned long)*it); } tfm::format(std::cout, "\n"); if (!opts.tor.empty()) { CService service(LookupNumeric(opts.tor.c_str(), 9050)); if (service.IsValid()) { tfm::format(std::cout, "Using Tor proxy at %s\n", service.ToStringIPPort()); SetProxy(NET_ONION, proxyType(service)); } } if (!opts.ipv4_proxy.empty()) { CService service(LookupNumeric(opts.ipv4_proxy.c_str(), 9050)); if (service.IsValid()) { tfm::format(std::cout, "Using IPv4 proxy at %s\n", service.ToStringIPPort()); SetProxy(NET_IPV4, proxyType(service)); } } if (!opts.ipv6_proxy.empty()) { CService service(LookupNumeric(opts.ipv6_proxy.c_str(), 9050)); if (service.IsValid()) { tfm::format(std::cout, "Using IPv6 proxy at %s\n", service.ToStringIPPort()); SetProxy(NET_IPV6, proxyType(service)); } } bool fDNS = true; tfm::format(std::cout, "Using %s.\n", gArgs.GetChainName()); if (opts.ns.empty()) { tfm::format(std::cout, "No nameserver set. Not starting DNS server.\n"); fDNS = false; } if (fDNS && opts.host.empty()) { tfm::format(std::cerr, "No hostname set. Please use -h.\n"); return EXIT_FAILURE; } if (fDNS && opts.mbox.empty()) { tfm::format(std::cerr, "No e-mail address set. Please use -m.\n"); return EXIT_FAILURE; } FILE *f = fsbridge::fopen("dnsseed.dat", "r"); if (f) { tfm::format(std::cout, "Loading dnsseed.dat..."); CAutoFile cf(f, SER_DISK, CLIENT_VERSION); cf >> db; if (opts.fWipeBan) { db.banned.clear(); tfm::format(std::cout, "Ban list wiped..."); } if (opts.fWipeIgnore) { db.ResetIgnores(); tfm::format(std::cout, "Ignore list wiped..."); } tfm::format(std::cout, "done\n"); } pthread_t threadDns, threadSeed, threadDump, threadStats; if (fDNS) { tfm::format(std::cout, "Starting %i DNS threads for %s on %s (port %i)...", opts.nDnsThreads, opts.host, opts.ns, opts.nPort); dnsThread.clear(); for (int i = 0; i < opts.nDnsThreads; i++) { dnsThread.push_back(new CDnsThread(&opts, i)); pthread_create(&threadDns, nullptr, ThreadDNS, dnsThread[i]); tfm::format(std::cout, "."); UninterruptibleSleep(20ms); } tfm::format(std::cout, "done\n"); } tfm::format(std::cout, "Starting seeder..."); pthread_create(&threadSeed, nullptr, ThreadSeeder, nullptr); tfm::format(std::cout, "done\n"); tfm::format(std::cout, "Starting %i crawler threads...", opts.nThreads); pthread_attr_t attr_crawler; pthread_attr_init(&attr_crawler); pthread_attr_setstacksize(&attr_crawler, 0x20000); for (int i = 0; i < opts.nThreads; i++) { pthread_t thread; pthread_create(&thread, &attr_crawler, ThreadCrawler, &opts.nThreads); } pthread_attr_destroy(&attr_crawler); tfm::format(std::cout, "done\n"); pthread_create(&threadStats, nullptr, ThreadStats, nullptr); - pthread_create(&threadDump, nullptr, ThreadDumper, nullptr); + pthread_create(&threadDump, nullptr, ThreadDumper, &opts.dumpInterval); void *res; pthread_join(threadDump, &res); return EXIT_SUCCESS; } diff --git a/src/seeder/options.cpp b/src/seeder/options.cpp index 7d6e84c60..1cd8e3dfd 100644 --- a/src/seeder/options.cpp +++ b/src/seeder/options.cpp @@ -1,156 +1,170 @@ // Copyright (c) 2022 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include namespace seeder { int CDnsSeedOpts::ParseCommandLine(int argc, const char **argv) { assert(argsManager); std::string error; if (!argsManager->ParseParameters(argc, argv, error)) { tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error); return EXIT_FAILURE; } if (HelpRequested(*argsManager) || argsManager->IsArgSet("-version")) { std::string strUsage = PACKAGE_NAME " Seeder " + FormatFullVersion() + "\n"; if (HelpRequested(*argsManager)) { strUsage += "\nUsage: bitcoin-seeder -host= -ns= " "[-mbox=] [-threads=] [-port=]\n\n" + argsManager->GetHelpMessage(); } tfm::format(std::cout, "%s", strUsage); return EXIT_SUCCESS; } + dumpInterval = std::chrono::seconds( + argsManager->GetArg("-dumpinterval", DEFAULT_DUMP_INTERVAL_SECONDS)); + if (dumpInterval.count() <= 0) { + tfm::format( + std::cerr, + "Error: -dumpinterval argument expects only positive integers\n"); + return EXIT_FAILURE; + } + nThreads = argsManager->GetArg("-threads", DEFAULT_NUM_THREADS); if (nThreads <= 0) { tfm::format( std::cerr, "Error: -threads argument expects only positive integers\n"); return EXIT_FAILURE; } nPort = argsManager->GetArg("-port", DEFAULT_PORT); if (nPort < 0 || nPort > 65535) { tfm::format(std::cerr, "Error: -port argument expects only positive " "integers in the range 0 - 65535\n"); return EXIT_FAILURE; } nDnsThreads = argsManager->GetArg("-dnsthreads", DEFAULT_NUM_DNS_THREADS); if (nDnsThreads <= 0) { tfm::format( std::cerr, "Error: -dnsthreads argument expects only positive integers\n"); return EXIT_FAILURE; } fWipeBan = argsManager->GetBoolArg("-wipeban", DEFAULT_WIPE_BAN); fWipeIgnore = argsManager->GetBoolArg("-wipeignore", DEFAULT_WIPE_IGNORE); mbox = argsManager->GetArg("-mbox", DEFAULT_EMAIL); ns = argsManager->GetArg("-ns", DEFAULT_NAMESERVER); host = argsManager->GetArg("-host", DEFAULT_HOST); tor = argsManager->GetArg("-onion", DEFAULT_TOR_PROXY); ip_addr = argsManager->GetArg("-address", DEFAULT_LISTEN_ADDRESS); ipv4_proxy = argsManager->GetArg("-proxyipv4", DEFAULT_IPV4_PROXY); ipv6_proxy = argsManager->GetArg("-proxyipv6", DEFAULT_IPV6_PROXY); SelectParams(argsManager->GetChainName()); // Both IPv4 and IPv6 addresses are valid, but the listening address is // treated as IPv6 internally if (ip_addr.find(':') == std::string::npos) { ip_addr.insert(0, "::FFFF:"); } if (argsManager->IsArgSet("-filter")) { // Parse whitelist additions std::string flagString = argsManager->GetArg("-filter", ""); size_t flagstartpos = 0; while (flagstartpos < flagString.size()) { size_t flagendpos = flagString.find_first_of(',', flagstartpos); uint64_t flag = atoi64( flagString.substr(flagstartpos, (flagendpos - flagstartpos))); filter_whitelist.insert(flag); if (flagendpos == std::string::npos) { break; } flagstartpos = flagendpos + 1; } } if (filter_whitelist.empty()) { filter_whitelist.insert(NODE_NETWORK); filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM); filter_whitelist.insert(NODE_NETWORK_LIMITED); filter_whitelist.insert(NODE_NETWORK_LIMITED | NODE_BLOOM); } return CONTINUE_EXECUTION; } void CDnsSeedOpts::SetupSeederArgs() { assert(argsManager); SetupHelpOptions(*argsManager); argsManager->AddArg( "-help-debug", "Show all debugging options (usage: --help -help-debug)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); SetupChainParamsBaseOptions(*argsManager); argsManager->AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsManager->AddArg("-host=", "Hostname of the DNS seed", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsManager->AddArg("-ns=", "Hostname of the nameserver", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsManager->AddArg("-mbox=", "E-Mail address reported in SOA records", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsManager->AddArg( + "-dumpinterval=", + strprintf("Number of seconds between each database dump (default: %d)", + DEFAULT_DUMP_INTERVAL_SECONDS), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsManager->AddArg( "-threads=", strprintf("Number of crawlers to run in parallel (default: %d)", DEFAULT_NUM_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsManager->AddArg("-dnsthreads=", strprintf("Number of DNS server threads (default: %d)", DEFAULT_NUM_DNS_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsManager->AddArg("-address=
", strprintf("Address to listen on (default: '%s')", DEFAULT_LISTEN_ADDRESS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsManager->AddArg( "-port=", strprintf("UDP port to listen on (default: %d)", DEFAULT_PORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsManager->AddArg("-onion=", "Tor proxy IP/Port", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsManager->AddArg("-overridednsseed", "If set, only use the specified DNS seed when " "querying for peer addresses via DNS lookup->", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsManager->AddArg("-proxyipv4=", "IPV4 SOCKS5 proxy IP/Port", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsManager->AddArg("-proxyipv6=", "IPV6 SOCKS5 proxy IP/Port", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsManager->AddArg("-filter=", "Allow these flag combinations as filters", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsManager->AddArg("-wipeban", "Wipe list of banned nodes", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsManager->AddArg("-wipeignore", "Wipe list of ignored nodes", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); } } // namespace seeder diff --git a/src/seeder/options.h b/src/seeder/options.h index 9586a531d..a2dc4f0ac 100644 --- a/src/seeder/options.h +++ b/src/seeder/options.h @@ -1,62 +1,67 @@ // Copyright (c) 2022 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_SEEDER_OPTIONS_H #define BITCOIN_SEEDER_OPTIONS_H +#include #include #include class ArgsManager; namespace seeder { static const int CONTINUE_EXECUTION = -1; +static const int DEFAULT_DUMP_INTERVAL_SECONDS = 60 * 60; static const int DEFAULT_NUM_THREADS = 96; static const int DEFAULT_PORT = 53; static const int DEFAULT_NUM_DNS_THREADS = 4; static const bool DEFAULT_WIPE_BAN = false; static const bool DEFAULT_WIPE_IGNORE = false; static const std::string DEFAULT_EMAIL = ""; static const std::string DEFAULT_NAMESERVER = ""; static const std::string DEFAULT_HOST = ""; static const std::string DEFAULT_TOR_PROXY = ""; static const std::string DEFAULT_LISTEN_ADDRESS = "::"; static const std::string DEFAULT_IPV4_PROXY = ""; static const std::string DEFAULT_IPV6_PROXY = ""; class CDnsSeedOpts { public: ArgsManager *argsManager{nullptr}; + std::chrono::seconds dumpInterval; int nThreads; int nPort; int nDnsThreads; bool fWipeBan; bool fWipeIgnore; std::string mbox; std::string ns; std::string host; std::string tor; std::string ip_addr; std::string ipv4_proxy; std::string ipv6_proxy; std::set filter_whitelist; CDnsSeedOpts(ArgsManager *argsMan) - : argsManager(argsMan), nThreads(DEFAULT_NUM_THREADS), - nPort(DEFAULT_PORT), nDnsThreads(DEFAULT_NUM_DNS_THREADS), - fWipeBan(DEFAULT_WIPE_BAN), fWipeIgnore(DEFAULT_WIPE_IGNORE), - mbox(DEFAULT_EMAIL), ns(DEFAULT_NAMESERVER), host(DEFAULT_HOST), - tor(DEFAULT_TOR_PROXY), ip_addr(DEFAULT_LISTEN_ADDRESS), - ipv4_proxy(DEFAULT_IPV4_PROXY), ipv6_proxy(DEFAULT_IPV6_PROXY) {} + : argsManager(argsMan), + dumpInterval(std::chrono::seconds(DEFAULT_DUMP_INTERVAL_SECONDS)), + nThreads(DEFAULT_NUM_THREADS), nPort(DEFAULT_PORT), + nDnsThreads(DEFAULT_NUM_DNS_THREADS), fWipeBan(DEFAULT_WIPE_BAN), + fWipeIgnore(DEFAULT_WIPE_IGNORE), mbox(DEFAULT_EMAIL), + ns(DEFAULT_NAMESERVER), host(DEFAULT_HOST), tor(DEFAULT_TOR_PROXY), + ip_addr(DEFAULT_LISTEN_ADDRESS), ipv4_proxy(DEFAULT_IPV4_PROXY), + ipv6_proxy(DEFAULT_IPV6_PROXY) {} int ParseCommandLine(int argc, const char **argv); void SetupSeederArgs(); }; } // namespace seeder #endif // BITCOIN_SEEDER_OPTIONS_H diff --git a/src/seeder/test/options_tests.cpp b/src/seeder/test/options_tests.cpp index 102cb9d40..4738d83a1 100644 --- a/src/seeder/test/options_tests.cpp +++ b/src/seeder/test/options_tests.cpp @@ -1,110 +1,132 @@ // Copyright (c) 2022 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include static const char *TEST_HOST = "-host=seeder.bitcoinabc.org"; static const char *TEST_NAMESERVER = "-ns=localhost"; static const char *TEST_EMAIL = "-mbox=email@bitcoinabc.org"; class ArgsTestingSetup { public: ArgsManager argsManager; seeder::CDnsSeedOpts opts = seeder::CDnsSeedOpts(&argsManager); ArgsTestingSetup() { opts.SetupSeederArgs(); } }; BOOST_AUTO_TEST_SUITE(options_tests) BOOST_FIXTURE_TEST_CASE(options_defaults_test, ArgsTestingSetup) { const char *argv[] = {"ignored"}; BOOST_CHECK(opts.ParseCommandLine(1, argv) == seeder::CONTINUE_EXECUTION); + BOOST_CHECK(opts.dumpInterval == + std::chrono::seconds(seeder::DEFAULT_DUMP_INTERVAL_SECONDS)); BOOST_CHECK(opts.nPort == seeder::DEFAULT_PORT); BOOST_CHECK(opts.nThreads == seeder::DEFAULT_NUM_THREADS); BOOST_CHECK(opts.nDnsThreads == seeder::DEFAULT_NUM_DNS_THREADS); BOOST_CHECK(opts.fWipeBan == seeder::DEFAULT_WIPE_BAN); BOOST_CHECK(opts.fWipeIgnore == seeder::DEFAULT_WIPE_IGNORE); } BOOST_FIXTURE_TEST_CASE(options_basic_test, ArgsTestingSetup) { const char *argv[] = {"ignored", TEST_HOST, TEST_NAMESERVER, TEST_EMAIL, "-port=5555"}; BOOST_CHECK(opts.ParseCommandLine(5, argv) == seeder::CONTINUE_EXECUTION); BOOST_CHECK(opts.host == "seeder.bitcoinabc.org"); BOOST_CHECK(opts.ns == "localhost"); BOOST_CHECK(opts.mbox == "email@bitcoinabc.org"); BOOST_CHECK(opts.nPort == 5555); } +BOOST_FIXTURE_TEST_CASE(options_dumpinterval_test, ArgsTestingSetup) { + const std::map expectedResults = { + {-9999, EXIT_FAILURE}, + {-1, EXIT_FAILURE}, + {0, EXIT_FAILURE}, + {1, seeder::CONTINUE_EXECUTION}, + {seeder::DEFAULT_DUMP_INTERVAL_SECONDS, seeder::CONTINUE_EXECUTION}, + {9999, seeder::CONTINUE_EXECUTION}}; + + for (const auto entry : expectedResults) { + const std::string testArg = "-dumpinterval=" + ToString(entry.first); + const char *argv[] = {"ignored", TEST_HOST, TEST_NAMESERVER, TEST_EMAIL, + testArg.c_str()}; + BOOST_CHECK(opts.ParseCommandLine(5, argv) == entry.second); + if (entry.second == seeder::CONTINUE_EXECUTION) { + BOOST_CHECK(opts.dumpInterval == std::chrono::seconds(entry.first)); + } + } +} + BOOST_FIXTURE_TEST_CASE(options_threads_test, ArgsTestingSetup) { const std::map expectedResults = { {-9999, EXIT_FAILURE}, {-1, EXIT_FAILURE}, {0, EXIT_FAILURE}, {1, seeder::CONTINUE_EXECUTION}, {seeder::DEFAULT_NUM_THREADS, seeder::CONTINUE_EXECUTION}, {9999, seeder::CONTINUE_EXECUTION}}; for (const auto entry : expectedResults) { const std::string testArg = "-threads=" + ToString(entry.first); const char *argv[] = {"ignored", TEST_HOST, TEST_NAMESERVER, TEST_EMAIL, testArg.c_str()}; BOOST_CHECK(opts.ParseCommandLine(5, argv) == entry.second); if (entry.second == seeder::CONTINUE_EXECUTION) { BOOST_CHECK(opts.nThreads == entry.first); } } } BOOST_FIXTURE_TEST_CASE(options_dns_threads_test, ArgsTestingSetup) { const std::map expectedResults = { {-9999, EXIT_FAILURE}, {-1, EXIT_FAILURE}, {0, EXIT_FAILURE}, {1, seeder::CONTINUE_EXECUTION}, {seeder::DEFAULT_NUM_DNS_THREADS, seeder::CONTINUE_EXECUTION}, {9999, seeder::CONTINUE_EXECUTION}}; for (const auto entry : expectedResults) { const std::string testArg = "-dnsthreads=" + ToString(entry.first); const char *argv[] = {"ignored", TEST_HOST, TEST_NAMESERVER, TEST_EMAIL, testArg.c_str()}; BOOST_CHECK(opts.ParseCommandLine(5, argv) == entry.second); if (entry.second == seeder::CONTINUE_EXECUTION) { BOOST_CHECK(opts.nDnsThreads == entry.first); } } } BOOST_FIXTURE_TEST_CASE(options_port_test, ArgsTestingSetup) { const std::map expectedResults = { {-9999, EXIT_FAILURE}, {-1, EXIT_FAILURE}, // Note: port 0 indicates to the kernel that a random unused port should // be assigned {0, seeder::CONTINUE_EXECUTION}, {1, seeder::CONTINUE_EXECUTION}, {seeder::DEFAULT_PORT, seeder::CONTINUE_EXECUTION}, {53, seeder::CONTINUE_EXECUTION}, {15353, seeder::CONTINUE_EXECUTION}, {65535, seeder::CONTINUE_EXECUTION}, {65536, EXIT_FAILURE}, {999999, EXIT_FAILURE}}; for (const auto entry : expectedResults) { const std::string testArg = "-port=" + ToString(entry.first); const char *argv[] = {"ignored", TEST_HOST, TEST_NAMESERVER, TEST_EMAIL, testArg.c_str()}; BOOST_CHECK(opts.ParseCommandLine(5, argv) == entry.second); if (entry.second == seeder::CONTINUE_EXECUTION) { BOOST_CHECK(opts.nPort == entry.first); } } } BOOST_AUTO_TEST_SUITE_END()