Changeset View
Changeset View
Standalone View
Standalone View
src/seeder/main.cpp
// Copyright (c) 2017-2019 The Bitcoin developers | // Copyright (c) 2017-2019 The Bitcoin developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <chainparams.h> | |||||
#include <clientversion.h> | #include <clientversion.h> | ||||
#include <fs.h> | #include <fs.h> | ||||
#include <logging.h> | #include <logging.h> | ||||
#include <protocol.h> | #include <protocol.h> | ||||
#include <seeder/bitcoin.h> | #include <seeder/bitcoin.h> | ||||
#include <seeder/db.h> | #include <seeder/db.h> | ||||
#include <seeder/dns.h> | #include <seeder/dns.h> | ||||
#include <streams.h> | #include <streams.h> | ||||
#include <util/strencodings.h> | |||||
#include <util/system.h> | #include <util/system.h> | ||||
#include <algorithm> | #include <algorithm> | ||||
#include <atomic> | #include <atomic> | ||||
#include <cinttypes> | #include <cinttypes> | ||||
#include <csignal> | #include <csignal> | ||||
#include <cstdlib> | #include <cstdlib> | ||||
#include <getopt.h> | |||||
#include <pthread.h> | #include <pthread.h> | ||||
const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr; | const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr; | ||||
static const int CONTINUE_EXECUTION = -1; | |||||
static const int DEFAULT_NUM_THREADS = 96; | static const int DEFAULT_NUM_THREADS = 96; | ||||
static const int DEFAULT_PORT = 53; | static const int DEFAULT_PORT = 53; | ||||
static const int DEFAULT_NUM_DNS_THREADS = 4; | static const int DEFAULT_NUM_DNS_THREADS = 4; | ||||
static const bool DEFAULT_TESTNET = false; | |||||
static const bool DEFAULT_WIPE_BAN = false; | static const bool DEFAULT_WIPE_BAN = false; | ||||
static const bool DEFAULT_WIPE_IGNORE = false; | static const bool DEFAULT_WIPE_IGNORE = false; | ||||
static const std::string DEFAULT_EMAIL = ""; | static const std::string DEFAULT_EMAIL = ""; | ||||
static const std::string DEFAULT_NAMESERVER = ""; | static const std::string DEFAULT_NAMESERVER = ""; | ||||
static const std::string DEFAULT_HOST = ""; | static const std::string DEFAULT_HOST = ""; | ||||
static const std::string DEFAULT_TOR_PROXY = ""; | static const std::string DEFAULT_TOR_PROXY = ""; | ||||
static const std::string DEFAULT_IPV4_PROXY = ""; | static const std::string DEFAULT_IPV4_PROXY = ""; | ||||
static const std::string DEFAULT_IPV6_PROXY = ""; | static const std::string DEFAULT_IPV6_PROXY = ""; | ||||
class CDnsSeedOpts { | class CDnsSeedOpts { | ||||
public: | public: | ||||
int nThreads; | int nThreads; | ||||
int nPort; | int nPort; | ||||
int nDnsThreads; | int nDnsThreads; | ||||
int fUseTestNet; | bool fWipeBan; | ||||
int fWipeBan; | bool fWipeIgnore; | ||||
int fWipeIgnore; | |||||
std::string mbox; | std::string mbox; | ||||
std::string ns; | std::string ns; | ||||
std::string host; | std::string host; | ||||
std::string tor; | std::string tor; | ||||
std::string ipv4_proxy; | std::string ipv4_proxy; | ||||
std::string ipv6_proxy; | std::string ipv6_proxy; | ||||
std::set<uint64_t> filter_whitelist; | std::set<uint64_t> filter_whitelist; | ||||
CDnsSeedOpts() | CDnsSeedOpts() | ||||
: nThreads(DEFAULT_NUM_THREADS), nPort(DEFAULT_PORT), | : nThreads(DEFAULT_NUM_THREADS), nPort(DEFAULT_PORT), | ||||
nDnsThreads(DEFAULT_NUM_DNS_THREADS), fUseTestNet(DEFAULT_TESTNET), | nDnsThreads(DEFAULT_NUM_DNS_THREADS), fWipeBan(DEFAULT_WIPE_BAN), | ||||
fWipeBan(DEFAULT_WIPE_BAN), fWipeIgnore(DEFAULT_WIPE_IGNORE), | fWipeIgnore(DEFAULT_WIPE_IGNORE), mbox(DEFAULT_EMAIL), | ||||
mbox(DEFAULT_EMAIL), ns(DEFAULT_NAMESERVER), host(DEFAULT_HOST), | ns(DEFAULT_NAMESERVER), host(DEFAULT_HOST), tor(DEFAULT_TOR_PROXY), | ||||
tor(DEFAULT_TOR_PROXY), ipv4_proxy(DEFAULT_IPV4_PROXY), | ipv4_proxy(DEFAULT_IPV4_PROXY), ipv6_proxy(DEFAULT_IPV6_PROXY) {} | ||||
ipv6_proxy(DEFAULT_IPV6_PROXY) {} | |||||
int ParseCommandLine(int argc, char **argv) { | |||||
void ParseCommandLine(int argc, char **argv) { | SetupSeederArgs(); | ||||
static const char *help = | std::string error; | ||||
"Bitcoin-cash-seeder\n" | if (!gArgs.ParseParameters(argc, argv, error)) { | ||||
"Usage: %s -h <host> -n <ns> [-m <mbox>] [-t <threads>] [-p " | fprintf(stderr, "Error parsing command line arguments: %s\n", | ||||
"<port>]\n" | error.c_str()); | ||||
"\n" | return EXIT_FAILURE; | ||||
"Options:\n" | } | ||||
"-h <host> Hostname of the DNS seed\n" | if (HelpRequested(gArgs)) { | ||||
"-n <ns> Hostname of the nameserver\n" | std::string strUsage = "Bitcoin-cash-seeder\nUsage: bitcoin-seeder " | ||||
"-m <mbox> E-Mail address reported in SOA records\n" | "-host=<host> -ns=<ns> [-mbox=<mbox>] " | ||||
"-t <threads> Number of crawlers to run in parallel (default " | "[-threads=<threads>] [-port=<port>]\n\n" + | ||||
"96)\n" | gArgs.GetHelpMessage(); | ||||
"-d <threads> Number of DNS server threads (default 4)\n" | |||||
"-p <port> UDP port to listen on (default 53)\n" | fprintf(stdout, "%s", strUsage.c_str()); | ||||
"-o <ip:port> Tor proxy IP/Port\n" | return EXIT_SUCCESS; | ||||
"-i <ip:port> IPV4 SOCKS5 proxy IP/Port\n" | } | ||||
"-k <ip:port> IPV6 SOCKS5 proxy IP/Port\n" | |||||
"-w f1,f2,... Allow these flag combinations as filters\n" | nThreads = gArgs.GetArg("-threads", DEFAULT_NUM_THREADS); | ||||
"--testnet Use testnet\n" | nPort = gArgs.GetArg("-port", DEFAULT_PORT); | ||||
"--wipeban Wipe list of banned nodes\n" | nDnsThreads = gArgs.GetArg("-dnsthreads", DEFAULT_NUM_DNS_THREADS); | ||||
"--wipeignore Wipe list of ignored nodes\n" | fWipeBan = gArgs.GetBoolArg("-wipeban", DEFAULT_WIPE_BAN); | ||||
"-?, --help Show this text\n" | fWipeIgnore = gArgs.GetBoolArg("-wipeignore", DEFAULT_WIPE_IGNORE); | ||||
"\n"; | mbox = gArgs.GetArg("-mbox", DEFAULT_EMAIL); | ||||
bool showHelp = false; | ns = gArgs.GetArg("-ns", DEFAULT_NAMESERVER); | ||||
host = gArgs.GetArg("-host", DEFAULT_HOST); | |||||
while (1) { | tor = gArgs.GetArg("-onion", DEFAULT_TOR_PROXY); | ||||
static struct option long_options[] = { | ipv4_proxy = gArgs.GetArg("-proxyipv4", DEFAULT_IPV4_PROXY); | ||||
{"host", required_argument, 0, 'h'}, | ipv6_proxy = gArgs.GetArg("-proxyipv6", DEFAULT_IPV6_PROXY); | ||||
{"ns", required_argument, 0, 'n'}, | SelectParams(gArgs.GetChainName()); | ||||
{"mbox", required_argument, 0, 'm'}, | |||||
{"threads", required_argument, 0, 't'}, | if (gArgs.IsArgSet("-filter")) { | ||||
{"dnsthreads", required_argument, 0, 'd'}, | // Parse whitelist additions | ||||
{"port", required_argument, 0, 'p'}, | std::string flagString = gArgs.GetArg("-filter", ""); | ||||
{"onion", required_argument, 0, 'o'}, | size_t flagstartpos = 0; | ||||
{"proxyipv4", required_argument, 0, 'i'}, | while (flagstartpos < flagString.size()) { | ||||
{"proxyipv6", required_argument, 0, 'k'}, | size_t flagendpos = flagString.find_first_of(',', flagstartpos); | ||||
{"filter", required_argument, 0, 'w'}, | uint64_t flag = atoi64(flagString.substr( | ||||
{"testnet", no_argument, &fUseTestNet, 1}, | flagstartpos, (flagendpos - flagstartpos))); | ||||
{"wipeban", no_argument, &fWipeBan, 1}, | filter_whitelist.insert(flag); | ||||
{"wipeignore", no_argument, &fWipeIgnore, 1}, | if (flagendpos == std::string::npos) { | ||||
{"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 = std::string(optarg); | |||||
break; | |||||
} | |||||
case 'm': { | |||||
mbox = std::string(optarg); | |||||
break; | |||||
} | |||||
case 'n': { | |||||
ns = std::string(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 = std::string(optarg); | |||||
break; | |||||
} | |||||
case 'i': { | |||||
ipv4_proxy = std::string(optarg); | |||||
break; | |||||
} | |||||
case 'k': { | |||||
ipv6_proxy = std::string(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; | break; | ||||
} | } | ||||
flagstartpos = flagendpos + 1; | |||||
} | } | ||||
} | } | ||||
if (filter_whitelist.empty()) { | if (filter_whitelist.empty()) { | ||||
filter_whitelist.insert(NODE_NETWORK); | filter_whitelist.insert(NODE_NETWORK); | ||||
filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM); | filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM); | ||||
filter_whitelist.insert(NODE_NETWORK | NODE_XTHIN); | filter_whitelist.insert(NODE_NETWORK | NODE_XTHIN); | ||||
filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM | NODE_XTHIN); | filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM | NODE_XTHIN); | ||||
} | } | ||||
if (!host.empty() && ns.empty()) showHelp = true; | return CONTINUE_EXECUTION; | ||||
if (showHelp) fprintf(stderr, help, argv[0]); | } | ||||
private: | |||||
void SetupSeederArgs() { | |||||
gArgs.AddArg("-?", _("Print this help message and exit"), false, | |||||
OptionsCategory::OPTIONS); | |||||
gArgs.AddArg("-host=<host>", _("Hostname of the DNS seed"), false, | |||||
OptionsCategory::OPTIONS); | |||||
gArgs.AddArg("-ns=<ns>", _("Hostname of the nameserver"), false, | |||||
OptionsCategory::OPTIONS); | |||||
gArgs.AddArg("-mbox=<mbox>", | |||||
_("E-Mail address reported in SOA records"), false, | |||||
OptionsCategory::OPTIONS); | |||||
gArgs.AddArg("-threads=<threads>", | |||||
_("Number of crawlers to run in parallel (default 96)"), | |||||
false, OptionsCategory::OPTIONS); | |||||
gArgs.AddArg("-dnsthreads=<threads>", | |||||
_("Number of DNS server threads (default 4)"), false, | |||||
OptionsCategory::OPTIONS); | |||||
gArgs.AddArg("-port=<port>", _("UDP port to listen on (default 53)"), | |||||
false, OptionsCategory::CONNECTION); | |||||
gArgs.AddArg("-onion=<ip:port>", _("Tor proxy IP/Port"), false, | |||||
OptionsCategory::CONNECTION); | |||||
gArgs.AddArg("-proxyipv4=<ip:port>", _("IPV4 SOCKS5 proxy IP/Port"), | |||||
false, OptionsCategory::CONNECTION); | |||||
gArgs.AddArg("-proxyipv6=<ip:port>", _("IPV6 SOCKS5 proxy IP/Port"), | |||||
false, OptionsCategory::CONNECTION); | |||||
gArgs.AddArg("-filter=<f1,f2,...>", | |||||
_("Allow these flag combinations as filters"), false, | |||||
OptionsCategory::OPTIONS); | |||||
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-debug", | |||||
_("Show all debugging options (usage: --help -help-debug)"), false, | |||||
OptionsCategory::DEBUG_TEST); | |||||
SetupChainParamsBaseOptions(); | |||||
gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); | |||||
gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); | |||||
} | } | ||||
}; | }; | ||||
extern "C" { | extern "C" { | ||||
#include <seeder/dns.h> | #include <seeder/dns.h> | ||||
} | } | ||||
CAddrDb db; | CAddrDb db; | ||||
▲ Show 20 Lines • Show All 266 Lines • ▼ Show 20 Lines | do { | ||||
stats.nNew, stats.nAvail - stats.nTracked - stats.nNew, | stats.nNew, stats.nAvail - stats.nTracked - stats.nNew, | ||||
stats.nBanned, (unsigned long long)requests, | stats.nBanned, (unsigned long long)requests, | ||||
(unsigned long long)queries); | (unsigned long long)queries); | ||||
Sleep(1000); | Sleep(1000); | ||||
} while (1); | } while (1); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
static const std::string mainnet_seeds[] = { | |||||
"seed.bitcoinabc.org", "seed-abc.bitcoinforks.org", | |||||
"seed.bitprim.org", "seed.deadalnix.me", | |||||
"seed.bchd.cash", ""}; | |||||
static const std::string testnet_seeds[] = { | |||||
"testnet-seed.bitcoinabc.org", "testnet-seed-abc.bitcoinforks.org", | |||||
"testnet-seed.bitprim.org", "testnet-seed.deadalnix.me", | |||||
"testnet-seed.bchd.cash", ""}; | |||||
static const std::string *seeds = mainnet_seeds; | |||||
const static unsigned int MAX_HOSTS_PER_SEED = 128; | const static unsigned int MAX_HOSTS_PER_SEED = 128; | ||||
extern "C" void *ThreadSeeder(void *) { | extern "C" void *ThreadSeeder(void *) { | ||||
do { | do { | ||||
for (int i = 0; seeds[i] != ""; i++) { | for (const std::string &seed : Params().DNSSeeds()) { | ||||
std::vector<CNetAddr> ips; | std::vector<CNetAddr> ips; | ||||
LookupHost(seeds[i].c_str(), ips, MAX_HOSTS_PER_SEED, true); | LookupHost(seed.c_str(), ips, MAX_HOSTS_PER_SEED, true); | ||||
for (auto &ip : ips) { | for (auto &ip : ips) { | ||||
db.Add(CAddress(CService(ip, GetDefaultPort()), ServiceFlags()), | db.Add(CAddress(CService(ip, GetDefaultPort()), ServiceFlags()), | ||||
true); | true); | ||||
} | } | ||||
} | } | ||||
Sleep(1800000); | Sleep(1800000); | ||||
} while (1); | } while (1); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||
// The logger dump everything on the console by default. | // The logger dump everything on the console by default. | ||||
LogInstance().m_print_to_console = true; | LogInstance().m_print_to_console = true; | ||||
signal(SIGPIPE, SIG_IGN); | signal(SIGPIPE, SIG_IGN); | ||||
setbuf(stdout, nullptr); | setbuf(stdout, nullptr); | ||||
CDnsSeedOpts opts; | CDnsSeedOpts opts; | ||||
opts.ParseCommandLine(argc, argv); | int parseResults = opts.ParseCommandLine(argc, argv); | ||||
if (parseResults != CONTINUE_EXECUTION) { | |||||
return parseResults; | |||||
} | |||||
fprintf(stdout, "Supporting whitelisted filters: "); | fprintf(stdout, "Supporting whitelisted filters: "); | ||||
for (std::set<uint64_t>::const_iterator it = opts.filter_whitelist.begin(); | for (std::set<uint64_t>::const_iterator it = opts.filter_whitelist.begin(); | ||||
it != opts.filter_whitelist.end(); it++) { | it != opts.filter_whitelist.end(); it++) { | ||||
if (it != opts.filter_whitelist.begin()) { | if (it != opts.filter_whitelist.begin()) { | ||||
fprintf(stdout, ","); | fprintf(stdout, ","); | ||||
} | } | ||||
fprintf(stdout, "0x%lx", (unsigned long)*it); | fprintf(stdout, "0x%lx", (unsigned long)*it); | ||||
} | } | ||||
Show All 18 Lines | if (!opts.ipv6_proxy.empty()) { | ||||
CService service(LookupNumeric(opts.ipv6_proxy.c_str(), 9050)); | CService service(LookupNumeric(opts.ipv6_proxy.c_str(), 9050)); | ||||
if (service.IsValid()) { | if (service.IsValid()) { | ||||
fprintf(stdout, "Using IPv6 proxy at %s\n", | fprintf(stdout, "Using IPv6 proxy at %s\n", | ||||
service.ToStringIPPort().c_str()); | service.ToStringIPPort().c_str()); | ||||
SetProxy(NET_IPV6, proxyType(service)); | SetProxy(NET_IPV6, proxyType(service)); | ||||
} | } | ||||
} | } | ||||
bool fDNS = true; | bool fDNS = true; | ||||
if (opts.fUseTestNet) { | fprintf(stdout, "Using %s.\n", gArgs.GetChainName().c_str()); | ||||
fprintf(stdout, "Using testnet.\n"); | netMagic = Params().NetMagic(); | ||||
netMagic[0] = 0xf4; | |||||
netMagic[1] = 0xe5; | |||||
netMagic[2] = 0xf3; | |||||
netMagic[3] = 0xf4; | |||||
seeds = testnet_seeds; | |||||
fTestNet = true; | |||||
} | |||||
if (opts.ns.empty()) { | if (opts.ns.empty()) { | ||||
fprintf(stdout, "No nameserver set. Not starting DNS server.\n"); | fprintf(stdout, "No nameserver set. Not starting DNS server.\n"); | ||||
fDNS = false; | fDNS = false; | ||||
} | } | ||||
if (fDNS && opts.host.empty()) { | if (fDNS && opts.host.empty()) { | ||||
fprintf(stderr, "No hostname set. Please use -h.\n"); | fprintf(stderr, "No hostname set. Please use -h.\n"); | ||||
exit(1); | return EXIT_FAILURE; | ||||
} | } | ||||
if (fDNS && opts.mbox.empty()) { | if (fDNS && opts.mbox.empty()) { | ||||
fprintf(stderr, "No e-mail address set. Please use -m.\n"); | fprintf(stderr, "No e-mail address set. Please use -m.\n"); | ||||
exit(1); | return EXIT_FAILURE; | ||||
} | } | ||||
FILE *f = fsbridge::fopen("dnsseed.dat", "r"); | FILE *f = fsbridge::fopen("dnsseed.dat", "r"); | ||||
if (f) { | if (f) { | ||||
fprintf(stdout, "Loading dnsseed.dat..."); | fprintf(stdout, "Loading dnsseed.dat..."); | ||||
CAutoFile cf(f, SER_DISK, CLIENT_VERSION); | CAutoFile cf(f, SER_DISK, CLIENT_VERSION); | ||||
cf >> db; | cf >> db; | ||||
if (opts.fWipeBan) { | if (opts.fWipeBan) { | ||||
db.banned.clear(); | db.banned.clear(); | ||||
Show All 31 Lines | for (int i = 0; i < opts.nThreads; i++) { | ||||
pthread_create(&thread, &attr_crawler, ThreadCrawler, &opts.nThreads); | pthread_create(&thread, &attr_crawler, ThreadCrawler, &opts.nThreads); | ||||
} | } | ||||
pthread_attr_destroy(&attr_crawler); | pthread_attr_destroy(&attr_crawler); | ||||
fprintf(stdout, "done\n"); | fprintf(stdout, "done\n"); | ||||
pthread_create(&threadStats, nullptr, ThreadStats, nullptr); | pthread_create(&threadStats, nullptr, ThreadStats, nullptr); | ||||
pthread_create(&threadDump, nullptr, ThreadDumper, nullptr); | pthread_create(&threadDump, nullptr, ThreadDumper, nullptr); | ||||
void *res; | void *res; | ||||
pthread_join(threadDump, &res); | pthread_join(threadDump, &res); | ||||
return 0; | return EXIT_SUCCESS; | ||||
} | } |