diff --git a/src/seeder/CMakeLists.txt b/src/seeder/CMakeLists.txt index 979657bf6..874b570cc 100644 --- a/src/seeder/CMakeLists.txt +++ b/src/seeder/CMakeLists.txt @@ -1,26 +1,27 @@ # 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. project(bitcoin-seeder) add_library(seeder-base bitcoin.cpp db.cpp dns.cpp + options.cpp ) target_link_libraries(seeder-base server) add_executable(bitcoin-seeder main.cpp ) target_link_libraries(bitcoin-seeder seeder-base) include(BinaryTest) add_to_symbols_check(bitcoin-seeder) add_to_security_check(bitcoin-seeder) include(InstallationHelper) install_target(bitcoin-seeder) add_subdirectory(test) diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp index 08fc7f7de..429f77932 100644 --- a/src/seeder/main.cpp +++ b/src/seeder/main.cpp @@ -1,609 +1,451 @@ // 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 #include const std::function G_TRANSLATION_FUN = nullptr; -static const int CONTINUE_EXECUTION = -1; - -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: - 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() - : 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, char **argv) { - SetupSeederArgs(gArgs); - std::string error; - if (!gArgs.ParseParameters(argc, argv, error)) { - tfm::format(std::cerr, "Error parsing command line arguments: %s\n", - error); - return EXIT_FAILURE; - } - if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { - std::string strUsage = - PACKAGE_NAME " Seeder " + FormatFullVersion() + "\n"; - if (HelpRequested(gArgs)) { - strUsage += - "\nUsage: bitcoin-seeder -host= -ns= " - "[-mbox=] [-threads=] [-port=]\n\n" + - gArgs.GetHelpMessage(); - } - - tfm::format(std::cout, "%s", strUsage); - return EXIT_SUCCESS; - } - - nThreads = gArgs.GetArg("-threads", DEFAULT_NUM_THREADS); - nPort = gArgs.GetArg("-port", DEFAULT_PORT); - nDnsThreads = gArgs.GetArg("-dnsthreads", DEFAULT_NUM_DNS_THREADS); - fWipeBan = gArgs.GetBoolArg("-wipeban", DEFAULT_WIPE_BAN); - fWipeIgnore = gArgs.GetBoolArg("-wipeignore", DEFAULT_WIPE_IGNORE); - mbox = gArgs.GetArg("-mbox", DEFAULT_EMAIL); - ns = gArgs.GetArg("-ns", DEFAULT_NAMESERVER); - host = gArgs.GetArg("-host", DEFAULT_HOST); - tor = gArgs.GetArg("-onion", DEFAULT_TOR_PROXY); - ip_addr = gArgs.GetArg("-address", DEFAULT_LISTEN_ADDRESS); - ipv4_proxy = gArgs.GetArg("-proxyipv4", DEFAULT_IPV4_PROXY); - ipv6_proxy = gArgs.GetArg("-proxyipv6", DEFAULT_IPV6_PROXY); - SelectParams(gArgs.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 (gArgs.IsArgSet("-filter")) { - // Parse whitelist additions - std::string flagString = gArgs.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; - } - -private: - void SetupSeederArgs(ArgsManager &argsman) { - SetupHelpOptions(argsman); - argsman.AddArg("-help-debug", - "Show all debugging options (usage: --help -help-debug)", - ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); - - SetupChainParamsBaseOptions(argsman); - - argsman.AddArg("-version", "Print version and exit", - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-host=", "Hostname of the DNS seed", - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-ns=", "Hostname of the nameserver", - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-mbox=", "E-Mail address reported in SOA records", - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg( - "-threads=", - strprintf("Number of crawlers to run in parallel (default: %d)", - DEFAULT_NUM_THREADS), - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-dnsthreads=", - strprintf("Number of DNS server threads (default: %d)", - DEFAULT_NUM_DNS_THREADS), - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-address=
", - strprintf("Address to listen on (default: '%s')", - DEFAULT_LISTEN_ADDRESS), - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg( - "-port=", - strprintf("UDP port to listen on (default: %d)", DEFAULT_PORT), - ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-onion=", "Tor proxy IP/Port", - ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-overridednsseed", - "If set, only use the specified DNS seed when " - "querying for peer addresses via DNS lookup.", - ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-proxyipv4=", "IPV4 SOCKS5 proxy IP/Port", - ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-proxyipv6=", "IPV6 SOCKS5 proxy IP/Port", - ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-filter=", - "Allow these flag combinations as filters", - ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-wipeban", "Wipe list of banned nodes", - ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-wipeignore", "Wipe list of ignored nodes", - ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - } -}; - 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); Sleep(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(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 Sleep(100000 << count); if (count < 5) { count++; } { 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"); } 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, "%-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]); } } 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); Sleep(1000); } 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); } } Sleep(1800000); } 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); CDnsSeedOpts opts; - int parseResults = opts.ParseCommandLine(argc, argv); + int parseResults = + opts.ParseCommandLine(argc, const_cast(argv)); if (parseResults != 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, "."); Sleep(20); } 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); void *res; pthread_join(threadDump, &res); return EXIT_SUCCESS; } diff --git a/src/seeder/options.cpp b/src/seeder/options.cpp new file mode 100644 index 000000000..975190819 --- /dev/null +++ b/src/seeder/options.cpp @@ -0,0 +1,129 @@ +// 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 + +int CDnsSeedOpts::ParseCommandLine(int argc, const char **argv) { + SetupSeederArgs(gArgs); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", + error); + return EXIT_FAILURE; + } + if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { + std::string strUsage = + PACKAGE_NAME " Seeder " + FormatFullVersion() + "\n"; + if (HelpRequested(gArgs)) { + strUsage += + "\nUsage: bitcoin-seeder -host= -ns= " + "[-mbox=] [-threads=] [-port=]\n\n" + + gArgs.GetHelpMessage(); + } + + tfm::format(std::cout, "%s", strUsage); + return EXIT_SUCCESS; + } + + nThreads = gArgs.GetArg("-threads", DEFAULT_NUM_THREADS); + nPort = gArgs.GetArg("-port", DEFAULT_PORT); + nDnsThreads = gArgs.GetArg("-dnsthreads", DEFAULT_NUM_DNS_THREADS); + fWipeBan = gArgs.GetBoolArg("-wipeban", DEFAULT_WIPE_BAN); + fWipeIgnore = gArgs.GetBoolArg("-wipeignore", DEFAULT_WIPE_IGNORE); + mbox = gArgs.GetArg("-mbox", DEFAULT_EMAIL); + ns = gArgs.GetArg("-ns", DEFAULT_NAMESERVER); + host = gArgs.GetArg("-host", DEFAULT_HOST); + tor = gArgs.GetArg("-onion", DEFAULT_TOR_PROXY); + ip_addr = gArgs.GetArg("-address", DEFAULT_LISTEN_ADDRESS); + ipv4_proxy = gArgs.GetArg("-proxyipv4", DEFAULT_IPV4_PROXY); + ipv6_proxy = gArgs.GetArg("-proxyipv6", DEFAULT_IPV6_PROXY); + SelectParams(gArgs.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 (gArgs.IsArgSet("-filter")) { + // Parse whitelist additions + std::string flagString = gArgs.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(ArgsManager &argsman) { + SetupHelpOptions(argsman); + argsman.AddArg("-help-debug", + "Show all debugging options (usage: --help -help-debug)", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + + SetupChainParamsBaseOptions(argsman); + + argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, + OptionsCategory::OPTIONS); + argsman.AddArg("-host=", "Hostname of the DNS seed", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-ns=", "Hostname of the nameserver", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-mbox=", "E-Mail address reported in SOA records", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg( + "-threads=", + strprintf("Number of crawlers to run in parallel (default: %d)", + DEFAULT_NUM_THREADS), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-dnsthreads=", + strprintf("Number of DNS server threads (default: %d)", + DEFAULT_NUM_DNS_THREADS), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-address=
", + strprintf("Address to listen on (default: '%s')", + DEFAULT_LISTEN_ADDRESS), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg( + "-port=", + strprintf("UDP port to listen on (default: %d)", DEFAULT_PORT), + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-onion=", "Tor proxy IP/Port", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-overridednsseed", + "If set, only use the specified DNS seed when " + "querying for peer addresses via DNS lookup.", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-proxyipv4=", "IPV4 SOCKS5 proxy IP/Port", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-proxyipv6=", "IPV6 SOCKS5 proxy IP/Port", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-filter=", + "Allow these flag combinations as filters", + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-wipeban", "Wipe list of banned nodes", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-wipeignore", "Wipe list of ignored nodes", + ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); +} diff --git a/src/seeder/options.h b/src/seeder/options.h new file mode 100644 index 000000000..be746ff25 --- /dev/null +++ b/src/seeder/options.h @@ -0,0 +1,58 @@ +// 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 + +static const int CONTINUE_EXECUTION = -1; + +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 ArgsManager; + +class CDnsSeedOpts { +public: + 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() + : 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); + +private: + void SetupSeederArgs(ArgsManager &argsman); +}; + +#endif // BITCOIN_SEEDER_OPTIONS_H diff --git a/src/seeder/test/CMakeLists.txt b/src/seeder/test/CMakeLists.txt index 1aa2d1af6..8ba2463ff 100644 --- a/src/seeder/test/CMakeLists.txt +++ b/src/seeder/test/CMakeLists.txt @@ -1,24 +1,25 @@ # Copyright (c) 2019-2020 The Bitcoin developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. project(bitcoin-seeder-test) include(TestSuite) create_test_suite(seeder) add_dependencies(check check-seeder) add_boost_unit_tests_to_suite(seeder test-seeder fixture.cpp TESTS message_writer_tests.cpp + options_tests.cpp p2p_messaging_tests.cpp parse_name_tests.cpp write_name_tests.cpp ) target_link_libraries(test-seeder seeder-base testutil ) diff --git a/src/seeder/test/options_tests.cpp b/src/seeder/test/options_tests.cpp new file mode 100644 index 000000000..a8445b444 --- /dev/null +++ b/src/seeder/test/options_tests.cpp @@ -0,0 +1,23 @@ +// 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 + +BOOST_AUTO_TEST_SUITE(options_tests) + +BOOST_AUTO_TEST_CASE(options_basic_test) { + CDnsSeedOpts opts; + const char *argv[] = {"ignored", "-host=seeder.bitcoinabc.org", + "-ns=localhost", "-mbox=email@bitcoinabc.org", + "-port=5555"}; + BOOST_CHECK(opts.ParseCommandLine(5, argv) == 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_AUTO_TEST_SUITE_END()