diff --git a/src/seeder/bitcoin.cpp b/src/seeder/bitcoin.cpp index 6c7a1e483..8c68de1b2 100644 --- a/src/seeder/bitcoin.cpp +++ b/src/seeder/bitcoin.cpp @@ -1,330 +1,329 @@ // Copyright (c) 2017-2019 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 -// The network magic to use. -CMessageHeader::MessageMagic netMagic = {{0xe3, 0xe1, 0xf3, 0xe8}}; - #define BITCOIN_SEED_NONCE 0x0539a019ca550825ULL static const uint32_t allones(-1); void CSeederNode::BeginMessage(const char *pszCommand) { if (nHeaderStart != allones) { AbortMessage(); } nHeaderStart = vSend.size(); - vSend << CMessageHeader(netMagic, pszCommand, 0); + vSend << CMessageHeader(Params().NetMagic(), pszCommand, 0); nMessageStart = vSend.size(); // tfm::format(std::cout, "%s: SEND %s\n", ToString(you), // pszCommand); } void CSeederNode::AbortMessage() { if (nHeaderStart == allones) { return; } vSend.resize(nHeaderStart); nHeaderStart = allones; nMessageStart = allones; } void CSeederNode::EndMessage() { if (nHeaderStart == allones) { return; } uint32_t nSize = vSend.size() - nMessageStart; memcpy((char *)&vSend[nHeaderStart] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize)); if (vSend.GetVersion() >= 209) { uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end()); unsigned int nChecksum = 0; memcpy(&nChecksum, &hash, sizeof(nChecksum)); assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, pchChecksum) + sizeof(nChecksum)); memcpy((char *)&vSend[nHeaderStart] + offsetof(CMessageHeader, pchChecksum), &nChecksum, sizeof(nChecksum)); } nHeaderStart = allones; nMessageStart = allones; } void CSeederNode::Send() { if (sock == INVALID_SOCKET) { return; } if (vSend.empty()) { return; } int nBytes = send(sock, &vSend[0], vSend.size(), 0); if (nBytes > 0) { vSend.erase(vSend.begin(), vSend.begin() + nBytes); } else { close(sock); sock = INVALID_SOCKET; } } void CSeederNode::PushVersion() { int64_t nTime = time(nullptr); uint64_t nLocalNonce = BITCOIN_SEED_NONCE; int64_t nLocalServices = 0; CService myService; CAddress me(myService, ServiceFlags(NODE_NETWORK)); BeginMessage(NetMsgType::VERSION); int nBestHeight = GetRequireHeight(); std::string ver = "/bitcoin-cash-seeder:0.15/"; vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me << nLocalNonce << ver << nBestHeight; EndMessage(); } PeerMessagingState CSeederNode::ProcessMessage(std::string strCommand, CDataStream &recv) { // tfm::format(std::cout, "%s: RECV %s\n", ToString(you), // strCommand); if (strCommand == NetMsgType::VERSION) { int64_t nTime; CAddress addrMe; CAddress addrFrom; uint64_t nNonce = 1; uint64_t nServiceInt; recv >> nVersion >> nServiceInt >> nTime >> addrMe; you.nServices = ServiceFlags(nServiceInt); recv >> addrFrom >> nNonce; recv >> strSubVer; recv >> nStartingHeight; BeginMessage(NetMsgType::VERACK); EndMessage(); vSend.SetVersion(std::min(nVersion, PROTOCOL_VERSION)); return PeerMessagingState::AwaitingMessages; } if (strCommand == NetMsgType::VERACK) { vRecv.SetVersion(std::min(nVersion, PROTOCOL_VERSION)); // tfm::format(std::cout, "\n%s: version %i\n", ToString(you), // nVersion); if (vAddr) { BeginMessage(NetMsgType::GETADDR); EndMessage(); doneAfter = time(nullptr) + GetTimeout(); } else { doneAfter = time(nullptr) + 1; } return PeerMessagingState::AwaitingMessages; } if (strCommand == NetMsgType::ADDR && vAddr) { std::vector vAddrNew; recv >> vAddrNew; // tfm::format(std::cout, "%s: got %i addresses\n", // ToString(you), // (int)vAddrNew.size()); int64_t now = time(nullptr); std::vector::iterator it = vAddrNew.begin(); if (vAddrNew.size() > 1) { if (doneAfter == 0 || doneAfter > now + 1) { doneAfter = now + 1; } } while (it != vAddrNew.end()) { CAddress &addr = *it; // tfm::format(std::cout, "%s: got address %s\n", // ToString(you), // addr.ToString(), (int)(vAddr->size())); it++; if (addr.nTime <= 100000000 || addr.nTime > now + 600) { addr.nTime = now - 5 * 86400; } if (addr.nTime > now - 604800) { vAddr->push_back(addr); } // tfm::format(std::cout, "%s: added address %s (#%i)\n", // ToString(you), // addr.ToString(), (int)(vAddr->size())); if (vAddr->size() > ADDR_SOFT_CAP) { doneAfter = 1; return PeerMessagingState::Finished; } } return PeerMessagingState::AwaitingMessages; } return PeerMessagingState::AwaitingMessages; } bool CSeederNode::ProcessMessages() { if (vRecv.empty()) { return false; } + const CMessageHeader::MessageMagic netMagic = Params().NetMagic(); + do { CDataStream::iterator pstart = std::search( vRecv.begin(), vRecv.end(), BEGIN(netMagic), END(netMagic)); uint32_t nHeaderSize = GetSerializeSize(CMessageHeader(netMagic), vRecv.GetVersion()); if (vRecv.end() - pstart < nHeaderSize) { if (vRecv.size() > nHeaderSize) { vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); } break; } vRecv.erase(vRecv.begin(), pstart); std::vector vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); CMessageHeader hdr(netMagic); vRecv >> hdr; if (!hdr.IsValidWithoutConfig(netMagic)) { // tfm::format(std::cout, "%s: BAD (invalid header)\n", // ToString(you)); ban = 100000; return true; } std::string strCommand = hdr.GetCommand(); unsigned int nMessageSize = hdr.nMessageSize; if (nMessageSize > MAX_SIZE) { // tfm::format(std::cout, "%s: BAD (message too large)\n", // ToString(you)); ban = 100000; return true; } if (nMessageSize > vRecv.size()) { vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); break; } if (vRecv.GetVersion() >= 209) { uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) { continue; } } CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.GetType(), vRecv.GetVersion()); vRecv.ignore(nMessageSize); if (ProcessMessage(strCommand, vMsg) == PeerMessagingState::Finished) { return true; } // tfm::format(std::cout, "%s: done processing %s\n", // ToString(you), // strCommand); } while (1); return false; } CSeederNode::CSeederNode(const CService &ip, std::vector *vAddrIn) : sock(INVALID_SOCKET), vSend(SER_NETWORK, 0), vRecv(SER_NETWORK, 0), nHeaderStart(-1), nMessageStart(-1), nVersion(0), vAddr(vAddrIn), ban(0), doneAfter(0), you(ip, ServiceFlags(NODE_NETWORK)) { if (time(nullptr) > 1329696000) { vSend.SetVersion(209); vRecv.SetVersion(209); } } bool CSeederNode::Run() { // FIXME: This logic is duplicated with CConnman::ConnectNode for no // good reason. bool connected = false; proxyType proxy; if (you.IsValid()) { bool proxyConnectionFailed = false; if (GetProxy(you.GetNetwork(), proxy)) { sock = CreateSocket(proxy.proxy); if (sock == INVALID_SOCKET) { return false; } connected = ConnectThroughProxy( proxy, you.ToStringIP(), you.GetPort(), sock, nConnectTimeout, &proxyConnectionFailed); } else { // no proxy needed (none set for target network) sock = CreateSocket(you); if (sock == INVALID_SOCKET) { return false; } // no proxy needed (none set for target network) connected = ConnectSocketDirectly(you, sock, nConnectTimeout, false); } } if (!connected) { // tfm::format(std::cout, "Cannot connect to %s\n", // ToString(you)); CloseSocket(sock); return false; } PushVersion(); Send(); bool res = true; int64_t now; while (now = time(nullptr), ban == 0 && (doneAfter == 0 || doneAfter > now) && sock != INVALID_SOCKET) { char pchBuf[0x10000]; fd_set fdsetRecv; fd_set fdsetError; FD_ZERO(&fdsetRecv); FD_ZERO(&fdsetError); FD_SET(sock, &fdsetRecv); FD_SET(sock, &fdsetError); struct timeval wa; if (doneAfter) { wa.tv_sec = doneAfter - now; wa.tv_usec = 0; } else { wa.tv_sec = GetTimeout(); wa.tv_usec = 0; } int ret = select(sock + 1, &fdsetRecv, nullptr, &fdsetError, &wa); if (ret != 1) { if (!doneAfter) { res = false; } break; } int nBytes = recv(sock, pchBuf, sizeof(pchBuf), 0); int nPos = vRecv.size(); if (nBytes > 0) { vRecv.resize(nPos + nBytes); memcpy(&vRecv[nPos], pchBuf, nBytes); } else if (nBytes == 0) { // tfm::format(std::cout, "%s: BAD (connection closed // prematurely)\n", // ToString(you)); res = false; break; } else { // tfm::format(std::cout, "%s: BAD (connection error)\n", // ToString(you)); res = false; break; } ProcessMessages(); Send(); } if (sock == INVALID_SOCKET) { res = false; } close(sock); sock = INVALID_SOCKET; return (ban == 0) && res; } diff --git a/src/seeder/bitcoin.h b/src/seeder/bitcoin.h index 0f456859c..5613653f2 100644 --- a/src/seeder/bitcoin.h +++ b/src/seeder/bitcoin.h @@ -1,78 +1,75 @@ // Copyright (c) 2017-2019 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_BITCOIN_H #define BITCOIN_SEEDER_BITCOIN_H #include #include #include #include #include static inline unsigned short GetDefaultPort() { return Params().GetDefaultPort(); } // After the 1000th addr, the seeder will only add one more address per addr // message. static const unsigned int ADDR_SOFT_CAP = 1000; -// The network magic to use. -extern CMessageHeader::MessageMagic netMagic; - enum class PeerMessagingState { AwaitingMessages, Finished, }; class CSeederNode { private: SOCKET sock; CDataStream vSend; CDataStream vRecv; uint32_t nHeaderStart; uint32_t nMessageStart; int nVersion; std::string strSubVer; int nStartingHeight; std::vector *vAddr; int ban; int64_t doneAfter; CAddress you; int GetTimeout() { return you.IsTor() ? 120 : 30; } void BeginMessage(const char *pszCommand); void AbortMessage(); void EndMessage(); void Send(); void PushVersion(); bool ProcessMessages(); protected: PeerMessagingState ProcessMessage(std::string strCommand, CDataStream &recv); public: CSeederNode(const CService &ip, std::vector *vAddrIn); bool Run(); int GetBan() { return ban; } int GetClientVersion() { return nVersion; } std::string GetClientSubVersion() { return strSubVer; } int GetStartingHeight() { return nStartingHeight; } }; #endif // BITCOIN_SEEDER_BITCOIN_H diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp index 9e43fd99a..e654fa316 100644 --- a/src/seeder/main.cpp +++ b/src/seeder/main.cpp @@ -1,583 +1,582 @@ // 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 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_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 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), ipv4_proxy(DEFAULT_IPV4_PROXY), ipv6_proxy(DEFAULT_IPV6_PROXY) {} int ParseCommandLine(int argc, char **argv) { SetupSeederArgs(); 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); ipv4_proxy = gArgs.GetArg("-proxyipv4", DEFAULT_IPV4_PROXY); ipv6_proxy = gArgs.GetArg("-proxyipv6", DEFAULT_IPV6_PROXY); SelectParams(gArgs.GetChainName()); 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 | NODE_XTHIN); filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM | NODE_XTHIN); } return CONTINUE_EXECUTION; } private: void SetupSeederArgs() { SetupHelpOptions(gArgs); gArgs.AddArg("-help-debug", "Show all debugging options (usage: --help -help-debug)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); SetupChainParamsBaseOptions(); gArgs.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-host=", "Hostname of the DNS seed", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-ns=", "Hostname of the nameserver", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-mbox=", "E-Mail address reported in SOA records", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-threads=", "Number of crawlers to run in parallel (default 96)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-dnsthreads=", "Number of DNS server threads (default 4)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-port=", "UDP port to listen on (default 53)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-onion=", "Tor proxy IP/Port", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-proxyipv4=", "IPV4 SOCKS5 proxy IP/Port", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-proxyipv6=", "IPV6 SOCKS5 proxy IP/Port", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-filter=", "Allow these flag combinations as filters", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-wipeban", "Wipe list of banned nodes", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.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 = time(nullptr); 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 = ""; 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(); // 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; } time_t now = time(nullptr); 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.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", (unsigned long long)(time(nullptr)), 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 : Params().DNSSeeds()) { 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); 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()); - netMagic = Params().NetMagic(); 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; }