diff --git a/src/Makefile.am b/src/Makefile.am index 4eab2a26bd..40556b3618 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,575 +1,576 @@ # Copyright (c) 2013-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. DIST_SUBDIRS = secp256k1 univalue AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) AM_CPPFLAGS = $(HARDENED_CPPFLAGS) EXTRA_LIBRARIES = if EMBEDDED_UNIVALUE LIBUNIVALUE = univalue/libunivalue.la $(LIBUNIVALUE): $(wildcard univalue/lib/*) $(wildcard univalue/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) else LIBUNIVALUE = $(UNIVALUE_LIBS) endif BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS) BITCOIN_SEEDER_INCLUDES = -I$(srcdir)/seeder BITCOIN_SEEDER_INCLUDES += $(BITCOIN_INCLUDES) LIBBITCOIN_SERVER=libbitcoin_server.a LIBBITCOIN_COMMON=libbitcoin_common.a LIBBITCOIN_CONSENSUS=libbitcoin_consensus.a LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBBITCOINQT=qt/libbitcoinqt.a LIBSECP256K1=secp256k1/libsecp256k1.la if ENABLE_ZMQ LIBBITCOIN_ZMQ=libbitcoin_zmq.a endif if BUILD_BITCOIN_LIBS LIBBITCOINCONSENSUS=libbitcoinconsensus.la endif if BUILD_BITCOIN_SEEDER LIBBITCOIN_SEEDER=libbitcoin_seeder.a endif if ENABLE_WALLET LIBBITCOIN_WALLET=libbitcoin_wallet.a endif $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: EXTRA_LIBRARIES += \ $(LIBBITCOIN_CRYPTO) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_CLI) \ $(LIBBITCOIN_SEEDER) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS) bin_PROGRAMS = noinst_PROGRAMS = TESTS = BENCHMARKS = if BUILD_BITCOIND bin_PROGRAMS += bitcoind endif if BUILD_BITCOIN_SEEDER bin_PROGRAMS += bitcoin-seeder endif if BUILD_BITCOIN_UTILS bin_PROGRAMS += bitcoin-cli bitcoin-tx endif .PHONY: FORCE check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ addrdb.h \ addrman.h \ base58.h \ bloom.h \ blockencodings.h \ cashaddr.h \ cashaddrenc.h \ chain.h \ chainparams.h \ chainparamsbase.h \ chainparamsseeds.h \ checkpoints.h \ checkqueue.h \ clientversion.h \ coins.h \ compat.h \ compat/byteswap.h \ compat/endian.h \ compat/sanity.h \ compressor.h \ config.h \ consensus/consensus.h \ core_io.h \ core_memusage.h \ cuckoocache.h \ dstencode.h \ globals.h \ httprpc.h \ httpserver.h \ indirectmap.h \ init.h \ key.h \ keystore.h \ dbwrapper.h \ limitedmap.h \ memusage.h \ merkleblock.h \ miner.h \ net.h \ net_processing.h \ netaddress.h \ netbase.h \ netmessagemaker.h \ noui.h \ policy/fees.h \ policy/policy.h \ pow.h \ protocol.h \ random.h \ reverselock.h \ rpc/blockchain.h \ rpc/client.h \ rpc/misc.h \ rpc/protocol.h \ rpc/server.h \ rpc/tojson.h \ rpc/register.h \ scheduler.h \ script/scriptcache.h \ script/sigcache.h \ script/sign.h \ script/standard.h \ script/ismine.h \ streams.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ support/cleanse.h \ support/events.h \ support/lockedpool.h \ sync.h \ threadsafety.h \ threadinterrupt.h \ timedata.h \ torcontrol.h \ txdb.h \ txmempool.h \ ui_interface.h \ undo.h \ util.h \ utilmoneystr.h \ utiltime.h \ validation.h \ validationinterface.h \ versionbits.h \ wallet/coincontrol.h \ wallet/crypter.h \ wallet/db.h \ wallet/finaltx.h \ wallet/rpcdump.h \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ warnings.h \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h\ zmq/zmqnotificationinterface.h \ zmq/zmqpublishnotifier.h obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj @$(top_srcdir)/share/genbuild.sh $(abs_top_builddir)/src/obj/build.h \ $(abs_top_srcdir) libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h # server: shared between bitcoind and bitcoin-qt libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_server_a_SOURCES = \ addrman.cpp \ addrdb.cpp \ bloom.cpp \ blockencodings.cpp \ chain.cpp \ checkpoints.cpp \ config.cpp \ globals.cpp \ httprpc.cpp \ httpserver.cpp \ init.cpp \ dbwrapper.cpp \ merkleblock.cpp \ miner.cpp \ net.cpp \ net_processing.cpp \ noui.cpp \ policy/fees.cpp \ policy/policy.cpp \ pow.cpp \ rest.cpp \ rpc/abc.cpp \ rpc/blockchain.cpp \ rpc/mining.cpp \ rpc/misc.cpp \ rpc/net.cpp \ rpc/rawtransaction.cpp \ rpc/server.cpp \ script/scriptcache.cpp \ script/sigcache.cpp \ script/ismine.cpp \ timedata.cpp \ torcontrol.cpp \ txdb.cpp \ txmempool.cpp \ ui_interface.cpp \ validation.cpp \ validationinterface.cpp \ versionbits.cpp \ $(BITCOIN_CORE_H) if ENABLE_ZMQ libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_zmq_a_SOURCES = \ zmq/zmqabstractnotifier.cpp \ zmq/zmqnotificationinterface.cpp \ zmq/zmqpublishnotifier.cpp endif # wallet: shared between bitcoind and bitcoin-qt, but only linked # when wallet enabled libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/finaltx.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ $(BITCOIN_CORE_H) # crypto primitives library crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES) crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) crypto_libbitcoin_crypto_a_SOURCES = \ crypto/aes.cpp \ crypto/aes.h \ crypto/chacha20.h \ crypto/chacha20.cpp \ crypto/common.h \ crypto/hmac_sha256.cpp \ crypto/hmac_sha256.h \ crypto/hmac_sha512.cpp \ crypto/hmac_sha512.h \ crypto/ripemd160.cpp \ crypto/ripemd160.h \ crypto/sha1.cpp \ crypto/sha1.h \ crypto/sha256.cpp \ crypto/sha256.h \ crypto/sha512.cpp \ crypto/sha512.h # consensus: shared between all executables that validate any consensus rules. libbitcoin_consensus_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_consensus_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_consensus_a_SOURCES = \ amount.h \ arith_uint256.cpp \ arith_uint256.h \ consensus/merkle.cpp \ consensus/merkle.h \ consensus/params.h \ consensus/validation.h \ hash.cpp \ hash.h \ prevector.h \ primitives/block.cpp \ primitives/block.h \ primitives/transaction.cpp \ primitives/transaction.h \ pubkey.cpp \ pubkey.h \ script/bitcoinconsensus.cpp \ script/interpreter.cpp \ script/interpreter.h \ script/script.cpp \ script/script.h \ script/script_error.cpp \ script/script_error.h \ serialize.h \ tinyformat.h \ uint256.cpp \ uint256.h \ utilstrencodings.cpp \ utilstrencodings.h \ version.h # common: shared between bitcoind, and bitcoin-qt and non-server tools libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ amount.cpp \ base58.cpp \ cashaddr.cpp \ cashaddrenc.cpp \ chainparams.cpp \ config.cpp \ coins.cpp \ compressor.cpp \ dstencode.cpp \ globals.cpp \ core_read.cpp \ core_write.cpp \ key.cpp \ keystore.cpp \ netaddress.cpp \ netbase.cpp \ protocol.cpp \ scheduler.cpp \ script/sign.cpp \ script/standard.cpp \ warnings.cpp \ $(BITCOIN_CORE_H) # util: shared between all executables. # This library *must* be included to make sure that the glibc # backward-compatibility objects and their sanity checks are linked. libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_util_a_SOURCES = \ support/lockedpool.cpp \ chainparamsbase.cpp \ clientversion.cpp \ compat/glibc_sanity.cpp \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ random.cpp \ rpc/protocol.cpp \ support/cleanse.cpp \ sync.cpp \ threadinterrupt.cpp \ util.cpp \ utilmoneystr.cpp \ utilstrencodings.cpp \ utiltime.cpp \ $(BITCOIN_CORE_H) if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp endif # cli: shared between bitcoin-cli and bitcoin-qt libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_cli_a_SOURCES = \ rpc/client.cpp \ $(BITCOIN_CORE_H) # seeder library libbitcoin_seeder_a_CPPFLAGS = $(AM_CPPFLAGS) $(PIE_FLAGS) $(BITCOIN_SEEDER_INCLUDES) libbitcoin_seeder_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_seeder_a_SOURCES = \ seeder/bitcoin.cpp \ seeder/bitcoin.h \ seeder/compat.h \ seeder/db.cpp \ seeder/db.h \ seeder/dns.cpp \ seeder/dns.h \ seeder/netbase.cpp \ seeder/netbase.h \ seeder/protocol.cpp \ seeder/protocol.h \ seeder/strlcpy.h \ seeder/util.cpp \ seeder/util.h nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h # # bitcoind binary # bitcoind_SOURCES = bitcoind.cpp bitcoind_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) bitcoind_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS bitcoind_SOURCES += bitcoind-res.rc endif bitcoind_LDADD = \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_COMMON) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ $(LIBLEVELDB) \ $(LIBLEVELDB_SSE42) \ $(LIBMEMENV) \ $(LIBSECP256K1) bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) # bitcoin-cli binary # bitcoin_cli_SOURCES = bitcoin-cli.cpp bitcoin_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) bitcoin_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoin_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS bitcoin_cli_SOURCES += bitcoin-cli-res.rc endif bitcoin_cli_LDADD = \ $(LIBBITCOIN_CLI) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) bitcoin_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) # # bitcoin-seeder binary # bitcoin_seeder_SOURCES = seeder/main.cpp bitcoin_seeder_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_SEEDER_INCLUDES) bitcoin_seeder_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoin_seeder_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) bitcoin_seeder_LDADD = \ $(LIBBITCOIN_SEEDER) \ + $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) bitcoin_seeder_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) # # bitcoin-tx binary # bitcoin_tx_SOURCES = bitcoin-tx.cpp bitcoin_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) bitcoin_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS bitcoin_tx_SOURCES += bitcoin-tx-res.rc endif bitcoin_tx_LDADD = \ $(LIBUNIVALUE) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ $(LIBSECP256K1) bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) # # bitcoinconsensus library # if BUILD_BITCOIN_LIBS include_HEADERS = script/bitcoinconsensus.h libbitcoinconsensus_la_SOURCES = $(crypto_libbitcoin_crypto_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) if GLIBC_BACK_COMPAT libbitcoinconsensus_la_SOURCES += compat/glibc_compat.cpp endif libbitcoinconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) libbitcoinconsensus_la_LIBADD = $(LIBSECP256K1) libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) endif # CTAES_DIST = crypto/ctaes/bench.c CTAES_DIST += crypto/ctaes/ctaes.c CTAES_DIST += crypto/ctaes/ctaes.h CTAES_DIST += crypto/ctaes/README.md CTAES_DIST += crypto/ctaes/test.c CLEANFILES = $(EXTRA_LIBRARIES) CLEANFILES += *.gcda *.gcno CLEANFILES += compat/*.gcda compat/*.gcno CLEANFILES += consensus/*.gcda consensus/*.gcno CLEANFILES += crypto/*.gcda crypto/*.gcno CLEANFILES += policy/*.gcda policy/*.gcno CLEANFILES += primitives/*.gcda primitives/*.gcno CLEANFILES += script/*.gcda script/*.gcno CLEANFILES += support/*.gcda support/*.gcno CLEANFILES += univalue/*.gcda univalue/*.gcno CLEANFILES += wallet/*.gcda wallet/*.gcno CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno CLEANFILES += zmq/*.gcda zmq/*.gcno DISTCLEANFILES = obj/build.h EXTRA_DIST = $(CTAES_DIST) clean-local: -$(MAKE) -C secp256k1 clean -$(MAKE) -C univalue clean -rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno -rm -rf test/__pycache__ .rc.o: @test -f $(WINDRES) ## FIXME: How to get the appropriate modulename_CPPFLAGS in here? $(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@ .mm.o: $(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CXXFLAGS) $(QT_INCLUDES) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $< check-symbols: $(bin_PROGRAMS) if GLIBC_BACK_COMPAT @echo "Checking glibc back compat..." $(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py < $(bin_PROGRAMS) endif check-security: $(bin_PROGRAMS) if HARDEN @echo "Checking binary security..." $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS) endif %.pb.cc %.pb.h: %.proto @test -f $(PROTOC) $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$( #define BITCOIN_SEED_NONCE 0x0539a019ca550825ULL static const uint32_t allones(-1); class CNode { 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) { if (nHeaderStart != allones) { AbortMessage(); } nHeaderStart = vSend.size(); vSend << CMessageHeader(pszCommand, 0); nMessageStart = vSend.size(); // printf("%s: SEND %s\n", ToString(you).c_str(), pszCommand); } void AbortMessage() { if (nHeaderStart == allones) { return; } vSend.resize(nHeaderStart); nHeaderStart = allones; nMessageStart = allones; } void 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, nChecksum) + sizeof(nChecksum)); memcpy((char *)&vSend[nHeaderStart] + offsetof(CMessageHeader, nChecksum), &nChecksum, sizeof(nChecksum)); } nHeaderStart = allones; nMessageStart = allones; } void 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 PushVersion() { int64_t nTime = time(nullptr); uint64_t nLocalNonce = BITCOIN_SEED_NONCE; int64_t nLocalServices = 0; - CAddress me(CService("0.0.0.0")); + CAddress me(CSeederService("0.0.0.0")); BeginMessage("version"); int nBestHeight = GetRequireHeight(); std::string ver = "/bitcoin-cash-seeder:0.15/"; vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me << nLocalNonce << ver << nBestHeight; EndMessage(); } void GotVersion() { // printf("\n%s: version %i\n", ToString(you).c_str(), nVersion); if (vAddr) { BeginMessage("getaddr"); EndMessage(); doneAfter = time(nullptr) + GetTimeout(); } else { doneAfter = time(nullptr) + 1; } } bool ProcessMessage(std::string strCommand, CDataStream &vRecv) { // printf("%s: RECV %s\n", ToString(you).c_str(), // strCommand.c_str()); if (strCommand == "version") { int64_t nTime; CAddress addrMe; CAddress addrFrom; uint64_t nNonce = 1; vRecv >> nVersion >> you.nServices >> nTime >> addrMe; if (nVersion == 10300) nVersion = 300; if (nVersion >= 106 && !vRecv.empty()) vRecv >> addrFrom >> nNonce; if (nVersion >= 106 && !vRecv.empty()) vRecv >> strSubVer; if (nVersion >= 209 && !vRecv.empty()) vRecv >> nStartingHeight; if (nVersion >= 209) { BeginMessage("verack"); EndMessage(); } vSend.SetVersion(std::min(nVersion, PROTOCOL_VERSION)); if (nVersion < 209) { this->vRecv.SetVersion(std::min(nVersion, PROTOCOL_VERSION)); GotVersion(); } return false; } if (strCommand == "verack") { this->vRecv.SetVersion(std::min(nVersion, PROTOCOL_VERSION)); GotVersion(); return false; } if (strCommand == "addr" && vAddr) { std::vector vAddrNew; vRecv >> vAddrNew; // printf("%s: got %i addresses\n", ToString(you).c_str(), // (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; // printf("%s: got address %s\n", ToString(you).c_str(), // addr.ToString().c_str(), (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); // printf("%s: added address %s (#%i)\n", // ToString(you).c_str(), addr.ToString().c_str(), // (int)(vAddr->size())); if (vAddr->size() > 1000) { doneAfter = 1; return true; } } return false; } return false; } bool ProcessMessages() { if (vRecv.empty()) { return false; } do { CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); uint32_t nHeaderSize = GetSerializeSize( CMessageHeader(), vRecv.GetType(), 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; vRecv >> hdr; if (!hdr.IsValid()) { // printf("%s: BAD (invalid header)\n", ToString(you).c_str()); ban = 100000; return true; } std::string strCommand = hdr.GetCommand(); unsigned int nMessageSize = hdr.nMessageSize; if (nMessageSize > MAX_SIZE) { // printf("%s: BAD (message too large)\n", // ToString(you).c_str()); 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); unsigned int nChecksum = 0; memcpy(&nChecksum, &hash, sizeof(nChecksum)); if (nChecksum != hdr.nChecksum) continue; } CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.GetType(), vRecv.GetVersion()); vRecv.ignore(nMessageSize); if (ProcessMessage(strCommand, vMsg)) return true; // printf("%s: done processing %s\n", ToString(you).c_str(), // strCommand.c_str()); } while (1); return false; } public: - CNode(const CService &ip, std::vector *vAddrIn) + CNode(const CSeederService &ip, std::vector *vAddrIn) : vSend(SER_NETWORK, 0), vRecv(SER_NETWORK, 0), nHeaderStart(-1), nMessageStart(-1), nVersion(0), vAddr(vAddrIn), ban(0), doneAfter(0), you(ip) { if (time(nullptr) > 1329696000) { vSend.SetVersion(209); vRecv.SetVersion(209); } } bool Run() { bool res = true; if (!ConnectSocket(you, sock)) return false; PushVersion(); Send(); int64_t now; while (now = time(nullptr), ban == 0 && (doneAfter == 0 || doneAfter > now) && sock != INVALID_SOCKET) { char pchBuf[0x10000]; fd_set set; FD_ZERO(&set); FD_SET(sock, &set); 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, &set, nullptr, &set, &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) { // printf("%s: BAD (connection closed prematurely)\n", // ToString(you).c_str()); res = false; break; } else { // printf("%s: BAD (connection error)\n", // ToString(you).c_str()); res = false; break; } ProcessMessages(); Send(); } if (sock == INVALID_SOCKET) res = false; close(sock); sock = INVALID_SOCKET; return (ban == 0) && res; } int GetBan() { return ban; } int GetClientVersion() { return nVersion; } std::string GetClientSubVersion() { return strSubVer; } int GetStartingHeight() { return nStartingHeight; } }; -bool TestNode(const CService &cip, int &ban, int &clientV, +bool TestNode(const CSeederService &cip, int &ban, int &clientV, std::string &clientSV, int &blocks, std::vector *vAddr) { try { CNode node(cip, vAddr); bool ret = node.Run(); if (!ret) { ban = node.GetBan(); } else { ban = 0; } clientV = node.GetClientVersion(); clientSV = node.GetClientSubVersion(); blocks = node.GetStartingHeight(); // printf("%s: %s!!!\n", cip.ToString().c_str(), ret ? "GOOD" : "BAD"); return ret; } catch (std::ios_base::failure &e) { ban = 0; return false; } } /* int main(void) { - CService ip("bitcoin.sipa.be", 8333, true); + CSeederService ip("bitcoin.sipa.be", 8333, true); std::vector vAddr; vAddr.clear(); int ban = 0; bool ret = TestNode(ip, ban, vAddr); printf("ret=%s ban=%i vAddr.size()=%i\n", ret ? "good" : "bad", ban, (int)vAddr.size()); } */ diff --git a/src/seeder/bitcoin.h b/src/seeder/bitcoin.h index 997f9b89a7..5070814617 100644 --- a/src/seeder/bitcoin.h +++ b/src/seeder/bitcoin.h @@ -1,9 +1,9 @@ #ifndef BITCOIN_SEEDER_BITCOIN_H #define BITCOIN_SEEDER_BITCOIN_H #include "protocol.h" -bool TestNode(const CService &cip, int &ban, int &client, std::string &clientSV, - int &blocks, std::vector *vAddr); +bool TestNode(const CSeederService &cip, int &ban, int &client, + std::string &clientSV, int &blocks, std::vector *vAddr); #endif diff --git a/src/seeder/db.cpp b/src/seeder/db.cpp index b39f53b4e9..10878903a8 100644 --- a/src/seeder/db.cpp +++ b/src/seeder/db.cpp @@ -1,229 +1,229 @@ #include "db.h" #include void CAddrInfo::Update(bool good) { int64_t now = time(nullptr); if (ourLastTry == 0) ourLastTry = now - MIN_RETRY; int age = now - ourLastTry; lastTry = now; ourLastTry = now; total++; if (good) { success++; ourLastSuccess = now; } stat2H.Update(good, age, 3600 * 2); stat8H.Update(good, age, 3600 * 8); stat1D.Update(good, age, 3600 * 24); stat1W.Update(good, age, 3600 * 24 * 7); stat1M.Update(good, age, 3600 * 24 * 30); int64_t ign = GetIgnoreTime(); if (ign && (ignoreTill == 0 || ignoreTill < ign + now)) ignoreTill = ign + now; // printf("%s: got %s result: success=%i/%i; 2H:%.2f%%-%.2f%%(%.2f) // 8H:%.2f%%-%.2f%%(%.2f) 1D:%.2f%%-%.2f%%(%.2f) 1W:%.2f%%-%.2f%%(%.2f) // \n", ToString(ip).c_str(), good ? "good" : "bad", success, total, // 100.0 * stat2H.reliability, 100.0 * (stat2H.reliability + 1.0 - // stat2H.weight), stat2H.count, // 100.0 * stat8H.reliability, 100.0 * (stat8H.reliability + 1.0 - // stat8H.weight), stat8H.count, // 100.0 * stat1D.reliability, 100.0 * (stat1D.reliability + 1.0 - // stat1D.weight), stat1D.count, // 100.0 * stat1W.reliability, 100.0 * (stat1W.reliability + 1.0 - // stat1W.weight), stat1W.count); } -bool CAddrDb::Get_(CServiceResult &ip, int &wait) { +bool CAddrDb::Get_(CSeederServiceResult &ip, int &wait) { int64_t now = time(nullptr); size_t tot = unkId.size() + ourId.size(); if (tot == 0) { wait = 5; return false; } do { size_t rnd = rand() % tot; int ret; if (rnd < unkId.size()) { std::set::iterator it = unkId.end(); it--; ret = *it; unkId.erase(it); } else { ret = ourId.front(); if (time(nullptr) - idToInfo[ret].ourLastTry < MIN_RETRY) { return false; } ourId.pop_front(); } if (idToInfo[ret].ignoreTill && idToInfo[ret].ignoreTill < now) { ourId.push_back(ret); idToInfo[ret].ourLastTry = now; } else { ip.service = idToInfo[ret].ip; ip.ourLastSuccess = idToInfo[ret].ourLastSuccess; break; } } while (1); nDirty++; return true; } -int CAddrDb::Lookup_(const CService &ip) { +int CAddrDb::Lookup_(const CSeederService &ip) { if (ipToId.count(ip)) return ipToId[ip]; return -1; } -void CAddrDb::Good_(const CService &addr, int clientV, std::string clientSV, - int blocks) { +void CAddrDb::Good_(const CSeederService &addr, int clientV, + std::string clientSV, int blocks) { int id = Lookup_(addr); if (id == -1) return; unkId.erase(id); banned.erase(addr); CAddrInfo &info = idToInfo[id]; info.clientVersion = clientV; info.clientSubVersion = clientSV; info.blocks = blocks; info.Update(true); if (info.IsGood() && goodId.count(id) == 0) { goodId.insert(id); // printf("%s: good; %i good nodes now\n", ToString(addr).c_str(), // (int)goodId.size()); } nDirty++; ourId.push_back(id); } -void CAddrDb::Bad_(const CService &addr, int ban) { +void CAddrDb::Bad_(const CSeederService &addr, int ban) { int id = Lookup_(addr); if (id == -1) return; unkId.erase(id); CAddrInfo &info = idToInfo[id]; info.Update(false); uint32_t now = time(nullptr); int ter = info.GetBanTime(); if (ter) { // printf("%s: terrible\n", ToString(addr).c_str()); if (ban < ter) ban = ter; } if (ban > 0) { // printf("%s: ban for %i seconds\n", ToString(addr).c_str(), ban); banned[info.ip] = ban + now; ipToId.erase(info.ip); goodId.erase(id); idToInfo.erase(id); } else { if (/*!info.IsGood() && */ goodId.count(id) == 1) { goodId.erase(id); // printf("%s: not good; %i good nodes left\n", // ToString(addr).c_str(), (int)goodId.size()); } ourId.push_back(id); } nDirty++; } -void CAddrDb::Skipped_(const CService &addr) { +void CAddrDb::Skipped_(const CSeederService &addr) { int id = Lookup_(addr); if (id == -1) return; unkId.erase(id); ourId.push_back(id); // printf("%s: skipped\n", ToString(addr).c_str()); nDirty++; } void CAddrDb::Add_(const CAddress &addr, bool force) { if (!force && !addr.IsRoutable()) { return; } - CService ipp(addr); + CSeederService ipp(addr); if (banned.count(ipp)) { time_t bantime = banned[ipp]; if (force || (bantime < time(nullptr) && addr.nTime > bantime)) { banned.erase(ipp); } else { return; } } if (ipToId.count(ipp)) { CAddrInfo &ai = idToInfo[ipToId[ipp]]; if (addr.nTime > ai.lastTry || ai.services != addr.nServices) { ai.lastTry = addr.nTime; ai.services |= addr.nServices; // printf("%s: updated\n", ToString(addr).c_str()); } if (force) { ai.ignoreTill = 0; } return; } CAddrInfo ai; ai.ip = ipp; ai.services = addr.nServices; ai.lastTry = addr.nTime; ai.ourLastTry = 0; ai.total = 0; ai.success = 0; int id = nId++; idToInfo[id] = ai; ipToId[ipp] = id; // printf("%s: added\n", ToString(ipp).c_str(), ipToId[ipp]); unkId.insert(id); nDirty++; } void CAddrDb::GetIPs_(std::set &ips, uint64_t requestedFlags, uint32_t max, const bool *nets) { if (goodId.size() == 0) { int id = -1; if (ourId.size() == 0) { if (unkId.size() == 0) { return; } id = *unkId.begin(); } else { id = *ourId.begin(); } if (id >= 0 && (idToInfo[id].services & requestedFlags) == requestedFlags) { ips.insert(idToInfo[id].ip); } return; } std::vector goodIdFiltered; for (auto &id : goodId) { if ((idToInfo[id].services & requestedFlags) == requestedFlags) { goodIdFiltered.push_back(id); } } if (!goodIdFiltered.size()) { return; } if (max > goodIdFiltered.size() / 2) { max = goodIdFiltered.size() / 2; } if (max < 1) { max = 1; } std::set ids; while (ids.size() < max) { ids.insert(goodIdFiltered[rand() % goodIdFiltered.size()]); } for (auto &id : ids) { - CService &ip = idToInfo[id].ip; + CSeederService &ip = idToInfo[id].ip; if (nets[ip.GetNetwork()]) { ips.insert(ip); } } } diff --git a/src/seeder/db.h b/src/seeder/db.h index 4713df6d18..ca9c653546 100644 --- a/src/seeder/db.h +++ b/src/seeder/db.h @@ -1,443 +1,443 @@ #ifndef BITCOIN_SEEDER_DB_H #define BITCOIN_SEEDER_DB_H #include "netbase.h" #include "protocol.h" #include "sync.h" #include "util.h" #include "version.h" #include #include #include #include #include #include #define MIN_RETRY 1000 #define REQUIRE_VERSION 70001 static inline int GetRequireHeight(const bool testnet = fTestNet) { return testnet ? 500000 : 350000; } -static inline std::string ToString(const CService &ip) { +static inline std::string ToString(const CSeederService &ip) { std::string str = ip.ToString(); while (str.size() < 22) { str += ' '; } return str; } class CAddrStat { private: float weight; float count; float reliability; public: CAddrStat() : weight(0), count(0), reliability(0) {} void Update(bool good, int64_t age, double tau) { double f = exp(-age / tau); reliability = reliability * f + (good ? (1.0 - f) : 0); count = count * f + 1; weight = weight * f + (1.0 - f); } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(weight); READWRITE(count); READWRITE(reliability); } friend class CAddrInfo; }; class CAddrReport { public: - CService ip; + CSeederService ip; int clientVersion; int blocks; double uptime[5]; std::string clientSubVersion; int64_t lastSuccess; bool fGood; uint64_t services; }; class CAddrInfo { private: - CService ip; + CSeederService ip; uint64_t services; int64_t lastTry; int64_t ourLastTry; int64_t ourLastSuccess; int64_t ignoreTill; CAddrStat stat2H; CAddrStat stat8H; CAddrStat stat1D; CAddrStat stat1W; CAddrStat stat1M; int clientVersion; int blocks; int total; int success; std::string clientSubVersion; public: CAddrInfo() : services(0), lastTry(0), ourLastTry(0), ourLastSuccess(0), ignoreTill(0), clientVersion(0), blocks(0), total(0), success(0) {} CAddrReport GetReport() const { CAddrReport ret; ret.ip = ip; ret.clientVersion = clientVersion; ret.clientSubVersion = clientSubVersion; ret.blocks = blocks; ret.uptime[0] = stat2H.reliability; ret.uptime[1] = stat8H.reliability; ret.uptime[2] = stat1D.reliability; ret.uptime[3] = stat1W.reliability; ret.uptime[4] = stat1M.reliability; ret.lastSuccess = ourLastSuccess; ret.fGood = IsGood(); ret.services = services; return ret; } bool IsGood() const { if (ip.GetPort() != GetDefaultPort()) return false; if (!(services & NODE_NETWORK)) return false; if (!ip.IsRoutable()) return false; if (clientVersion && clientVersion < REQUIRE_VERSION) return false; if (blocks && blocks < GetRequireHeight()) return false; if (total <= 3 && success * 2 >= total) return true; if (stat2H.reliability > 0.85 && stat2H.count > 2) return true; if (stat8H.reliability > 0.70 && stat8H.count > 4) return true; if (stat1D.reliability > 0.55 && stat1D.count > 8) return true; if (stat1W.reliability > 0.45 && stat1W.count > 16) return true; if (stat1M.reliability > 0.35 && stat1M.count > 32) return true; return false; } int64_t GetBanTime() const { if (IsGood()) { return 0; } if (clientVersion && clientVersion < 31900) { return 604800; } if (stat1M.reliability - stat1M.weight + 1.0 < 0.15 && stat1M.count > 32) { return 30 * 86400; } if (stat1W.reliability - stat1W.weight + 1.0 < 0.10 && stat1W.count > 16) { return 7 * 86400; } if (stat1D.reliability - stat1D.weight + 1.0 < 0.05 && stat1D.count > 8) { return 1 * 86400; } return 0; } int64_t GetIgnoreTime() const { if (IsGood()) { return 0; } if (stat1M.reliability - stat1M.weight + 1.0 < 0.20 && stat1M.count > 2) { return 10 * 86400; } if (stat1W.reliability - stat1W.weight + 1.0 < 0.16 && stat1W.count > 2) { return 3 * 86400; } if (stat1D.reliability - stat1D.weight + 1.0 < 0.12 && stat1D.count > 2) { return 8 * 3600; } if (stat8H.reliability - stat8H.weight + 1.0 < 0.08 && stat8H.count > 2) { return 2 * 3600; } return 0; } void Update(bool good); friend class CAddrDb; ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { uint8_t version = 4; READWRITE(version); READWRITE(ip); READWRITE(services); READWRITE(lastTry); uint8_t tried = ourLastTry != 0; READWRITE(tried); if (!tried) { return; } READWRITE(ourLastTry); READWRITE(ignoreTill); READWRITE(stat2H); READWRITE(stat8H); READWRITE(stat1D); READWRITE(stat1W); if (version >= 1) { READWRITE(stat1M); } else if (!ser_action.ForRead()) { *((CAddrStat *)(&stat1M)) = stat1W; } READWRITE(total); READWRITE(success); READWRITE(clientVersion); if (version >= 2) READWRITE(clientSubVersion); if (version >= 3) READWRITE(blocks); if (version >= 4) READWRITE(ourLastSuccess); } }; class CAddrDbStats { public: int nBanned; int nAvail; int nTracked; int nNew; int nGood; int nAge; }; -struct CServiceResult { - CService service; +struct CSeederServiceResult { + CSeederService service; bool fGood; int nBanTime; int nHeight; int nClientV; std::string strClientV; int64_t ourLastSuccess; }; /** * seen nodes * / \ * (a) banned nodes available nodes-------------- * / | \ * tracked nodes (b) unknown nodes (e) active nodes * / \ * (d) good nodes (c) non-good nodes */ class CAddrDb { private: mutable CCriticalSection cs; // number of address id's int nId; // map address id to address info (b,c,d,e) std::map idToInfo; // map ip to id (b,c,d,e) - std::map ipToId; + std::map ipToId; // sequence of tried nodes, in order we have tried connecting to them (c,d) std::deque ourId; // set of nodes not yet tried (b) std::set unkId; // set of good nodes (d, good e) std::set goodId; int nDirty; protected: // internal routines that assume proper locks are acquired // add an address void Add_(const CAddress &addr, bool force); // get an IP to test (must call Good_, Bad_, or Skipped_ on result // afterwards) - bool Get_(CServiceResult &ip, int &wait); - bool GetMany_(std::vector &ips, int max, int &wait); + bool Get_(CSeederServiceResult &ip, int &wait); + bool GetMany_(std::vector &ips, int max, int &wait); // mark an IP as good (must have been returned by Get_) - void Good_(const CService &ip, int clientV, std::string clientSV, + void Good_(const CSeederService &ip, int clientV, std::string clientSV, int blocks); // mark an IP as bad (and optionally ban it) (must have been returned by // Get_) - void Bad_(const CService &ip, int ban); + void Bad_(const CSeederService &ip, int ban); // mark an IP as skipped (must have been returned by Get_) - void Skipped_(const CService &ip); + void Skipped_(const CSeederService &ip); // look up id of an IP - int Lookup_(const CService &ip); + int Lookup_(const CSeederService &ip); // get a random set of IPs (shared lock only) void GetIPs_(std::set &ips, uint64_t requestedFlags, uint32_t max, const bool *nets); public: // nodes that are banned, with their unban time (a) - std::map banned; + std::map banned; void GetStats(CAddrDbStats &stats) { LOCK(cs); stats.nBanned = banned.size(); stats.nAvail = idToInfo.size(); stats.nTracked = ourId.size(); stats.nGood = goodId.size(); stats.nNew = unkId.size(); stats.nAge = time(nullptr) - idToInfo[ourId[0]].ourLastTry; } void ResetIgnores() { for (std::map::iterator it = idToInfo.begin(); it != idToInfo.end(); it++) { (*it).second.ignoreTill = 0; } } std::vector GetAll() { std::vector ret; LOCK(cs); for (std::deque::const_iterator it = ourId.begin(); it != ourId.end(); it++) { const CAddrInfo &info = idToInfo[*it]; if (info.success > 0) { ret.push_back(info.GetReport()); } } return ret; } // serialization code // format: // nVersion (0 for now) // n (number of ips in (b,c,d)) // CAddrInfo[n] // banned // acquires a shared lock (this does not suffice for read mode, but we // assume that only happens at startup, single-threaded) this way, dumping // does not interfere with GetIPs_, which is called from the DNS thread template void Serialize(Stream &s) const { LOCK(cs); int nVersion = 0; s << nVersion; CAddrDb *db = const_cast(this); int n = ourId.size() + unkId.size(); s << n; for (std::deque::const_iterator it = ourId.begin(); it != ourId.end(); it++) { std::map::iterator ci = db->idToInfo.find(*it); s << (*ci).second; } for (std::set::const_iterator it = unkId.begin(); it != unkId.end(); it++) { std::map::iterator ci = db->idToInfo.find(*it); s << (*ci).second; } s << banned; } template void Unserialize(Stream &s) { LOCK(cs); int nVersion; s >> nVersion; CAddrDb *db = const_cast(this); db->nId = 0; int n; s >> n; for (int i = 0; i < n; i++) { CAddrInfo info; s >> info; if (!info.GetBanTime()) { int id = db->nId++; db->idToInfo[id] = info; db->ipToId[info.ip] = id; if (info.ourLastTry) { db->ourId.push_back(id); if (info.IsGood()) db->goodId.insert(id); } else { db->unkId.insert(id); } } } db->nDirty++; s >> banned; } void Add(const CAddress &addr, bool fForce = false) { LOCK(cs); Add_(addr, fForce); } void Add(const std::vector &vAddr, bool fForce = false) { LOCK(cs); for (size_t i = 0; i < vAddr.size(); i++) { Add_(vAddr[i], fForce); } } - void Good(const CService &addr, int clientVersion, + void Good(const CSeederService &addr, int clientVersion, std::string clientSubVersion, int blocks) { LOCK(cs); Good_(addr, clientVersion, clientSubVersion, blocks); } - void Skipped(const CService &addr) { + void Skipped(const CSeederService &addr) { LOCK(cs); Skipped_(addr); } - void Bad(const CService &addr, int ban = 0) { + void Bad(const CSeederService &addr, int ban = 0) { LOCK(cs); Bad_(addr, ban); } - bool Get(CServiceResult &ip, int &wait) { + bool Get(CSeederServiceResult &ip, int &wait) { LOCK(cs); return Get_(ip, wait); } - void GetMany(std::vector &ips, int max, int &wait) { + void GetMany(std::vector &ips, int max, int &wait) { LOCK(cs); while (max > 0) { - CServiceResult ip = {}; + CSeederServiceResult ip = {}; if (!Get_(ip, wait)) { return; } ips.push_back(ip); max--; } } - void ResultMany(const std::vector &ips) { + void ResultMany(const std::vector &ips) { LOCK(cs); for (size_t i = 0; i < ips.size(); i++) { if (ips[i].fGood) { Good_(ips[i].service, ips[i].nClientV, ips[i].strClientV, ips[i].nHeight); } else { Bad_(ips[i].service, ips[i].nBanTime); } } } void GetIPs(std::set &ips, uint64_t requestedFlags, uint32_t max, const bool *nets) { LOCK(cs); GetIPs_(ips, requestedFlags, max, nets); } }; #endif diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp index 6391029a1e..69b4eff0dc 100644 --- a/src/seeder/main.cpp +++ b/src/seeder/main.cpp @@ -1,569 +1,569 @@ #include "bitcoin.h" #include "clientversion.h" #include "db.h" #include "dns.h" #include "streams.h" #include #include #include #include #include #include #include #include bool fTestNet = false; 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 "dns.h" } CAddrDb db; extern "C" void *ThreadCrawler(void *data) { int *nThreads = (int *)data; do { - std::vector ips; + 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]; + CSeederServiceResult &res = ips[i]; res.nBanTime = 0; res.nClientV = 0; res.nHeight = 0; res.strClientV = ""; bool getaddr = res.ourLastSuccess + 86400 < now; res.fGood = TestNode(res.service, res.nBanTime, res.nClientV, res.strClientV, res.nHeight, getaddr ? &addr : nullptr); } 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; dns_opt.ns = opts->ns; dns_opt.mbox = opts->mbox; 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 = fopen("dnsseed.dat.new", "w+"); if (f) { { CAutoFile cf(f, SER_DISK, CLIENT_VERSION); cf << db; } rename("dnsseed.dat.new", "dnsseed.dat"); } FILE *d = fopen("dnsseed.dump", "w"); fprintf(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) { fprintf( d, "%-47s %4d %11" PRId64 " %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 " %5i \"%s\"\n", rep.ip.ToString().c_str(), (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.c_str()); 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]; } fclose(d); FILE *ff = fopen("dnsstats.log", "a"); fprintf(ff, "%llu %g %g %g %g %g\n", (unsigned long long)(time(nullptr)), stat[0], stat[1], stat[2], stat[3], stat[4]); fclose(ff); } } 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; printf("\n\n\n\x1b[3A"); } else printf("\x1b[2K\x1b[u"); printf("\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; } printf("%s %i/%i available (%i tried in %is, %i new, %i active), %i " "banned; %llu DNS requests, %llu db queries", 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; } static const std::string mainnet_seeds[] = { "seed.bitcoinabc.org", "seed-abc.bitcoinforks.org", "seed.bitprim.org", "seed.deadalnix.me", "seeder.criptolayer.net", ""}; static const std::string testnet_seeds[] = { "testnet-seed.bitcoinabc.org", "testnet-seed-abc.bitcoinforks.org", "testnet-seed.bitprim.org", "testnet-seed.deadalnix.me", "testnet-seeder.criptolayer.net", ""}; static const std::string *seeds = mainnet_seeds; extern "C" void *ThreadSeeder(void *) { if (!fTestNet) { - db.Add(CService("kjy2eqzk4zwi5zd3.onion", 8333), true); + db.Add(CSeederService("kjy2eqzk4zwi5zd3.onion", 8333), true); } do { for (int i = 0; seeds[i] != ""; i++) { std::vector ips; LookupHost(seeds[i].c_str(), ips); for (auto &ip : ips) { - db.Add(CService(ip, GetDefaultPort()), true); + db.Add(CSeederService(ip, GetDefaultPort()), true); } } Sleep(1800000); } while (1); return nullptr; } int main(int argc, char **argv) { signal(SIGPIPE, SIG_IGN); setbuf(stdout, nullptr); CDnsSeedOpts opts; opts.ParseCommandLine(argc, argv); printf("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()) { printf(","); } printf("0x%lx", (unsigned long)*it); } printf("\n"); if (opts.tor) { - CService service(opts.tor, 9050); + CSeederService service(opts.tor, 9050); if (service.IsValid()) { printf("Using Tor proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_TOR, service); } } if (opts.ipv4_proxy) { - CService service(opts.ipv4_proxy, 9050); + CSeederService service(opts.ipv4_proxy, 9050); if (service.IsValid()) { printf("Using IPv4 proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_IPV4, service); } } if (opts.ipv6_proxy) { - CService service(opts.ipv6_proxy, 9050); + CSeederService service(opts.ipv6_proxy, 9050); if (service.IsValid()) { printf("Using IPv6 proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_IPV6, service); } } bool fDNS = true; if (opts.fUseTestNet) { printf("Using testnet.\n"); pchMessageStart[0] = 0xf4; pchMessageStart[1] = 0xe5; pchMessageStart[2] = 0xf3; pchMessageStart[3] = 0xf4; seeds = testnet_seeds; fTestNet = true; } if (!opts.ns) { printf("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); } if (fDNS && !opts.mbox) { fprintf(stderr, "No e-mail address set. Please use -m.\n"); exit(1); } FILE *f = fopen("dnsseed.dat", "r"); if (f) { printf("Loading dnsseed.dat..."); CAutoFile cf(f, SER_DISK, CLIENT_VERSION); cf >> db; if (opts.fWipeBan) db.banned.clear(); if (opts.fWipeIgnore) db.ResetIgnores(); printf("done\n"); } pthread_t threadDns, threadSeed, threadDump, threadStats; if (fDNS) { printf("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]); printf("."); Sleep(20); } printf("done\n"); } printf("Starting seeder..."); pthread_create(&threadSeed, nullptr, ThreadSeeder, nullptr); printf("done\n"); printf("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); printf("done\n"); pthread_create(&threadStats, nullptr, ThreadStats, nullptr); pthread_create(&threadDump, nullptr, ThreadDumper, nullptr); void *res; pthread_join(threadDump, &res); return 0; } diff --git a/src/seeder/netbase.cpp b/src/seeder/netbase.cpp index dc20da759d..3aa1a7b62f 100644 --- a/src/seeder/netbase.cpp +++ b/src/seeder/netbase.cpp @@ -1,1066 +1,649 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "netbase.h" #include "strlcpy.h" #include "util.h" #ifndef WIN32 #include #endif #include // for to_lower() #define printf my_printf // Settings -typedef std::pair proxyType; +typedef std::pair proxyType; static proxyType proxyInfo[NET_MAX]; static proxyType nameproxyInfo; int nConnectTimeout = 5000; bool fNameLookup = false; static const uint8_t pchIPv4[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; enum Network ParseNetwork(std::string net) { boost::to_lower(net); if (net == "ipv4") return NET_IPV4; if (net == "ipv6") return NET_IPV6; if (net == "tor") return NET_TOR; - if (net == "i2p") return NET_I2P; return NET_UNROUTABLE; } void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { size_t colon = in.find_last_of(':'); // if a : is found, and it either follows a [...], or no other : is in the // string, treat it as port separator bool fHaveColon = colon != in.npos; // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is // safe bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); bool fMultiColon = fHaveColon && (in.find_last_of(':', colon - 1) != in.npos); if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) { char *endp = nullptr; int n = strtol(in.c_str() + colon + 1, &endp, 10); if (endp && *endp == 0 && n >= 0) { in = in.substr(0, colon); if (n > 0 && n < 0x10000) portOut = n; } } if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') hostOut = in.substr(1, in.size() - 2); else hostOut = in; } static bool LookupIntern(const char *pszName, std::vector &vIP, unsigned int nMaxSolutions, bool fAllowLookup) { vIP.clear(); { CNetAddr addr; if (addr.SetSpecial(std::string(pszName))) { vIP.push_back(addr); return true; } } struct addrinfo aiHint; memset(&aiHint, 0, sizeof(struct addrinfo)); aiHint.ai_socktype = SOCK_STREAM; aiHint.ai_protocol = IPPROTO_TCP; #ifdef WIN32 aiHint.ai_family = AF_UNSPEC; aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST; #else aiHint.ai_family = AF_UNSPEC; aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST; #endif struct addrinfo *aiRes = nullptr; int nErr = getaddrinfo(pszName, nullptr, &aiHint, &aiRes); if (nErr) return false; struct addrinfo *aiTrav = aiRes; while (aiTrav != nullptr && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) { if (aiTrav->ai_family == AF_INET) { assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in)); vIP.push_back( CNetAddr(((struct sockaddr_in *)(aiTrav->ai_addr))->sin_addr)); } if (aiTrav->ai_family == AF_INET6) { assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6)); vIP.push_back(CNetAddr( ((struct sockaddr_in6 *)(aiTrav->ai_addr))->sin6_addr)); } aiTrav = aiTrav->ai_next; } freeaddrinfo(aiRes); return (vIP.size() > 0); } bool LookupHost(const char *pszName, std::vector &vIP, unsigned int nMaxSolutions, bool fAllowLookup) { if (pszName[0] == 0) return false; char psz[256]; char *pszHost = psz; strlcpy(psz, pszName, sizeof(psz)); if (psz[0] == '[' && psz[strlen(psz) - 1] == ']') { pszHost = psz + 1; psz[strlen(psz) - 1] = 0; } return LookupIntern(pszHost, vIP, nMaxSolutions, fAllowLookup); } bool LookupHostNumeric(const char *pszName, std::vector &vIP, unsigned int nMaxSolutions) { return LookupHost(pszName, vIP, nMaxSolutions, false); } -bool Lookup(const char *pszName, std::vector &vAddr, int portDefault, - bool fAllowLookup, unsigned int nMaxSolutions) { +bool Lookup(const char *pszName, std::vector &vAddr, + int portDefault, bool fAllowLookup, unsigned int nMaxSolutions) { if (pszName[0] == 0) return false; int port = portDefault; std::string hostname = ""; SplitHostPort(std::string(pszName), port, hostname); std::vector vIP; bool fRet = LookupIntern(hostname.c_str(), vIP, nMaxSolutions, fAllowLookup); if (!fRet) return false; vAddr.resize(vIP.size()); for (unsigned int i = 0; i < vIP.size(); i++) - vAddr[i] = CService(vIP[i], port); + vAddr[i] = CSeederService(vIP[i], port); return true; } -bool Lookup(const char *pszName, CService &addr, int portDefault, +bool Lookup(const char *pszName, CSeederService &addr, int portDefault, bool fAllowLookup) { - std::vector vService; + std::vector vService; bool fRet = Lookup(pszName, vService, portDefault, fAllowLookup, 1); if (!fRet) return false; addr = vService[0]; return true; } -bool LookupNumeric(const char *pszName, CService &addr, int portDefault) { +bool LookupNumeric(const char *pszName, CSeederService &addr, int portDefault) { return Lookup(pszName, addr, portDefault, false); } -static bool Socks4(const CService &addrDest, SOCKET &hSocket) { +static bool Socks4(const CSeederService &addrDest, SOCKET &hSocket) { printf("SOCKS4 connecting %s\n", addrDest.ToString().c_str()); if (!addrDest.IsIPv4()) { closesocket(hSocket); return error("Proxy destination is not IPv4"); } char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; struct sockaddr_in addr; socklen_t len = sizeof(addr); if (!addrDest.GetSockAddr((struct sockaddr *)&addr, &len) || addr.sin_family != AF_INET) { closesocket(hSocket); return error("Cannot get proxy destination address"); } memcpy(pszSocks4IP + 2, &addr.sin_port, 2); memcpy(pszSocks4IP + 4, &addr.sin_addr, 4); char *pszSocks4 = pszSocks4IP; int nSize = sizeof(pszSocks4IP); int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL); if (ret != nSize) { closesocket(hSocket); return error("Error sending to proxy"); } char pchRet[8]; if (recv(hSocket, pchRet, 8, 0) != 8) { closesocket(hSocket); return error("Error reading proxy response"); } if (pchRet[1] != 0x5a) { closesocket(hSocket); if (pchRet[1] != 0x5b) printf("ERROR: Proxy returned error %d\n", pchRet[1]); return false; } printf("SOCKS4 connected %s\n", addrDest.ToString().c_str()); return true; } static bool Socks5(std::string strDest, int port, SOCKET &hSocket) { printf("SOCKS5 connecting %s\n", strDest.c_str()); if (strDest.size() > 255) { closesocket(hSocket); return error("Hostname too long"); } char pszSocks5Init[] = "\5\1\0"; char *pszSocks5 = pszSocks5Init; ssize_t nSize = sizeof(pszSocks5Init) - 1; ssize_t ret = send(hSocket, pszSocks5, nSize, MSG_NOSIGNAL); if (ret != nSize) { closesocket(hSocket); return error("Error sending to proxy"); } char pchRet1[2]; if (recv(hSocket, pchRet1, 2, 0) != 2) { closesocket(hSocket); return error("Error reading proxy response"); } if (pchRet1[0] != 0x05 || pchRet1[1] != 0x00) { closesocket(hSocket); return error("Proxy failed to initialize"); } std::string strSocks5("\5\1"); strSocks5 += '\000'; strSocks5 += '\003'; strSocks5 += static_cast(std::min((int)strDest.size(), 255)); strSocks5 += strDest; strSocks5 += static_cast((port >> 8) & 0xFF); strSocks5 += static_cast((port >> 0) & 0xFF); ret = send(hSocket, strSocks5.c_str(), strSocks5.size(), MSG_NOSIGNAL); if (ret != (ssize_t)strSocks5.size()) { closesocket(hSocket); return error("Error sending to proxy"); } char pchRet2[4]; if (recv(hSocket, pchRet2, 4, 0) != 4) { closesocket(hSocket); return error("Error reading proxy response"); } if (pchRet2[0] != 0x05) { closesocket(hSocket); return error("Proxy failed to accept request"); } if (pchRet2[1] != 0x00) { closesocket(hSocket); switch (pchRet2[1]) { case 0x01: return error("Proxy error: general failure"); case 0x02: return error("Proxy error: connection not allowed"); case 0x03: return error("Proxy error: network unreachable"); case 0x04: return error("Proxy error: host unreachable"); case 0x05: return error("Proxy error: connection refused"); case 0x06: return error("Proxy error: TTL expired"); case 0x07: return error("Proxy error: protocol error"); case 0x08: return error("Proxy error: address type not supported"); default: return error("Proxy error: unknown"); } } if (pchRet2[2] != 0x00) { closesocket(hSocket); return error("Error: malformed proxy response"); } char pchRet3[256]; switch (pchRet2[3]) { case 0x01: ret = recv(hSocket, pchRet3, 4, 0) != 4; break; case 0x04: ret = recv(hSocket, pchRet3, 16, 0) != 16; break; case 0x03: { ret = recv(hSocket, pchRet3, 1, 0) != 1; if (ret) return error("Error reading from proxy"); int nRecv = pchRet3[0]; ret = recv(hSocket, pchRet3, nRecv, 0) != nRecv; break; } default: closesocket(hSocket); return error("Error: malformed proxy response"); } if (ret) { closesocket(hSocket); return error("Error reading from proxy"); } if (recv(hSocket, pchRet3, 2, 0) != 2) { closesocket(hSocket); return error("Error reading from proxy"); } printf("SOCKS5 connected %s\n", strDest.c_str()); return true; } -static bool ConnectSocketDirectly(const CService &addrConnect, +static bool ConnectSocketDirectly(const CSeederService &addrConnect, SOCKET &hSocketRet, int nTimeout) { hSocketRet = INVALID_SOCKET; struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); if (!addrConnect.GetSockAddr((struct sockaddr *)&sockaddr, &len)) { printf("Cannot connect to %s: unsupported network\n", addrConnect.ToString().c_str()); return false; } SOCKET hSocket = socket(((struct sockaddr *)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); if (hSocket == INVALID_SOCKET) return false; #ifdef SO_NOSIGPIPE int set = 1; setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); #endif #ifdef WIN32 u_long fNonblock = 1; if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) #else int fFlags = fcntl(hSocket, F_GETFL, 0); if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == -1) #endif { closesocket(hSocket); return false; } if (connect(hSocket, (struct sockaddr *)&sockaddr, len) == SOCKET_ERROR) { // WSAEINVAL is here because some legacy version of winsock uses it if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL) { struct timeval timeout; timeout.tv_sec = nTimeout / 1000; timeout.tv_usec = (nTimeout % 1000) * 1000; fd_set fdset; FD_ZERO(&fdset); FD_SET(hSocket, &fdset); int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout); if (nRet == 0) { printf("connection timeout\n"); closesocket(hSocket); return false; } if (nRet == SOCKET_ERROR) { printf("select() for connection failed: %i\n", WSAGetLastError()); closesocket(hSocket); return false; } socklen_t nRetSize = sizeof(nRet); #ifdef WIN32 if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char *)(&nRet), &nRetSize) == SOCKET_ERROR) #else if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR) #endif { printf("getsockopt() for connection failed: %i\n", WSAGetLastError()); closesocket(hSocket); return false; } if (nRet != 0) { printf("connect() failed after select(): %s\n", strerror(nRet)); closesocket(hSocket); return false; } } #ifdef WIN32 else if (WSAGetLastError() != WSAEISCONN) #else else #endif { printf("connect() failed: %i\n", WSAGetLastError()); closesocket(hSocket); return false; } } // this isn't even strictly necessary // CNode::ConnectNode immediately turns the socket back to non-blocking // but we'll turn it back to blocking just in case #ifdef WIN32 fNonblock = 0; if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) #else fFlags = fcntl(hSocket, F_GETFL, 0); if (fcntl(hSocket, F_SETFL, fFlags & !O_NONBLOCK) == SOCKET_ERROR) #endif { closesocket(hSocket); return false; } hSocketRet = hSocket; return true; } -bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion) { +bool SetProxy(enum Network net, CSeederService addrProxy, int nSocksVersion) { assert(net >= 0 && net < NET_MAX); if (nSocksVersion != 0 && nSocksVersion != 4 && nSocksVersion != 5) return false; if (nSocksVersion != 0 && !addrProxy.IsValid()) return false; proxyInfo[net] = std::make_pair(addrProxy, nSocksVersion); return true; } -bool GetProxy(enum Network net, CService &addrProxy) { +bool GetProxy(enum Network net, CSeederService &addrProxy) { assert(net >= 0 && net < NET_MAX); if (!proxyInfo[net].second) return false; addrProxy = proxyInfo[net].first; return true; } -bool SetNameProxy(CService addrProxy, int nSocksVersion) { +bool SetNameProxy(CSeederService addrProxy, int nSocksVersion) { if (nSocksVersion != 0 && nSocksVersion != 5) return false; if (nSocksVersion != 0 && !addrProxy.IsValid()) return false; nameproxyInfo = std::make_pair(addrProxy, nSocksVersion); return true; } bool GetNameProxy() { return nameproxyInfo.second != 0; } bool IsProxy(const CNetAddr &addr) { for (int i = 0; i < NET_MAX; i++) { if (proxyInfo[i].second && (addr == (CNetAddr)proxyInfo[i].first)) return true; } return false; } -bool ConnectSocket(const CService &addrDest, SOCKET &hSocketRet, int nTimeout) { +bool ConnectSocket(const CSeederService &addrDest, SOCKET &hSocketRet, + int nTimeout) { const proxyType &proxy = proxyInfo[addrDest.GetNetwork()]; // no proxy needed if (!proxy.second) return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); SOCKET hSocket = INVALID_SOCKET; // first connect to proxy server if (!ConnectSocketDirectly(proxy.first, hSocket, nTimeout)) return false; // do socks negotiation switch (proxy.second) { case 4: if (!Socks4(addrDest, hSocket)) return false; break; case 5: if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket)) return false; break; default: return false; } hSocketRet = hSocket; return true; } -bool ConnectSocketByName(CService &addr, SOCKET &hSocketRet, +bool ConnectSocketByName(CSeederService &addr, SOCKET &hSocketRet, const char *pszDest, int portDefault, int nTimeout) { std::string strDest; int port = portDefault; SplitHostPort(std::string(pszDest), port, strDest); SOCKET hSocket = INVALID_SOCKET; - CService addrResolved( - CNetAddr(strDest, fNameLookup && !nameproxyInfo.second), port); - if (addrResolved.IsValid()) { - addr = addrResolved; - return ConnectSocket(addr, hSocketRet, nTimeout); + CSeederService addrResolved; + if (Lookup(strDest.c_str(), addrResolved, port, + fNameLookup && !nameproxyInfo.second)) { + if (addrResolved.IsValid()) { + addr = addrResolved; + return ConnectSocket(addr, hSocketRet, nTimeout); + } } - addr = CService("0.0.0.0:0"); + + addr = CSeederService("0.0.0.0:0"); if (!nameproxyInfo.second) return false; if (!ConnectSocketDirectly(nameproxyInfo.first, hSocket, nTimeout)) return false; switch (nameproxyInfo.second) { default: case 4: return false; case 5: if (!Socks5(strDest, port, hSocket)) return false; break; } hSocketRet = hSocket; return true; } -void CNetAddr::Init() { - memset(ip, 0, 16); -} - -void CNetAddr::SetIP(const CNetAddr &ipIn) { - memcpy(ip, ipIn.ip, sizeof(ip)); -} - -static const uint8_t pchOnionCat[] = {0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43}; -static const uint8_t pchGarliCat[] = {0xFD, 0x60, 0xDB, 0x4D, 0xDD, 0xB5}; - -bool CNetAddr::SetSpecial(const std::string &strName) { - if (strName.size() > 6 && - strName.substr(strName.size() - 6, 6) == ".onion") { - std::vector vchAddr = - DecodeBase32(strName.substr(0, strName.size() - 6).c_str()); - if (vchAddr.size() != 16 - sizeof(pchOnionCat)) return false; - memcpy(ip, pchOnionCat, sizeof(pchOnionCat)); - for (unsigned int i = 0; i < 16 - sizeof(pchOnionCat); i++) - ip[i + sizeof(pchOnionCat)] = vchAddr[i]; - return true; - } - if (strName.size() > 11 && - strName.substr(strName.size() - 11, 11) == ".oc.b32.i2p") { - std::vector vchAddr = - DecodeBase32(strName.substr(0, strName.size() - 11).c_str()); - if (vchAddr.size() != 16 - sizeof(pchGarliCat)) return false; - memcpy(ip, pchOnionCat, sizeof(pchGarliCat)); - for (unsigned int i = 0; i < 16 - sizeof(pchGarliCat); i++) - ip[i + sizeof(pchGarliCat)] = vchAddr[i]; - return true; - } - return false; -} - -CNetAddr::CNetAddr() { - Init(); -} - -CNetAddr::CNetAddr(const struct in_addr &ipv4Addr) { - memcpy(ip, pchIPv4, 12); - memcpy(ip + 12, &ipv4Addr, 4); -} - -CNetAddr::CNetAddr(const struct in6_addr &ipv6Addr) { - memcpy(ip, &ipv6Addr, 16); -} - -CNetAddr::CNetAddr(const char *pszIp, bool fAllowLookup) { - Init(); - std::vector vIP; - if (LookupHost(pszIp, vIP, 1, fAllowLookup)) *this = vIP[0]; -} - -CNetAddr::CNetAddr(const std::string &strIp, bool fAllowLookup) { - Init(); - std::vector vIP; - if (LookupHost(strIp.c_str(), vIP, 1, fAllowLookup)) *this = vIP[0]; -} - -unsigned int CNetAddr::GetByte(int n) const { - return ip[15 - n]; -} - -bool CNetAddr::IsIPv4() const { - return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0); -} - -bool CNetAddr::IsIPv6() const { - return (!IsIPv4() && !IsTor() && !IsI2P()); -} - -bool CNetAddr::IsRFC1918() const { - return IsIPv4() && - (GetByte(3) == 10 || (GetByte(3) == 192 && GetByte(2) == 168) || - (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31))); -} - -bool CNetAddr::IsReserved() const { - return IsIPv4() && (GetByte(3) >= 240); -} - -bool CNetAddr::IsRFC3927() const { - return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254); -} - -bool CNetAddr::IsRFC3849() const { - return GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x0D && - GetByte(12) == 0xB8; -} - -bool CNetAddr::IsRFC3964() const { - return (GetByte(15) == 0x20 && GetByte(14) == 0x02); -} - -bool CNetAddr::IsRFC6052() const { - static const uint8_t pchRFC6052[] = {0, 0x64, 0xFF, 0x9B, 0, 0, - 0, 0, 0, 0, 0, 0}; - return (memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0); -} - -bool CNetAddr::IsRFC4380() const { - return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && - GetByte(12) == 0); -} - -bool CNetAddr::IsRFC4862() const { - static const uint8_t pchRFC4862[] = {0xFE, 0x80, 0, 0, 0, 0, 0, 0}; - return (memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0); -} - -bool CNetAddr::IsRFC4193() const { - return ((GetByte(15) & 0xFE) == 0xFC); -} - -bool CNetAddr::IsRFC6145() const { - static const uint8_t pchRFC6145[] = {0, 0, 0, 0, 0, 0, - 0, 0, 0xFF, 0xFF, 0, 0}; - return (memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0); -} - -bool CNetAddr::IsRFC4843() const { - return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && - (GetByte(12) & 0xF0) == 0x10); -} - -bool CNetAddr::IsTor() const { - return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0); -} - -bool CNetAddr::IsI2P() const { - return (memcmp(ip, pchGarliCat, sizeof(pchGarliCat)) == 0); -} - -bool CNetAddr::IsLocal() const { - // IPv4 loopback - if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0)) return true; - - // IPv6 loopback (::1/128) - static const uint8_t pchLocal[16] = {0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1}; - if (memcmp(ip, pchLocal, 16) == 0) return true; - - return false; -} - -bool CNetAddr::IsMulticast() const { - return (IsIPv4() && (GetByte(3) & 0xF0) == 0xE0) || (GetByte(15) == 0xFF); -} - -bool CNetAddr::IsValid() const { - // Cleanup 3-byte shifted addresses caused by garbage in size field - // of addr messages from versions before 0.2.9 checksum. - // Two consecutive addr messages look like this: - // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 - // addr26 addr26... - // so if the first length field is garbled, it reads the second batch - // of addr misaligned by 3 bytes. - if (memcmp(ip, pchIPv4 + 3, sizeof(pchIPv4) - 3) == 0) return false; - - // unspecified IPv6 address (::/128) - uint8_t ipNone[16] = {}; - if (memcmp(ip, ipNone, 16) == 0) return false; - - // documentation IPv6 address - if (IsRFC3849()) return false; - - if (IsIPv4()) { - // INADDR_NONE - uint32_t ipNone = INADDR_NONE; - if (memcmp(ip + 12, &ipNone, 4) == 0) return false; - - // 0 - ipNone = 0; - if (memcmp(ip + 12, &ipNone, 4) == 0) return false; - } - - return true; -} - -bool CNetAddr::IsRoutable() const { - return IsValid() && - !(IsReserved() || IsRFC1918() || IsRFC3927() || IsRFC4862() || - (IsRFC4193() && !IsTor() && !IsI2P()) || IsRFC4843() || IsLocal()); -} - -enum Network CNetAddr::GetNetwork() const { - if (!IsRoutable()) return NET_UNROUTABLE; - - if (IsIPv4()) return NET_IPV4; - - if (IsTor()) return NET_TOR; - - if (IsI2P()) return NET_I2P; - - return NET_IPV6; -} - -std::string CNetAddr::ToStringIP() const { - if (IsTor()) return EncodeBase32(&ip[6], 10) + ".onion"; - if (IsI2P()) return EncodeBase32(&ip[6], 10) + ".oc.b32.i2p"; - CService serv(*this, 0); - struct sockaddr_storage sockaddr; - socklen_t socklen = sizeof(sockaddr); - if (serv.GetSockAddr((struct sockaddr *)&sockaddr, &socklen)) { - char name[1025] = ""; - if (!getnameinfo((const struct sockaddr *)&sockaddr, socklen, name, - sizeof(name), nullptr, 0, NI_NUMERICHOST)) - return std::string(name); - } - - if (IsIPv4()) { - return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), - GetByte(0)); - } - - return strprintf("%x:%x:%x:%x:%x:%x:%x:%x", GetByte(15) << 8 | GetByte(14), - GetByte(13) << 8 | GetByte(12), - GetByte(11) << 8 | GetByte(10), - GetByte(9) << 8 | GetByte(8), GetByte(7) << 8 | GetByte(6), - GetByte(5) << 8 | GetByte(4), GetByte(3) << 8 | GetByte(2), - GetByte(1) << 8 | GetByte(0)); -} - -std::string CNetAddr::ToString() const { - return ToStringIP(); -} - -bool operator==(const CNetAddr &a, const CNetAddr &b) { - return (memcmp(a.ip, b.ip, 16) == 0); -} - -bool operator!=(const CNetAddr &a, const CNetAddr &b) { - return (memcmp(a.ip, b.ip, 16) != 0); -} - -bool operator<(const CNetAddr &a, const CNetAddr &b) { - return (memcmp(a.ip, b.ip, 16) < 0); -} - -bool CNetAddr::GetInAddr(struct in_addr *pipv4Addr) const { - if (!IsIPv4()) return false; - memcpy(pipv4Addr, ip + 12, 4); - return true; -} - -bool CNetAddr::GetIn6Addr(struct in6_addr *pipv6Addr) const { - memcpy(pipv6Addr, ip, 16); - return true; -} - -// get canonical identifier of an address' group -// no two connections will be attempted to addresses with the same group -std::vector CNetAddr::GetGroup() const { - std::vector vchRet; - int nClass = NET_IPV6; - int nStartByte = 0; - int nBits = 16; - - // all local addresses belong to the same group - if (IsLocal()) { - nClass = 255; - nBits = 0; - } - - // all unroutable addresses belong to the same group - if (!IsRoutable()) { - nClass = NET_UNROUTABLE; - nBits = 0; - } - // for IPv4 addresses, '1' + the 16 higher-order bits of the IP - // includes mapped IPv4, SIIT translated IPv4, and the well-known prefix - else if (IsIPv4() || IsRFC6145() || IsRFC6052()) { - nClass = NET_IPV4; - nStartByte = 12; - } - // for 6to4 tunnelled addresses, use the encapsulated IPv4 address - else if (IsRFC3964()) { - nClass = NET_IPV4; - nStartByte = 2; - } - // for Teredo-tunnelled IPv6 addresses, use the encapsulated IPv4 address - else if (IsRFC4380()) { - vchRet.push_back(NET_IPV4); - vchRet.push_back(GetByte(3) ^ 0xFF); - vchRet.push_back(GetByte(2) ^ 0xFF); - return vchRet; - } else if (IsTor()) { - nClass = NET_TOR; - nStartByte = 6; - nBits = 4; - } else if (IsI2P()) { - nClass = NET_I2P; - nStartByte = 6; - nBits = 4; - } else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && - GetByte(13) == 0x04 && GetByte(12) == 0x70) { - // for he.net, use /36 groups - nBits = 36; - } else { - // for the rest of the IPv6 network, use /32 groups - nBits = 32; - } - - vchRet.push_back(nClass); - while (nBits >= 8) { - vchRet.push_back(GetByte(15 - nStartByte)); - nStartByte++; - nBits -= 8; - } - if (nBits > 0) - vchRet.push_back(GetByte(15 - nStartByte) | ((1 << nBits) - 1)); - - return vchRet; -} - -uint64_t CNetAddr::GetHash() const { - uint256 hash = Hash(&ip[0], &ip[16]); - uint64_t nRet; - memcpy(&nRet, &hash, sizeof(nRet)); - return nRet; -} - -void CNetAddr::print() const { - printf("CNetAddr(%s)\n", ToString().c_str()); -} - -// private extensions to enum Network, only returned by GetExtNetwork, -// and only used in GetReachabilityFrom -static const int NET_UNKNOWN = NET_MAX + 0; -static const int NET_TEREDO = NET_MAX + 1; -static int GetExtNetwork(const CNetAddr *addr) { - if (addr == nullptr) return NET_UNKNOWN; - if (addr->IsRFC4380()) return NET_TEREDO; - return addr->GetNetwork(); -} - -/** Calculates a metric for how reachable (*this) is from a given partner */ -int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const { - enum Reachability { - REACH_UNREACHABLE, - REACH_DEFAULT, - REACH_TEREDO, - REACH_IPV6_WEAK, - REACH_IPV4, - REACH_IPV6_STRONG, - REACH_PRIVATE - }; - - if (!IsRoutable()) return REACH_UNREACHABLE; - - int ourNet = GetExtNetwork(this); - int theirNet = GetExtNetwork(paddrPartner); - bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145(); - - switch (theirNet) { - case NET_IPV4: - switch (ourNet) { - default: - return REACH_DEFAULT; - case NET_IPV4: - return REACH_IPV4; - } - case NET_IPV6: - switch (ourNet) { - default: - return REACH_DEFAULT; - case NET_TEREDO: - return REACH_TEREDO; - case NET_IPV4: - return REACH_IPV4; - case NET_IPV6: - // only prefer giving our IPv6 address if it's not tunnelled - return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; - } - case NET_TOR: - switch (ourNet) { - default: - return REACH_DEFAULT; - case NET_IPV4: - // Tor users can connect to IPv4 as well - return REACH_IPV4; - case NET_TOR: - return REACH_PRIVATE; - } - case NET_I2P: - switch (ourNet) { - default: - return REACH_DEFAULT; - case NET_I2P: - return REACH_PRIVATE; - } - case NET_TEREDO: - switch (ourNet) { - default: - return REACH_DEFAULT; - case NET_TEREDO: - return REACH_TEREDO; - case NET_IPV6: - return REACH_IPV6_WEAK; - case NET_IPV4: - return REACH_IPV4; - } - case NET_UNKNOWN: - case NET_UNROUTABLE: - default: - switch (ourNet) { - default: - return REACH_DEFAULT; - case NET_TEREDO: - return REACH_TEREDO; - case NET_IPV6: - return REACH_IPV6_WEAK; - case NET_IPV4: - return REACH_IPV4; - case NET_I2P: - // assume connections from unroutable addresses are - return REACH_PRIVATE; - case NET_TOR: - // either from Tor/I2P, or don't care about our address - return REACH_PRIVATE; - } - } -} - -void CService::Init() { +void CSeederService::Init() { port = 0; } -CService::CService() { +CSeederService::CSeederService() { Init(); } -CService::CService(const CNetAddr &cip, unsigned short portIn) +CSeederService::CSeederService(const CNetAddr &cip, unsigned short portIn) : CNetAddr(cip), port(portIn) {} -CService::CService(const struct in_addr &ipv4Addr, unsigned short portIn) +CSeederService::CSeederService(const struct in_addr &ipv4Addr, + unsigned short portIn) : CNetAddr(ipv4Addr), port(portIn) {} -CService::CService(const struct in6_addr &ipv6Addr, unsigned short portIn) +CSeederService::CSeederService(const struct in6_addr &ipv6Addr, + unsigned short portIn) : CNetAddr(ipv6Addr), port(portIn) {} -CService::CService(const struct sockaddr_in &addr) +CSeederService::CSeederService(const struct sockaddr_in &addr) : CNetAddr(addr.sin_addr), port(ntohs(addr.sin_port)) { assert(addr.sin_family == AF_INET); } -CService::CService(const struct sockaddr_in6 &addr) +CSeederService::CSeederService(const struct sockaddr_in6 &addr) : CNetAddr(addr.sin6_addr), port(ntohs(addr.sin6_port)) { assert(addr.sin6_family == AF_INET6); } -bool CService::SetSockAddr(const struct sockaddr *paddr) { +bool CSeederService::SetSockAddr(const struct sockaddr *paddr) { switch (paddr->sa_family) { case AF_INET: - *this = CService(*(const struct sockaddr_in *)paddr); + *this = CSeederService(*(const struct sockaddr_in *)paddr); return true; case AF_INET6: - *this = CService(*(const struct sockaddr_in6 *)paddr); + *this = CSeederService(*(const struct sockaddr_in6 *)paddr); return true; default: return false; } } -CService::CService(const char *pszIpPort, bool fAllowLookup) { +CSeederService::CSeederService(const char *pszIpPort, bool fAllowLookup) { Init(); - CService ip; + CSeederService ip; if (Lookup(pszIpPort, ip, 0, fAllowLookup)) *this = ip; } -CService::CService(const char *pszIpPort, int portDefault, bool fAllowLookup) { +CSeederService::CSeederService(const char *pszIpPort, int portDefault, + bool fAllowLookup) { Init(); - CService ip; + CSeederService ip; if (Lookup(pszIpPort, ip, portDefault, fAllowLookup)) *this = ip; } -CService::CService(const std::string &strIpPort, bool fAllowLookup) { +CSeederService::CSeederService(const std::string &strIpPort, + bool fAllowLookup) { Init(); - CService ip; + CSeederService ip; if (Lookup(strIpPort.c_str(), ip, 0, fAllowLookup)) *this = ip; } -CService::CService(const std::string &strIpPort, int portDefault, - bool fAllowLookup) { +CSeederService::CSeederService(const std::string &strIpPort, int portDefault, + bool fAllowLookup) { Init(); - CService ip; + CSeederService ip; if (Lookup(strIpPort.c_str(), ip, portDefault, fAllowLookup)) *this = ip; } -unsigned short CService::GetPort() const { +unsigned short CSeederService::GetPort() const { return port; } -bool operator==(const CService &a, const CService &b) { +bool operator==(const CSeederService &a, const CSeederService &b) { return (CNetAddr)a == (CNetAddr)b && a.port == b.port; } -bool operator!=(const CService &a, const CService &b) { +bool operator!=(const CSeederService &a, const CSeederService &b) { return (CNetAddr)a != (CNetAddr)b || a.port != b.port; } -bool operator<(const CService &a, const CService &b) { +bool operator<(const CSeederService &a, const CSeederService &b) { return (CNetAddr)a < (CNetAddr)b || ((CNetAddr)a == (CNetAddr)b && a.port < b.port); } -bool CService::GetSockAddr(struct sockaddr *paddr, socklen_t *addrlen) const { +bool CSeederService::GetSockAddr(struct sockaddr *paddr, + socklen_t *addrlen) const { if (IsIPv4()) { if (*addrlen < (socklen_t)sizeof(struct sockaddr_in)) return false; *addrlen = sizeof(struct sockaddr_in); struct sockaddr_in *paddrin = (struct sockaddr_in *)paddr; memset(paddrin, 0, *addrlen); if (!GetInAddr(&paddrin->sin_addr)) return false; paddrin->sin_family = AF_INET; paddrin->sin_port = htons(port); return true; } if (IsIPv6()) { if (*addrlen < (socklen_t)sizeof(struct sockaddr_in6)) return false; *addrlen = sizeof(struct sockaddr_in6); struct sockaddr_in6 *paddrin6 = (struct sockaddr_in6 *)paddr; memset(paddrin6, 0, *addrlen); if (!GetIn6Addr(&paddrin6->sin6_addr)) return false; paddrin6->sin6_family = AF_INET6; paddrin6->sin6_port = htons(port); return true; } return false; } -std::vector CService::GetKey() const { +std::vector CSeederService::GetKey() const { std::vector vKey; vKey.resize(18); memcpy(&vKey[0], ip, 16); vKey[16] = port / 0x100; vKey[17] = port & 0x0FF; return vKey; } -std::string CService::ToStringPort() const { +std::string CSeederService::ToStringPort() const { return strprintf("%u", port); } -std::string CService::ToStringIPPort() const { - if (IsIPv4() || IsTor() || IsI2P()) { +std::string CSeederService::ToStringIPPort() const { + if (IsIPv4() || IsTor()) { return ToStringIP() + ":" + ToStringPort(); } else { return "[" + ToStringIP() + "]:" + ToStringPort(); } } -std::string CService::ToString() const { +std::string CSeederService::ToString() const { return ToStringIPPort(); } -void CService::print() const { - printf("CService(%s)\n", ToString().c_str()); +void CSeederService::print() const { + printf("CSeederService(%s)\n", ToString().c_str()); } -void CService::SetPort(unsigned short portIn) { +void CSeederService::SetPort(unsigned short portIn) { port = portIn; } diff --git a/src/seeder/netbase.h b/src/seeder/netbase.h index 334c7c7955..8f9aa89d77 100644 --- a/src/seeder/netbase.h +++ b/src/seeder/netbase.h @@ -1,162 +1,98 @@ // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_SEEDER_NETBASE_H #define BITCOIN_SEEDER_NETBASE_H #include "compat.h" +#include "netaddress.h" #include "serialize.h" #include #include extern int nConnectTimeout; #ifdef WIN32 // In MSVC, this is defined as a macro, undefine it to prevent a compile and // link error #undef SetPort #endif -enum Network { - NET_UNROUTABLE, - NET_IPV4, - NET_IPV6, - NET_TOR, - NET_I2P, - - NET_MAX, -}; - extern int nConnectTimeout; extern bool fNameLookup; -/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */ -class CNetAddr { -protected: - uint8_t ip[16]; // in network byte order - -public: - CNetAddr(); - CNetAddr(const struct in_addr &ipv4Addr); - explicit CNetAddr(const char *pszIp, bool fAllowLookup = false); - explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false); - void Init(); - void SetIP(const CNetAddr &ip); - bool SetSpecial(const std::string &strName); // for Tor and I2P addresses - bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) - bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor/I2P) - bool IsReserved() const; // Against Hetzners Abusal/Netscan Bot - bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, - // 192.168.0.0/16, 172.16.0.0/12) - bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) - bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16) - bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16) - bool IsRFC4193() const; // IPv6 unique local (FC00::/15) - bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32) - bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28) - bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64) - bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96) - bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) - bool IsTor() const; - bool IsI2P() const; - bool IsLocal() const; - bool IsRoutable() const; - bool IsValid() const; - bool IsMulticast() const; - enum Network GetNetwork() const; - std::string ToString() const; - std::string ToStringIP() const; - unsigned int GetByte(int n) const; - uint64_t GetHash() const; - bool GetInAddr(struct in_addr *pipv4Addr) const; - std::vector GetGroup() const; - int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const; - void print() const; - - CNetAddr(const struct in6_addr &pipv6Addr); - bool GetIn6Addr(struct in6_addr *pipv6Addr) const; - - friend bool operator==(const CNetAddr &a, const CNetAddr &b); - friend bool operator!=(const CNetAddr &a, const CNetAddr &b); - friend bool operator<(const CNetAddr &a, const CNetAddr &b); - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(FLATDATA(ip)); - } -}; - /** A combination of a network address (CNetAddr) and a (TCP) port */ -class CService : public CNetAddr { +class CSeederService : public CNetAddr { protected: unsigned short port; // host order public: - CService(); - CService(const CNetAddr &ip, unsigned short port); - CService(const struct in_addr &ipv4Addr, unsigned short port); - CService(const struct sockaddr_in &addr); - explicit CService(const char *pszIpPort, int portDefault, - bool fAllowLookup = false); - explicit CService(const char *pszIpPort, bool fAllowLookup = false); - explicit CService(const std::string &strIpPort, int portDefault, - bool fAllowLookup = false); - explicit CService(const std::string &strIpPort, bool fAllowLookup = false); + CSeederService(); + CSeederService(const CNetAddr &ip, unsigned short port); + CSeederService(const struct in_addr &ipv4Addr, unsigned short port); + CSeederService(const struct sockaddr_in &addr); + explicit CSeederService(const char *pszIpPort, int portDefault, + bool fAllowLookup = false); + explicit CSeederService(const char *pszIpPort, bool fAllowLookup = false); + explicit CSeederService(const std::string &strIpPort, int portDefault, + bool fAllowLookup = false); + explicit CSeederService(const std::string &strIpPort, + bool fAllowLookup = false); void Init(); void SetPort(unsigned short portIn); unsigned short GetPort() const; bool GetSockAddr(struct sockaddr *paddr, socklen_t *addrlen) const; bool SetSockAddr(const struct sockaddr *paddr); - friend bool operator==(const CService &a, const CService &b); - friend bool operator!=(const CService &a, const CService &b); - friend bool operator<(const CService &a, const CService &b); + friend bool operator==(const CSeederService &a, const CSeederService &b); + friend bool operator!=(const CSeederService &a, const CSeederService &b); + friend bool operator<(const CSeederService &a, const CSeederService &b); std::vector GetKey() const; std::string ToString() const; std::string ToStringPort() const; std::string ToStringIPPort() const; void print() const; - CService(const struct in6_addr &ipv6Addr, unsigned short port); - CService(const struct sockaddr_in6 &addr); + CSeederService(const struct in6_addr &ipv6Addr, unsigned short port); + CSeederService(const struct sockaddr_in6 &addr); ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { - CService *pthis = const_cast(this); + CSeederService *pthis = const_cast(this); READWRITE(FLATDATA(ip)); unsigned short portN = htons(port); READWRITE(portN); if (ser_action.ForRead()) { pthis->port = ntohs(portN); } } }; enum Network ParseNetwork(std::string net); void SplitHostPort(std::string in, int &portOut, std::string &hostOut); -bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion = 5); -bool GetProxy(enum Network net, CService &addrProxy); +bool SetProxy(enum Network net, CSeederService addrProxy, + int nSocksVersion = 5); +bool GetProxy(enum Network net, CSeederService &addrProxy); bool IsProxy(const CNetAddr &addr); -bool SetNameProxy(CService addrProxy, int nSocksVersion = 5); +bool SetNameProxy(CSeederService addrProxy, int nSocksVersion = 5); bool GetNameProxy(); bool LookupHost(const char *pszName, std::vector &vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true); bool LookupHostNumeric(const char *pszName, std::vector &vIP, unsigned int nMaxSolutions = 0); -bool Lookup(const char *pszName, CService &addr, int portDefault = 0, +bool Lookup(const char *pszName, CSeederService &addr, int portDefault = 0, bool fAllowLookup = true); -bool Lookup(const char *pszName, std::vector &vAddr, +bool Lookup(const char *pszName, std::vector &vAddr, int portDefault = 0, bool fAllowLookup = true, unsigned int nMaxSolutions = 0); -bool LookupNumeric(const char *pszName, CService &addr, int portDefault = 0); -bool ConnectSocket(const CService &addr, SOCKET &hSocketRet, +bool LookupNumeric(const char *pszName, CSeederService &addr, + int portDefault = 0); +bool ConnectSocket(const CSeederService &addr, SOCKET &hSocketRet, int nTimeout = nConnectTimeout); -bool ConnectSocketByName(CService &addr, SOCKET &hSocketRet, +bool ConnectSocketByName(CSeederService &addr, SOCKET &hSocketRet, const char *pszDest, int portDefault = 0, int nTimeout = nConnectTimeout); #endif diff --git a/src/seeder/protocol.cpp b/src/seeder/protocol.cpp index 009c53fad2..e6ebc4185b 100644 --- a/src/seeder/protocol.cpp +++ b/src/seeder/protocol.cpp @@ -1,137 +1,138 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2011 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "protocol.h" #include "netbase.h" #include "util.h" #include #include #ifndef WIN32 #include #endif static const char *ppszTypeName[] = { "ERROR", "tx", "block", }; uint8_t pchMessageStart[4] = {0xe3, 0xe1, 0xf3, 0xe8}; CMessageHeader::CMessageHeader() { memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); memset(pchCommand, 0, sizeof(pchCommand)); pchCommand[1] = 1; nMessageSize = -1; nChecksum = 0; } CMessageHeader::CMessageHeader(const char *pszCommand, unsigned int nMessageSizeIn) { memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); strncpy(pchCommand, pszCommand, COMMAND_SIZE); nMessageSize = nMessageSizeIn; nChecksum = 0; } std::string CMessageHeader::GetCommand() const { if (pchCommand[COMMAND_SIZE - 1] == 0) return std::string(pchCommand, pchCommand + strlen(pchCommand)); else return std::string(pchCommand, pchCommand + COMMAND_SIZE); } bool CMessageHeader::IsValid() const { // Check start string if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0) return false; // Check the command string for errors for (const char *p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) { if (*p1 == 0) { // Must be all zeros after the first zero for (; p1 < pchCommand + COMMAND_SIZE; p1++) if (*p1 != 0) return false; } else if (*p1 < ' ' || *p1 > 0x7E) return false; } // Message size if (nMessageSize > MAX_SIZE) { printf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > " "MAX_SIZE\n", GetCommand().c_str(), nMessageSize); return false; } return true; } -CAddress::CAddress() : CService() { +CAddress::CAddress() : CSeederService() { Init(); } -CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn) { +CAddress::CAddress(CSeederService ipIn, uint64_t nServicesIn) + : CSeederService(ipIn) { Init(); nServices = nServicesIn; } void CAddress::Init() { nServices = NODE_NETWORK | NODE_BITCOIN_CASH; nTime = 100000000; } void CAddress::print() const { printf("CAddress(%s)\n", ToString().c_str()); } CInv::CInv() { type = 0; hash.SetNull(); } CInv::CInv(uint32_t typeIn, const uint256 &hashIn) { type = typeIn; hash = hashIn; } CInv::CInv(const std::string &strType, const uint256 &hashIn) { size_t i; for (i = 1; i < ARRAYLEN(ppszTypeName); i++) { if (strType == ppszTypeName[i]) { type = i; break; } } if (i == ARRAYLEN(ppszTypeName)) { throw std::out_of_range("CInv::CInv(string, uint256) : unknown type"); } hash = hashIn; } bool operator<(const CInv &a, const CInv &b) { return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); } bool CInv::IsKnownType() const { return (type >= 1 && type < ARRAYLEN(ppszTypeName)); } const char *CInv::GetCommand() const { if (!IsKnownType()) throw std::out_of_range("CInv::GetCommand() : unknown type"); return ppszTypeName[type]; } std::string CInv::ToString() const { return "CInv()"; } void CInv::print() const { printf("CInv\n"); } diff --git a/src/seeder/protocol.h b/src/seeder/protocol.h index 319667d5e6..49f554ffd5 100644 --- a/src/seeder/protocol.h +++ b/src/seeder/protocol.h @@ -1,136 +1,136 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2011 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #ifndef __cplusplus #error This header can only be compiled as C++. #endif #ifndef BITCOIN_SEEDER_PROTOCOL_H #define BITCOIN_SEEDER_PROTOCOL_H #include "netbase.h" #include "serialize.h" #include "uint256.h" #include #include extern bool fTestNet; static inline unsigned short GetDefaultPort(const bool testnet = fTestNet) { return testnet ? 18333 : 8333; } // // Message header // (4) message start // (12) command // (4) size // (4) checksum extern uint8_t pchMessageStart[4]; class CMessageHeader { public: CMessageHeader(); CMessageHeader(const char *pszCommand, unsigned int nMessageSizeIn); std::string GetCommand() const; bool IsValid() const; ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(FLATDATA(pchMessageStart)); READWRITE(FLATDATA(pchCommand)); READWRITE(nMessageSize); if (s.GetVersion() >= 209) { READWRITE(nChecksum); } } // TODO: make private (improves encapsulation) public: enum { COMMAND_SIZE = 12 }; char pchMessageStart[sizeof(::pchMessageStart)]; char pchCommand[COMMAND_SIZE]; unsigned int nMessageSize; unsigned int nChecksum; }; enum ServiceFlags : uint64_t { NODE_NETWORK = (1 << 0), NODE_BLOOM = (1 << 2), NODE_XTHIN = (1 << 4), NODE_BITCOIN_CASH = (1 << 5), }; -class CAddress : public CService { +class CAddress : public CSeederService { public: CAddress(); - CAddress(CService ipIn, + CAddress(CSeederService ipIn, uint64_t nServicesIn = NODE_NETWORK | NODE_BITCOIN_CASH); void Init(); ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { int nVersion = s.GetVersion(); CAddress *pthis = const_cast(this); - CService *pip = (CService *)pthis; + CSeederService *pip = (CSeederService *)pthis; if (ser_action.ForRead()) { pthis->Init(); } if (s.GetType() & SER_DISK) { READWRITE(nVersion); } if ((s.GetType() & SER_DISK) || (nVersion >= 31402 && !(s.GetType() & SER_GETHASH))) { READWRITE(nTime); } READWRITE(nServices); READWRITE(*pip); } void print() const; // TODO: make private (improves encapsulation) public: uint64_t nServices; // disk and network only unsigned int nTime; }; class CInv { public: CInv(); CInv(uint32_t typeIn, const uint256 &hashIn); CInv(const std::string &strType, const uint256 &hashIn); ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(type); READWRITE(hash); } friend bool operator<(const CInv &a, const CInv &b); bool IsKnownType() const; const char *GetCommand() const; std::string ToString() const; void print() const; // TODO: make private (improves encapsulation) public: uint32_t type; uint256 hash; }; #endif // __INCLUDED_PROTOCOL_H__