diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -3,3 +3,5 @@ This release includes the following features and fixes: + - bitcoin-seeder no longer takes single letter parameter name. Please use + full length parameter names. See `bitcoin-seeder -?` for more information. diff --git a/src/seeder/README.md b/src/seeder/README.md --- a/src/seeder/README.md +++ b/src/seeder/README.md @@ -33,10 +33,10 @@ On the system vps.example.com, you can now run dnsseed: -./bitcoin-seeder -h dnsseed.example.com -n vps.example.com +./bitcoin-seeder -host=dnsseed.example.com -ns=vps.example.com If you want the DNS server to report SOA records, please provide an -e-mail address (with the @ part replaced by .) using -m. +e-mail address (with the @ part replaced by .) using -mbox. RUNNING AS NON-ROOT @@ -50,7 +50,7 @@ $ iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 5353 If properly configured, this will allow you to run dnsseed in userspace, using -the -p 5353 option. +the -post=5353 option. Generate Seed Lists ------------------- diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp --- a/src/seeder/main.cpp +++ b/src/seeder/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,160 +14,10 @@ #include #include #include -#include #include const std::function G_TRANSLATION_FUN = nullptr; -class CDnsSeedOpts { -public: - int nThreads; - int nPort; - int nDnsThreads; - int fUseTestNet; - int fWipeBan; - int fWipeIgnore; - const char *mbox; - const char *ns; - const char *host; - const char *tor; - const char *ipv4_proxy; - const char *ipv6_proxy; - std::set filter_whitelist; - - CDnsSeedOpts() - : nThreads(96), nPort(53), nDnsThreads(4), fUseTestNet(false), - fWipeBan(false), fWipeIgnore(false), mbox(nullptr), ns(nullptr), - host(nullptr), tor(nullptr), ipv4_proxy(nullptr), - ipv6_proxy(nullptr) {} - - void ParseCommandLine(int argc, char **argv) { - static const char *help = - "Bitcoin-cash-seeder\n" - "Usage: %s -h -n [-m ] [-t ] [-p " - "]\n" - "\n" - "Options:\n" - "-h Hostname of the DNS seed\n" - "-n Hostname of the nameserver\n" - "-m E-Mail address reported in SOA records\n" - "-t Number of crawlers to run in parallel (default " - "96)\n" - "-d Number of DNS server threads (default 4)\n" - "-p UDP port to listen on (default 53)\n" - "-o Tor proxy IP/Port\n" - "-i IPV4 SOCKS5 proxy IP/Port\n" - "-k IPV6 SOCKS5 proxy IP/Port\n" - "-w f1,f2,... Allow these flag combinations as filters\n" - "--testnet Use testnet\n" - "--wipeban Wipe list of banned nodes\n" - "--wipeignore Wipe list of ignored nodes\n" - "-?, --help Show this text\n" - "\n"; - bool showHelp = false; - - while (1) { - static struct option long_options[] = { - {"host", required_argument, 0, 'h'}, - {"ns", required_argument, 0, 'n'}, - {"mbox", required_argument, 0, 'm'}, - {"threads", required_argument, 0, 't'}, - {"dnsthreads", required_argument, 0, 'd'}, - {"port", required_argument, 0, 'p'}, - {"onion", required_argument, 0, 'o'}, - {"proxyipv4", required_argument, 0, 'i'}, - {"proxyipv6", required_argument, 0, 'k'}, - {"filter", required_argument, 0, 'w'}, - {"testnet", no_argument, &fUseTestNet, 1}, - {"wipeban", no_argument, &fWipeBan, 1}, - {"wipeignore", no_argument, &fWipeBan, 1}, - {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0}}; - int option_index = 0; - int c = - getopt_long(argc, argv, "h:n:m:t:p:d:o:i:k:w:", long_options, - &option_index); - if (c == -1) break; - switch (c) { - case 'h': { - host = optarg; - break; - } - - case 'm': { - mbox = optarg; - break; - } - - case 'n': { - ns = optarg; - break; - } - - case 't': { - int n = strtol(optarg, nullptr, 10); - if (n > 0 && n < 1000) nThreads = n; - break; - } - - case 'd': { - int n = strtol(optarg, nullptr, 10); - if (n > 0 && n < 1000) nDnsThreads = n; - break; - } - - case 'p': { - int p = strtol(optarg, nullptr, 10); - if (p > 0 && p < 65536) nPort = p; - break; - } - - case 'o': { - tor = optarg; - break; - } - - case 'i': { - ipv4_proxy = optarg; - break; - } - - case 'k': { - ipv6_proxy = optarg; - break; - } - - case 'w': { - char *ptr = optarg; - while (*ptr != 0) { - unsigned long l = strtoul(ptr, &ptr, 0); - if (*ptr == ',') { - ptr++; - } else if (*ptr != 0) { - break; - } - filter_whitelist.insert(l); - } - break; - } - - case '?': { - showHelp = true; - break; - } - } - } - if (filter_whitelist.empty()) { - filter_whitelist.insert(NODE_NETWORK); - filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM); - filter_whitelist.insert(NODE_NETWORK | NODE_XTHIN); - filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM | NODE_XTHIN); - } - if (host != nullptr && ns == nullptr) showHelp = true; - if (showHelp) fprintf(stderr, help, argv[0]); - } -}; - extern "C" { #include } @@ -270,18 +121,18 @@ } } - CDnsThread(CDnsSeedOpts *opts, int idIn) : id(idIn) { - dns_opt.host = opts->host; - dns_opt.ns = opts->ns; - dns_opt.mbox = opts->mbox; + CDnsThread(std::set &filter_whitelist, int idIn) : id(idIn) { + dns_opt.host = gArgs.GetArg("-host", "").c_str(); + dns_opt.ns = gArgs.GetArg("-ns", "").c_str(); + dns_opt.mbox = gArgs.GetArg("-mbox", "").c_str(); dns_opt.datattl = 3600; dns_opt.nsttl = 40000; dns_opt.cb = GetIPList; - dns_opt.port = opts->nPort; + dns_opt.port = gArgs.GetArg("-port", 53); dns_opt.nRequests = 0; dbQueries = 0; perflag.clear(); - filterWhitelist = opts->filter_whitelist; + filterWhitelist = filter_whitelist; } void run() { dnsserver(&dns_opt); } @@ -472,101 +323,223 @@ return nullptr; } -int main(int argc, char **argv) { - // The logger dump everything on the console by default. - GetLogger().m_print_to_console = true; +static void SetupSeederArgs() { + gArgs.AddArg("-?", _("Print this help message and exit"), false, + OptionsCategory::OPTIONS); + gArgs.AddArg("-host=", _("Hostname of the DNS seed"), false, + OptionsCategory::OPTIONS); + gArgs.AddArg("-ns=", _("Hostname of the nameserver"), false, + OptionsCategory::OPTIONS); + gArgs.AddArg("-mbox=", _("E-Mail address reported in SOA records"), + false, OptionsCategory::OPTIONS); + gArgs.AddArg("-threads=", + _("Number of crawlers to run in parallel (default 96)"), false, + OptionsCategory::OPTIONS); + gArgs.AddArg("-dnsthreads=", + _("Number of DNS server threads (default 4)"), false, + OptionsCategory::OPTIONS); + gArgs.AddArg("-port=", _("UDP port to listen on (default 53)"), false, + OptionsCategory::CONNECTION); + gArgs.AddArg("-onion=", _("Tor proxy IP/Port"), false, + OptionsCategory::CONNECTION); + gArgs.AddArg("-proxyipv4=", _("IPV4 SOCKS5 proxy IP/Port"), false, + OptionsCategory::CONNECTION); + gArgs.AddArg("-proxyipv6=", _("IPV6 SOCKS5 proxy IP/Port"), false, + OptionsCategory::CONNECTION); + gArgs.AddArg("-filter=", + _("Allow these flag combinations as filters"), false, + OptionsCategory::OPTIONS); + gArgs.AddArg("-testnet", _("Use testnet"), false, + OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-wipeban", _("Wipe list of banned nodes"), false, + OptionsCategory::CONNECTION); + gArgs.AddArg("-wipeignore", _("Wipe list of ignored nodes"), false, + OptionsCategory::CONNECTION); + + gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); +} - signal(SIGPIPE, SIG_IGN); - setbuf(stdout, nullptr); - CDnsSeedOpts opts; - opts.ParseCommandLine(argc, argv); +static bool ProcessOptions(int argc, char *argv[], + std::set &filter_whitelist) { + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + fprintf(stderr, "Error parsing command line arguments: %s\n", + error.c_str()); + return false; + } + if (HelpRequested(gArgs)) { + std::string strUsage = "Bitcoin-cash-seeder\n"; + strUsage += "Usage: bitcoin-seeder -host= -ns= " + "[-mbox=] [-threads=] [-port=]\n"; + strUsage += "\n" + gArgs.GetHelpMessage(); + + fprintf(stdout, "%s", strUsage.c_str()); + return true; + } + // Get whitelist filter flags + if (gArgs.IsArgSet("-filter")) { + // Parse whitelist additions + auto flags = gArgs.GetArg("-filter", ""); + size_t flagstartpos = 0; + while (flagstartpos < flags.size() && + flagstartpos != std::string::npos) { + size_t flagendpos = flags.find_first_of(",", flagstartpos); + uint64_t flag = + atoi64(flags.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 | NODE_XTHIN); + filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM | NODE_XTHIN); + } fprintf(stdout, "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()) { + for (std::set::const_iterator it = filter_whitelist.begin(); + it != filter_whitelist.end(); it++) { + if (it != filter_whitelist.begin()) { fprintf(stdout, ","); } fprintf(stdout, "0x%lx", (unsigned long)*it); } + fprintf(stdout, "\n"); - if (opts.tor) { - CService service(LookupNumeric(opts.tor, 9050)); + // Check tor proxy + if (!gArgs.GetArg("-onion", "").empty()) { + CService service( + LookupNumeric(gArgs.GetArg("-onion", "").c_str(), 9050)); if (service.IsValid()) { fprintf(stdout, "Using Tor proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_ONION, proxyType(service)); } } - if (opts.ipv4_proxy) { - CService service(LookupNumeric(opts.ipv4_proxy, 9050)); + // Check ipv4 proxy + if (!gArgs.GetArg("-proxyipv4", "").empty()) { + CService service( + LookupNumeric(gArgs.GetArg("-proxyipv4", "").c_str(), 9050)); if (service.IsValid()) { fprintf(stdout, "Using IPv4 proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_IPV4, proxyType(service)); } } - if (opts.ipv6_proxy) { - CService service(LookupNumeric(opts.ipv6_proxy, 9050)); + // Check ipv6 proxy + if (!gArgs.GetArg("-proxyipv6", "").empty()) { + CService service( + LookupNumeric(gArgs.GetArg("-proxyipv6", "").c_str(), 9050)); if (service.IsValid()) { fprintf(stdout, "Using IPv6 proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_IPV6, proxyType(service)); } } - bool fDNS = true; - if (opts.fUseTestNet) { + + // Get -testnet option + if (gArgs.GetBoolArg("-testnet", false)) { fprintf(stdout, "Using testnet.\n"); netMagic[0] = 0xf4; netMagic[1] = 0xe5; netMagic[2] = 0xf3; netMagic[3] = 0xf4; seeds = testnet_seeds; - fTestNet = true; } - if (!opts.ns) { + // Check hostname of nameserver + if (gArgs.GetArg("-ns", "").empty()) { + // No hostname set fprintf(stdout, "No nameserver set. Not starting DNS server.\n"); - fDNS = false; } - if (fDNS && !opts.host) { - fprintf(stderr, "No hostname set. Please use -h.\n"); - exit(1); + + // Check hostname of DNS seed + if (!gArgs.GetArg("-ns", "").empty() && gArgs.GetArg("-host", "").empty()) { + fprintf(stderr, "No hostname set. Please use -host.\n"); + return false; } - if (fDNS && !opts.mbox) { - fprintf(stderr, "No e-mail address set. Please use -m.\n"); - exit(1); + + // Check email + if (!gArgs.GetArg("-ns", "").empty() && gArgs.GetArg("-mbox", "").empty()) { + fprintf(stderr, "No e-mail address set. Please use -mbox.\n"); + return false; } + FILE *f = fsbridge::fopen("dnsseed.dat", "r"); if (f) { fprintf(stdout, "Loading dnsseed.dat..."); CAutoFile cf(f, SER_DISK, CLIENT_VERSION); cf >> db; - if (opts.fWipeBan) db.banned.clear(); - if (opts.fWipeIgnore) db.ResetIgnores(); + // Check for -wipeban and the -wipeignore + if (gArgs.GetBoolArg("-wipeban", false)) { + db.banned.clear(); + fprintf(stdout, "Ban list wiped..."); + } + if (gArgs.GetBoolArg("-wipeignore", false)) { + db.ResetIgnores(); + fprintf(stdout, "Ignore list wiped..."); + } fprintf(stdout, "done\n"); } + // Get DNS threading and port options + if (gArgs.GetArg("-dnsthreads", 4) < 1 && + gArgs.GetArg("-dnsthreads", 4) > 999) { + gArgs.ForceSetArg("-dnsthreads", "4"); + } + if (gArgs.GetArg("-port", 53) < 1 && gArgs.GetArg("-port", 53) > 65535) { + gArgs.ForceSetArg("-port", "53"); + } + // Get threading options + if (gArgs.GetArg("-threads", 96) < 1 && + gArgs.GetArg("-threads", 96) > 999) { + gArgs.ForceSetArg("-threads", "96"); + } + + return true; +} + +static bool AppInit(int argc, char *argv[]) { + // The logger dump everything on the console by default. + GetLogger().m_print_to_console = true; + + signal(SIGPIPE, SIG_IGN); + setbuf(stdout, nullptr); + SetupSeederArgs(); + + std::set filter_whitelist; + if (!ProcessOptions(argc, argv, filter_whitelist)) { + return false; + } + pthread_t threadDns, threadSeed, threadDump, threadStats; - if (fDNS) { - fprintf(stdout, "Starting %i DNS threads for %s on %s (port %i)...", - opts.nDnsThreads, opts.host, opts.ns, opts.nPort); + if (!gArgs.GetArg("-ns", "").empty()) { + fprintf(stdout, "Starting %li DNS threads for %s on %s (port %li)...", + gArgs.GetArg("-dnsthreads", 4), + gArgs.GetArg("-host", "").c_str(), + gArgs.GetArg("-ns", "").c_str(), gArgs.GetArg("-port", 53)); dnsThread.clear(); - for (int i = 0; i < opts.nDnsThreads; i++) { - dnsThread.push_back(new CDnsThread(&opts, i)); + for (int i = 0; i < gArgs.GetArg("-dnsthreads", 4); i++) { + dnsThread.push_back(new CDnsThread(filter_whitelist, i)); pthread_create(&threadDns, nullptr, ThreadDNS, dnsThread[i]); fprintf(stdout, "."); Sleep(20); } fprintf(stdout, "done\n"); } + uint64_t threads = gArgs.GetArg("-threads", 96); fprintf(stdout, "Starting seeder..."); pthread_create(&threadSeed, nullptr, ThreadSeeder, nullptr); fprintf(stdout, "done\n"); - fprintf(stdout, "Starting %i crawler threads...", opts.nThreads); + fprintf(stdout, "Starting %li crawler threads...", threads); pthread_attr_t attr_crawler; pthread_attr_init(&attr_crawler); pthread_attr_setstacksize(&attr_crawler, 0x20000); - for (int i = 0; i < opts.nThreads; i++) { + for (size_t i = 0; i < threads; i++) { pthread_t thread; - pthread_create(&thread, &attr_crawler, ThreadCrawler, &opts.nThreads); + pthread_create(&thread, &attr_crawler, ThreadCrawler, &threads); } pthread_attr_destroy(&attr_crawler); fprintf(stdout, "done\n"); @@ -576,3 +549,7 @@ pthread_join(threadDump, &res); return 0; } + +int main(int argc, char **argv) { + return AppInit(argc, argv); +}