diff --git a/Makefile.am b/Makefile.am --- a/Makefile.am +++ b/Makefile.am @@ -22,6 +22,7 @@ BITCOIND_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT) BITCOIN_QT_BIN=$(top_builddir)/src/qt/$(BITCOIN_GUI_NAME)$(EXEEXT) BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT) +BITCOIN_SEEDER_BIN=$(top_builddir)/src/$(BITCOIN_SEEDER_NAME)$(EXEEXT) BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) empty := @@ -170,6 +171,9 @@ $(BITCOIN_CLI_BIN): FORCE $(MAKE) -C src $(@F) +$(BITCOIN_SEEDER_BIN): FORCE + $(MAKE) -C src $(@F) + if USE_LCOV baseline.info: diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,7 @@ BITCOIN_GUI_NAME=bitcoin-qt BITCOIN_CLI_NAME=bitcoin-cli BITCOIN_TX_NAME=bitcoin-tx +BITCOIN_SEEDER_NAME=bitcoin-seeder AC_CANONICAL_HOST @@ -313,6 +314,12 @@ [build_bitcoind=$withval], [build_bitcoind=yes]) +AC_ARG_WITH([seeder], + [AS_HELP_STRING([--with-seeder], + [build seeder (default=yes)])], + [build_bitcoin_seeder=$withval], + [build_bitcoin_seeder=yes]) + use_pkgconfig=yes case $host in *mingw*) @@ -700,7 +707,7 @@ dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus BITCOIN_QT_CONFIGURE([$use_pkgconfig], [qt5]) -if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then +if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$build_bitcoin_seeder$use_tests$use_bench = xnononononono; then use_boost=no else use_boost=yes @@ -1006,6 +1013,10 @@ AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes]) AC_MSG_RESULT($build_bitcoind) +AC_MSG_CHECKING([whether to build bitcoin-seeder]) +AM_CONDITIONAL([BUILD_BITCOIN_SEEDER], [test x$build_bitcoin_seeder = xyes]) +AC_MSG_RESULT($build_bitcoin_seeder) + AC_MSG_CHECKING([whether to build utils (bitcoin-cli bitcoin-tx)]) AM_CONDITIONAL([BUILD_BITCOIN_UTILS], [test x$build_bitcoin_utils = xyes]) AC_MSG_RESULT($build_bitcoin_utils) @@ -1135,7 +1146,7 @@ fi if test x$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnononononono; then - AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-bench or --enable-tests]) + AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-seeder --with-gui --enable-bench or --enable-tests]) fi AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin]) @@ -1174,6 +1185,7 @@ AC_SUBST(BITCOIN_GUI_NAME) AC_SUBST(BITCOIN_CLI_NAME) AC_SUBST(BITCOIN_TX_NAME) +AC_SUBST(BITCOIN_SEEDER_NAME) AC_SUBST(RELDFLAGS) AC_SUBST(ERROR_CXXFLAGS) diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,6 +24,9 @@ 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 @@ -39,6 +42,9 @@ 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 @@ -55,6 +61,7 @@ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_CLI) \ + $(LIBBITCOIN_SEEDER) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) @@ -69,6 +76,10 @@ bin_PROGRAMS += bitcoind endif +if BUILD_BITCOIN_SEEDER + bin_PROGRAMS += bitcoin-seeder +endif + if BUILD_BITCOIN_UTILS bin_PROGRAMS += bitcoin-cli bitcoin-tx endif @@ -359,6 +370,27 @@ rpc/client.cpp \ $(BITCOIN_CORE_H) +# seeder library +libbitcoin_seeder_a_CPPFLAGS = $(AM_CPPFLAGS) $(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.c \ + seeder/dns.h \ + seeder/netbase.cpp \ + seeder/netbase.h \ + seeder/protocol.cpp \ + seeder/protocol.h \ + seeder/serialize.h \ + seeder/strlcpy.h \ + seeder/uint256.h \ + seeder/util.cpp \ + seeder/util.h + nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h # @@ -406,6 +438,17 @@ 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) + +bitcoin_seeder_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) +# + # bitcoin-tx binary # bitcoin_tx_SOURCES = bitcoin-tx.cpp bitcoin_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) diff --git a/src/seeder/README.md b/src/seeder/README.md new file mode 100644 --- /dev/null +++ b/src/seeder/README.md @@ -0,0 +1,53 @@ +bitcoin-seeder +============== + +Bitcoin-seeder is a crawler for the Bitcoin network, which exposes a list +of reliable nodes via a built-in DNS server. + +Features: +* regularly revisits known nodes to check their availability +* bans nodes after enough failures, or bad behaviour +* accepts nodes down to v0.3.19 to request new IP addresses from, + but only reports good post-v0.3.24 nodes. +* keeps statistics over (exponential) windows of 2 hours, 8 hours, + 1 day and 1 week, to base decisions on. +* very low memory (a few tens of megabytes) and cpu requirements. +* crawlers run in parallel (by default 24 threads simultaneously). + +REQUIREMENTS +------------ + +$ sudo apt-get install build-essential libboost-all-dev libssl-dev + +USAGE +----- + +Assuming you want to run a dns seed on dnsseed.example.com, you will +need an authorative NS record in example.com's domain record, pointing +to for example vps.example.com: + +$ dig -t NS dnsseed.example.com + +;; ANSWER SECTION +dnsseed.example.com. 86400 IN NS vps.example.com. + +On the system vps.example.com, you can now run dnsseed: + +./bitcoin-seeder -h dnsseed.example.com -n vps.example.com + +If you want the DNS server to report SOA records, please provide an +e-mail address (with the @ part replaced by .) using -m. + + +RUNNING AS NON-ROOT +------------------- + +Typically, you'll need root privileges to listen to port 53 (name service). + +One solution is using an iptables rule (Linux only) to redirect it to +a non-privileged port: + +$ iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 5353 + +If properly configured, this will allow you to run dnsseed in userspace, using +the -p 5353 option. diff --git a/src/seeder/bitcoin.h b/src/seeder/bitcoin.h new file mode 100644 --- /dev/null +++ b/src/seeder/bitcoin.h @@ -0,0 +1,9 @@ +#ifndef _BITCOIN_H_ +#define _BITCOIN_H_ 1 + +#include "protocol.h" + +bool TestNode(const CService &cip, int &ban, int &client, std::string &clientSV, + int &blocks, std::vector *vAddr); + +#endif diff --git a/src/seeder/bitcoin.cpp b/src/seeder/bitcoin.cpp new file mode 100644 --- /dev/null +++ b/src/seeder/bitcoin.cpp @@ -0,0 +1,326 @@ +#include + +#include "db.h" +#include "netbase.h" +#include "protocol.h" +#include "serialize.h" +#include "uint256.h" + +#define BITCOIN_SEED_NONCE 0x0539a019ca550825ULL + +using namespace std; + +class CNode { + SOCKET sock; + CDataStream vSend; + CDataStream vRecv; + unsigned int nHeaderStart; + unsigned int nMessageStart; + int nVersion; + string strSubVer; + int nStartingHeight; + vector *vAddr; + int ban; + int64 doneAfter; + CAddress you; + + int GetTimeout() { + if (you.IsTor()) + return 120; + else + return 30; + } + + void BeginMessage(const char *pszCommand) { + if (nHeaderStart != -1) 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 == -1) return; + vSend.resize(nHeaderStart); + nHeaderStart = -1; + nMessageStart = -1; + } + + void EndMessage() { + if (nHeaderStart == -1) return; + unsigned int 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 = -1; + nMessageStart = -1; + } + + 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 nTime = time(NULL); + uint64 nLocalNonce = BITCOIN_SEED_NONCE; + int64 nLocalServices = 0; + CAddress me(CService("0.0.0.0")); + BeginMessage("version"); + int nBestHeight = GetRequireHeight(); + string ver = "/bitcoin-seeder:0.01/"; + 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(NULL) + GetTimeout(); + } else { + doneAfter = time(NULL) + 1; + } + } + + bool ProcessMessage(string strCommand, CDataStream &vRecv) { + // printf("%s: RECV %s\n", ToString(you).c_str(), + // strCommand.c_str()); + if (strCommand == "version") { + int64 nTime; + CAddress addrMe; + CAddress addrFrom; + uint64 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(min(nVersion, PROTOCOL_VERSION)); + if (nVersion < 209) { + this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION)); + GotVersion(); + } + return false; + } + + if (strCommand == "verack") { + this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION)); + GotVersion(); + return false; + } + + if (strCommand == "addr" && vAddr) { + vector vAddrNew; + vRecv >> vAddrNew; + // printf("%s: got %i addresses\n", ToString(you).c_str(), + // (int)vAddrNew.size()); + int64 now = time(NULL); + 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)); + int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); + if (vRecv.end() - pstart < nHeaderSize) { + if (vRecv.size() > nHeaderSize) { + vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); + } + break; + } + vRecv.erase(vRecv.begin(), pstart); + 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; + } + 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.nType, vRecv.nVersion); + 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, vector *vAddrIn) + : you(ip), nHeaderStart(-1), nMessageStart(-1), vAddr(vAddrIn), ban(0), + doneAfter(0), nVersion(0) { + vSend.SetType(SER_NETWORK); + vSend.SetVersion(0); + vRecv.SetType(SER_NETWORK); + vRecv.SetVersion(0); + if (time(NULL) > 1329696000) { + vSend.SetVersion(209); + vRecv.SetVersion(209); + } + } + bool Run() { + bool res = true; + if (!ConnectSocket(you, sock)) return false; + PushVersion(); + Send(); + int64 now; + while (now = time(NULL), 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, NULL, &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, + std::string &clientSV, int &blocks, 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); + 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/compat.h b/src/seeder/compat.h new file mode 100644 --- /dev/null +++ b/src/seeder/compat.h @@ -0,0 +1,64 @@ +// 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. +#ifndef _BITCOIN_COMPAT_H +#define _BITCOIN_COMPAT_H 1 + +#ifdef WIN32 +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +typedef u_int SOCKET; +#ifdef __APPLE__ +#define MSG_NOSIGNAL 0 +#endif +#ifdef WIN32 +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 +typedef int socklen_t; +#else +#include "errno.h" +#define WSAGetLastError() errno +#define WSAEINVAL EINVAL +#define WSAEALREADY EALREADY +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSAEINTR EINTR +#define WSAEINPROGRESS EINPROGRESS +#define WSAEADDRINUSE EADDRINUSE +#define WSAENOTSOCK EBADF +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR -1 +#endif + +inline int myclosesocket(SOCKET &hSocket) { + if (hSocket == INVALID_SOCKET) return WSAENOTSOCK; +#ifdef WIN32 + int ret = closesocket(hSocket); +#else + int ret = close(hSocket); +#endif + hSocket = INVALID_SOCKET; + return ret; +} +#define closesocket(s) myclosesocket(s) + +#endif diff --git a/src/seeder/db.h b/src/seeder/db.h new file mode 100644 --- /dev/null +++ b/src/seeder/db.h @@ -0,0 +1,397 @@ +#include +#include + +#include +#include +#include +#include + +#include "netbase.h" +#include "protocol.h" +#include "util.h" + +#define MIN_RETRY 1000 + +#define REQUIRE_VERSION 70001 + +static inline int GetRequireHeight(const bool testnet = fTestNet) { + return testnet ? 500000 : 350000; +} + +std::string static inline ToString(const CService &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 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); + } + + IMPLEMENT_SERIALIZE(READWRITE(weight); READWRITE(count); + READWRITE(reliability);) + + friend class CAddrInfo; +}; + +class CAddrReport { +public: + CService ip; + int clientVersion; + int blocks; + double uptime[5]; + std::string clientSubVersion; + int64_t lastSuccess; + bool fGood; + uint64_t services; +}; + +class CAddrInfo { +private: + CService ip; + uint64_t services; + int64 lastTry; + int64 ourLastTry; + int64 ourLastSuccess; + int64 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 (!(services & NODE_BITCOIN_CASH)) 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; + } + int 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; + } + int 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; + + IMPLEMENT_SERIALIZE(unsigned char version = 4; READWRITE(version); + READWRITE(ip); READWRITE(services); READWRITE(lastTry); + unsigned char tried = ourLastTry != 0; READWRITE(tried); + if (tried) { + READWRITE(ourLastTry); + READWRITE(ignoreTill); + READWRITE(stat2H); + READWRITE(stat8H); + READWRITE(stat1D); + READWRITE(stat1W); + if (version >= 1) + READWRITE(stat1M); + else if (!fWrite) + *((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; + bool fGood; + int nBanTime; + int nHeight; + int nClientV; + std::string strClientV; + int64 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; + int nId; // number of address id's + std::map + idToInfo; // map address id to address info (b,c,d,e) + std::map ipToId; // map ip to id (b,c,d,e) + std::deque ourId; // sequence of tried nodes, in order we have tried + // connecting to them (c,d) + std::set unkId; // set of nodes not yet tried (b) + std::set goodId; // set of good nodes (d, good e) + int nDirty; + +protected: + // internal routines that assume proper locks are acquired + void Add_(const CAddress &addr, bool force); // add an address + bool Get_(CServiceResult &ip, int &wait); // get an IP to test (must call + // Good_, Bad_, or Skipped_ on + // result afterwards) + bool GetMany_(std::vector &ips, int max, int &wait); + void + Good_(const CService &ip, int clientV, std::string clientSV, + int blocks); // mark an IP as good (must have been returned by Get_) + void Bad_(const CService &ip, int ban); // mark an IP as bad (and optionally + // ban it) (must have been returned + // by Get_) + void Skipped_(const CService &ip); // mark an IP as skipped (must have been + // returned by Get_) + int Lookup_(const CService &ip); // look up id of an IP + void + GetIPs_(std::set &ips, uint64_t requestedFlags, int max, + const bool *nets); // get a random set of IPs (shared lock only) + +public: + std::map + banned; // nodes that are banned, with their unban time (a) + + void GetStats(CAddrDbStats &stats) { + SHARED_CRITICAL_BLOCK(cs) { + stats.nBanned = banned.size(); + stats.nAvail = idToInfo.size(); + stats.nTracked = ourId.size(); + stats.nGood = goodId.size(); + stats.nNew = unkId.size(); + stats.nAge = time(NULL) - 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; + SHARED_CRITICAL_BLOCK(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 + IMPLEMENT_SERIALIZE(({ + int nVersion = 0; + READWRITE(nVersion); + SHARED_CRITICAL_BLOCK(cs) { + if (fWrite) { + CAddrDb *db = const_cast(this); + int n = ourId.size() + unkId.size(); + READWRITE(n); + for (std::deque::const_iterator it = + ourId.begin(); + it != ourId.end(); it++) { + std::map::iterator ci = + db->idToInfo.find(*it); + READWRITE((*ci).second); + } + for (std::set::const_iterator it = + unkId.begin(); + it != unkId.end(); it++) { + std::map::iterator ci = + db->idToInfo.find(*it); + READWRITE((*ci).second); + } + } else { + CAddrDb *db = const_cast(this); + db->nId = 0; + int n; + READWRITE(n); + for (int i = 0; i < n; i++) { + CAddrInfo info; + READWRITE(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++; + } + READWRITE(banned); + } + });) + + void Add(const CAddress &addr, bool fForce = false) { + CRITICAL_BLOCK(cs) + Add_(addr, fForce); + } + void Add(const std::vector &vAddr, bool fForce = false) { + CRITICAL_BLOCK(cs) + for (int i = 0; i < vAddr.size(); i++) + Add_(vAddr[i], fForce); + } + void Good(const CService &addr, int clientVersion, + std::string clientSubVersion, int blocks) { + CRITICAL_BLOCK(cs) + Good_(addr, clientVersion, clientSubVersion, blocks); + } + void Skipped(const CService &addr) { + CRITICAL_BLOCK(cs) + Skipped_(addr); + } + void Bad(const CService &addr, int ban = 0) { + CRITICAL_BLOCK(cs) + Bad_(addr, ban); + } + bool Get(CServiceResult &ip, int &wait) { + CRITICAL_BLOCK(cs) + return Get_(ip, wait); + } + void GetMany(std::vector &ips, int max, int &wait) { + CRITICAL_BLOCK(cs) { + while (max > 0) { + CServiceResult ip = {}; + if (!Get_(ip, wait)) return; + ips.push_back(ip); + max--; + } + } + } + void ResultMany(const std::vector &ips) { + CRITICAL_BLOCK(cs) { + for (int 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, int max, + const bool *nets) { + SHARED_CRITICAL_BLOCK(cs) + GetIPs_(ips, requestedFlags, max, nets); + } +}; diff --git a/src/seeder/db.cpp b/src/seeder/db.cpp new file mode 100644 --- /dev/null +++ b/src/seeder/db.cpp @@ -0,0 +1,208 @@ +#include "db.h" +#include + +using namespace std; + +void CAddrInfo::Update(bool good) { + uint32_t now = time(NULL); + 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); + int 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) { + int64 now = time(NULL); + int cont = 0; + int tot = unkId.size() + ourId.size(); + if (tot == 0) { + wait = 5; + return false; + } + do { + int rnd = rand() % tot; + int ret; + if (rnd < unkId.size()) { + set::iterator it = unkId.end(); + it--; + ret = *it; + unkId.erase(it); + } else { + ret = ourId.front(); + if (time(NULL) - 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) { + if (ipToId.count(ip)) return ipToId[ip]; + return -1; +} + +void CAddrDb::Good_(const CService &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) { + int id = Lookup_(addr); + if (id == -1) return; + unkId.erase(id); + CAddrInfo &info = idToInfo[id]; + info.Update(false); + uint32_t now = time(NULL); + 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) { + 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); + if (banned.count(ipp)) { + time_t bantime = banned[ipp]; + if (force || (bantime < time(NULL) && 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_(set &ips, uint64_t requestedFlags, int 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 (std::set::const_iterator it = goodId.begin(); it != goodId.end(); + it++) { + if ((idToInfo[*it].services & requestedFlags) == requestedFlags) + goodIdFiltered.push_back(*it); + } + + if (!goodIdFiltered.size()) return; + + if (max > goodIdFiltered.size() / 2) max = goodIdFiltered.size() / 2; + if (max < 1) max = 1; + + set ids; + while (ids.size() < max) { + ids.insert(goodIdFiltered[rand() % goodIdFiltered.size()]); + } + for (set::const_iterator it = ids.begin(); it != ids.end(); it++) { + CService &ip = idToInfo[*it].ip; + if (nets[ip.GetNetwork()]) ips.insert(ip); + } +} diff --git a/src/seeder/dns.h b/src/seeder/dns.h new file mode 100644 --- /dev/null +++ b/src/seeder/dns.h @@ -0,0 +1,29 @@ +#ifndef _DNS_H_ +#define _DNS_H_ 1 + +#include + +typedef struct { + int v; + union { + unsigned char v4[4]; + unsigned char v6[16]; + } data; +} addr_t; + +typedef struct { + int port; + int datattl; + int nsttl; + const char *host; + const char *ns; + const char *mbox; + int (*cb)(void *opt, char *requested_hostname, addr_t *addr, int max, + int ipv4, int ipv6); + // stats + uint64_t nRequests; +} dns_opt_t; + +extern int dnsserver(dns_opt_t *opt); + +#endif diff --git a/src/seeder/dns.c b/src/seeder/dns.c new file mode 100644 --- /dev/null +++ b/src/seeder/dns.c @@ -0,0 +1,567 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dns.h" + +#define BUFLEN 512 + +#if defined IP_RECVDSTADDR +#define DSTADDR_SOCKOPT IP_RECVDSTADDR +#define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in6_addr))) +#define dstaddr(x) (CMSG_DATA(x)) +#elif defined IPV6_PKTINFO +struct in6_pktinfo { + struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + unsigned int ipi6_ifindex; /* send/recv interface index */ +}; + +#define DSTADDR_SOCKOPT IPV6_PKTINFO +#define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in6_pktinfo))) +#define dstaddr(x) (&(((struct in6_pktinfo *)(CMSG_DATA(x)))->ipi6_addr)) +#else +#error "can't determine socket option" +#endif + +union control_data { + struct cmsghdr cmsg; + unsigned char data[DSTADDR_DATASIZE]; +}; + +typedef enum { CLASS_IN = 1, QCLASS_ANY = 255 } dns_class; + +typedef enum { + TYPE_A = 1, + TYPE_NS = 2, + TYPE_CNAME = 5, + TYPE_SOA = 6, + TYPE_MX = 15, + TYPE_AAAA = 28, + TYPE_SRV = 33, + QTYPE_ANY = 255 +} dns_type; + +// 0: ok +// -1: premature end of input, forward reference, component > 63 char, invalid +// character +// -2: insufficient space in output +int static parse_name(const unsigned char **inpos, const unsigned char *inend, + const unsigned char *inbuf, char *buf, size_t bufsize) { + size_t bufused = 0; + int init = 1; + do { + if (*inpos == inend) return -1; + // read length of next component + int octet = *((*inpos)++); + if (octet == 0) { + buf[bufused] = 0; + return 0; + } + // add dot in output + if (!init) { + if (bufused == bufsize - 1) return -2; + buf[bufused++] = '.'; + } else + init = 0; + // handle references + if ((octet & 0xC0) == 0xC0) { + if (*inpos == inend) return -1; + int ref = ((octet - 0xC0) << 8) + *((*inpos)++); + if (ref < 0 || ref >= (*inpos) - inbuf - 2) return -1; + const unsigned char *newbuf = inbuf + ref; + return parse_name(&newbuf, (*inpos) - 2, inbuf, buf + bufused, + bufsize - bufused); + } + if (octet > 63) return -1; + // copy label + while (octet) { + if (*inpos == inend) return -1; + if (bufused == bufsize - 1) return -2; + int c = *((*inpos)++); + if (c == '.') return -1; + octet--; + buf[bufused++] = c; + } + } while (1); +} + +// 0: k +// -1: component > 63 characters +// -2: insufficent space in output +// -3: two subsequent dots +int static write_name(unsigned char **outpos, const unsigned char *outend, + const char *name, int offset) { + while (*name != 0) { + char *dot = strchr(name, '.'); + const char *fin = dot; + if (!dot) fin = name + strlen(name); + if (fin - name > 63) return -1; + if (fin == name) return -3; + if (outend - *outpos < fin - name + 2) return -2; + *((*outpos)++) = fin - name; + memcpy(*outpos, name, fin - name); + *outpos += fin - name; + if (!dot) break; + name = dot + 1; + } + if (offset < 0) { + // no reference + if (outend == *outpos) return -2; + *((*outpos)++) = 0; + } else { + if (outend - *outpos < 2) return -2; + *((*outpos)++) = (offset >> 8) | 0xC0; + *((*outpos)++) = offset & 0xFF; + } + return 0; +} + +int static write_record(unsigned char **outpos, const unsigned char *outend, + const char *name, int offset, dns_type typ, + dns_class cls, int ttl) { + unsigned char *oldpos = *outpos; + int error = 0; + // name + int ret = write_name(outpos, outend, name, offset); + if (ret) { + error = ret; + goto error; + } + if (outend - *outpos < 8) { + error = -4; + goto error; + } + // type + *((*outpos)++) = typ >> 8; + *((*outpos)++) = typ & 0xFF; + // class + *((*outpos)++) = cls >> 8; + *((*outpos)++) = cls & 0xFF; + // ttl + *((*outpos)++) = (ttl >> 24) & 0xFF; + *((*outpos)++) = (ttl >> 16) & 0xFF; + *((*outpos)++) = (ttl >> 8) & 0xFF; + *((*outpos)++) = ttl & 0xFF; + return 0; +error: + *outpos = oldpos; + return error; +} + +int static write_record_a(unsigned char **outpos, const unsigned char *outend, + const char *name, int offset, dns_class cls, int ttl, + const addr_t *ip) { + if (ip->v != 4) return -6; + unsigned char *oldpos = *outpos; + int error = 0; + int ret = write_record(outpos, outend, name, offset, TYPE_A, cls, ttl); + if (ret) return ret; + if (outend - *outpos < 6) { + error = -5; + goto error; + } + // rdlength + *((*outpos)++) = 0; + *((*outpos)++) = 4; + // rdata + for (int i = 0; i < 4; i++) + *((*outpos)++) = ip->data.v4[i]; + return 0; +error: + *outpos = oldpos; + return error; +} + +int static write_record_aaaa(unsigned char **outpos, + const unsigned char *outend, const char *name, + int offset, dns_class cls, int ttl, + const addr_t *ip) { + if (ip->v != 6) return -6; + unsigned char *oldpos = *outpos; + int error = 0; + int ret = write_record(outpos, outend, name, offset, TYPE_AAAA, cls, ttl); + if (ret) return ret; + if (outend - *outpos < 6) { + error = -5; + goto error; + } + // rdlength + *((*outpos)++) = 0; + *((*outpos)++) = 16; + // rdata + for (int i = 0; i < 16; i++) + *((*outpos)++) = ip->data.v6[i]; + return 0; +error: + *outpos = oldpos; + return error; +} + +int static write_record_ns(unsigned char **outpos, const unsigned char *outend, + char *name, int offset, dns_class cls, int ttl, + const char *ns) { + unsigned char *oldpos = *outpos; + int ret = write_record(outpos, outend, name, offset, TYPE_NS, cls, ttl); + if (ret) return ret; + int error = 0; + if (outend - *outpos < 2) { + error = -5; + goto error; + } + (*outpos) += 2; + unsigned char *curpos = *outpos; + ret = write_name(outpos, outend, ns, -1); + if (ret) { + error = ret; + goto error; + } + curpos[-2] = (*outpos - curpos) >> 8; + curpos[-1] = (*outpos - curpos) & 0xFF; + return 0; +error: + *outpos = oldpos; + return error; +} + +int static write_record_soa(unsigned char **outpos, const unsigned char *outend, + char *name, int offset, dns_class cls, int ttl, + const char *mname, const char *rname, + uint32_t serial, uint32_t refresh, uint32_t retry, + uint32_t expire, uint32_t minimum) { + unsigned char *oldpos = *outpos; + int ret = write_record(outpos, outend, name, offset, TYPE_SOA, cls, ttl); + if (ret) return ret; + int error = 0; + if (outend - *outpos < 2) { + error = -5; + goto error; + } + (*outpos) += 2; + unsigned char *curpos = *outpos; + ret = write_name(outpos, outend, mname, -1); + if (ret) { + error = ret; + goto error; + } + ret = write_name(outpos, outend, rname, -1); + if (ret) { + error = ret; + goto error; + } + if (outend - *outpos < 20) { + error = -5; + goto error; + } + *((*outpos)++) = (serial >> 24) & 0xFF; + *((*outpos)++) = (serial >> 16) & 0xFF; + *((*outpos)++) = (serial >> 8) & 0xFF; + *((*outpos)++) = serial & 0xFF; + *((*outpos)++) = (refresh >> 24) & 0xFF; + *((*outpos)++) = (refresh >> 16) & 0xFF; + *((*outpos)++) = (refresh >> 8) & 0xFF; + *((*outpos)++) = refresh & 0xFF; + *((*outpos)++) = (retry >> 24) & 0xFF; + *((*outpos)++) = (retry >> 16) & 0xFF; + *((*outpos)++) = (retry >> 8) & 0xFF; + *((*outpos)++) = retry & 0xFF; + *((*outpos)++) = (expire >> 24) & 0xFF; + *((*outpos)++) = (expire >> 16) & 0xFF; + *((*outpos)++) = (expire >> 8) & 0xFF; + *((*outpos)++) = expire & 0xFF; + *((*outpos)++) = (minimum >> 24) & 0xFF; + *((*outpos)++) = (minimum >> 16) & 0xFF; + *((*outpos)++) = (minimum >> 8) & 0xFF; + *((*outpos)++) = minimum & 0xFF; + curpos[-2] = (*outpos - curpos) >> 8; + curpos[-1] = (*outpos - curpos) & 0xFF; + return 0; +error: + *outpos = oldpos; + return error; +} + +ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, + size_t insize, unsigned char *outbuf) { + int error = 0; + if (insize < 12) // DNS header + return -1; + // copy id + outbuf[0] = inbuf[0]; + outbuf[1] = inbuf[1]; + // copy flags; + outbuf[2] = inbuf[2]; + outbuf[3] = inbuf[3]; + // clear error + outbuf[3] &= ~15; + // check qr + if (inbuf[2] & 128) { /* printf("Got response?\n"); */ + error = 1; + goto error; + } + // check opcode + if (((inbuf[2] & 120) >> 3) != 0) { /* printf("Opcode nonzero?\n"); */ + error = 4; + goto error; + } + // unset TC + outbuf[2] &= ~2; + // unset RA + outbuf[3] &= ~128; + // check questions + int nquestion = (inbuf[4] << 8) + inbuf[5]; + if (nquestion == 0) { /* printf("No questions?\n"); */ + error = 0; + goto error; + } + if (nquestion > 1) { /* printf("Multiple questions %i?\n", nquestion); */ + error = 4; + goto error; + } + const unsigned char *inpos = inbuf + 12; + const unsigned char *inend = inbuf + insize; + char name[256]; + int offset = inpos - inbuf; + int ret = parse_name(&inpos, inend, inbuf, name, 256); + if (ret == -1) { + error = 1; + goto error; + } + if (ret == -2) { + error = 5; + goto error; + } + int namel = strlen(name), hostl = strlen(opt->host); + if (strcasecmp(name, opt->host) && + (namel < hostl + 2 || name[namel - hostl - 1] != '.' || + strcasecmp(name + namel - hostl, opt->host))) { + error = 5; + goto error; + } + if (inend - inpos < 4) { + error = 1; + goto error; + } + // copy question to output + memcpy(outbuf + 12, inbuf + 12, inpos + 4 - (inbuf + 12)); + // set counts + outbuf[4] = 0; + outbuf[5] = 1; + outbuf[6] = 0; + outbuf[7] = 0; + outbuf[8] = 0; + outbuf[9] = 0; + outbuf[10] = 0; + outbuf[11] = 0; + // set qr + outbuf[2] |= 128; + + int typ = (inpos[0] << 8) + inpos[1]; + int cls = (inpos[2] << 8) + inpos[3]; + inpos += 4; + + unsigned char *outpos = outbuf + (inpos - inbuf); + unsigned char *outend = outbuf + BUFLEN; + + // printf("DNS: Request host='%s' type=%i class=%i\n", name, typ, cls); + + // calculate max size of authority section + + int max_auth_size = 0; + + if (!((typ == TYPE_NS || typ == QTYPE_ANY) && + (cls == CLASS_IN || cls == QCLASS_ANY))) { + // authority section will be necessary, either NS or SOA + unsigned char *newpos = outpos; + write_record_ns(&newpos, outend, "", offset, CLASS_IN, 0, opt->ns); + max_auth_size = newpos - outpos; + + newpos = outpos; + write_record_soa(&newpos, outend, "", offset, CLASS_IN, opt->nsttl, + opt->ns, opt->mbox, time(NULL), 604800, 86400, 2592000, + 604800); + if (max_auth_size < newpos - outpos) max_auth_size = newpos - outpos; + // printf("Authority section will claim %i bytes max\n", + // max_auth_size); + } + + // Answer section + + int have_ns = 0; + + // NS records + if ((typ == TYPE_NS || typ == QTYPE_ANY) && + (cls == CLASS_IN || cls == QCLASS_ANY)) { + int ret2 = write_record_ns(&outpos, outend - max_auth_size, "", offset, + CLASS_IN, opt->nsttl, opt->ns); + // printf("wrote NS record: %i\n", ret2); + if (!ret2) { + outbuf[7]++; + have_ns++; + } + } + + // SOA records + if ((typ == TYPE_SOA || typ == QTYPE_ANY) && + (cls == CLASS_IN || cls == QCLASS_ANY) && opt->mbox) { + int ret2 = write_record_soa(&outpos, outend - max_auth_size, "", offset, + CLASS_IN, opt->nsttl, opt->ns, opt->mbox, + time(NULL), 604800, 86400, 2592000, 604800); + // printf("wrote SOA record: %i\n", ret2); + if (!ret2) { + outbuf[7]++; + } + } + + // A/AAAA records + if ((typ == TYPE_A || typ == TYPE_AAAA || typ == QTYPE_ANY) && + (cls == CLASS_IN || cls == QCLASS_ANY)) { + addr_t addr[32]; + int naddr = opt->cb((void *)opt, name, addr, 32, + typ == TYPE_A || typ == QTYPE_ANY, + typ == TYPE_AAAA || typ == QTYPE_ANY); + int n = 0; + while (n < naddr) { + int ret = 1; + if (addr[n].v == 4) + ret = write_record_a(&outpos, outend - max_auth_size, "", + offset, CLASS_IN, opt->datattl, &addr[n]); + else if (addr[n].v == 6) + ret = + write_record_aaaa(&outpos, outend - max_auth_size, "", + offset, CLASS_IN, opt->datattl, &addr[n]); + // printf("wrote A record: %i\n", ret); + if (!ret) { + n++; + outbuf[7]++; + } else + break; + } + } + + // Authority section + if (!have_ns && outbuf[7]) { + int ret2 = write_record_ns(&outpos, outend, "", offset, CLASS_IN, + opt->nsttl, opt->ns); + // printf("wrote NS record: %i\n", ret2); + if (!ret2) { + outbuf[9]++; + } + } else if (!outbuf[7]) { + // Didn't include any answers, so reply with SOA as this is a negative + // response. If we replied with NS above we'd create a bad horizontal + // referral loop, as the NS response indicates where the resolver should + // try next. + int ret2 = write_record_soa(&outpos, outend, "", offset, CLASS_IN, + opt->nsttl, opt->ns, opt->mbox, time(NULL), + 604800, 86400, 2592000, 604800); + // printf("wrote SOA record: %i\n", ret2); + if (!ret2) { + outbuf[9]++; + } + } + + // set AA + outbuf[2] |= 4; + + return outpos - outbuf; +error: + // set error + outbuf[3] |= error & 0xF; + // set counts + outbuf[4] = 0; + outbuf[5] = 0; + outbuf[6] = 0; + outbuf[7] = 0; + outbuf[8] = 0; + outbuf[9] = 0; + outbuf[10] = 0; + outbuf[11] = 0; + return 12; +} + +static int listenSocket = -1; + +int dnsserver(dns_opt_t *opt) { + struct sockaddr_in6 si_other; + int senderSocket = -1; + senderSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (senderSocket == -1) return -3; + + int replySocket; + if (listenSocket == -1) { + struct sockaddr_in6 si_me; + if ((listenSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + listenSocket = -1; + return -1; + } + replySocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (replySocket == -1) { + close(listenSocket); + return -1; + } + int sockopt = 1; + setsockopt(listenSocket, IPPROTO_IPV6, DSTADDR_SOCKOPT, &sockopt, + sizeof sockopt); + memset((char *)&si_me, 0, sizeof(si_me)); + si_me.sin6_family = AF_INET6; + si_me.sin6_port = htons(opt->port); + si_me.sin6_addr = in6addr_any; + if (bind(listenSocket, (struct sockaddr *)&si_me, sizeof(si_me)) == -1) + return -2; + } + + unsigned char inbuf[BUFLEN], outbuf[BUFLEN]; + struct iovec iov[1] = { + { + .iov_base = inbuf, .iov_len = sizeof(inbuf), + }, + }; + union control_data cmsg; + struct msghdr msg = { + .msg_name = &si_other, + .msg_namelen = sizeof(si_other), + .msg_iov = iov, + .msg_iovlen = 1, + .msg_control = &cmsg, + .msg_controllen = sizeof(cmsg), + }; + for (; 1; ++(opt->nRequests)) { + ssize_t insize = recvmsg(listenSocket, &msg, 0); + // unsigned char *addr = (unsigned char*)&si_other.sin_addr.s_addr; + // printf("DNS: Request %llu from %i.%i.%i.%i:%i of %i bytes\n", + // (unsigned long long)(opt->nRequests), addr[0], addr[1], addr[2], + // addr[3], ntohs(si_other.sin_port), (int)insize); + if (insize <= 0) continue; + + ssize_t ret = dnshandle(opt, inbuf, insize, outbuf); + if (ret <= 0) continue; + + bool handled = false; + for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; + hdr = CMSG_NXTHDR(&msg, hdr)) { + if (hdr->cmsg_level == IPPROTO_IP && + hdr->cmsg_type == DSTADDR_SOCKOPT) { + msg.msg_iov[0].iov_base = outbuf; + msg.msg_iov[0].iov_len = ret; + sendmsg(listenSocket, &msg, 0); + msg.msg_iov[0].iov_base = inbuf; + msg.msg_iov[0].iov_len = sizeof(inbuf); + handled = true; + } + } + if (!handled) + sendto(listenSocket, outbuf, ret, 0, (struct sockaddr *)&si_other, + sizeof(si_other)); + } + return 0; +} diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp new file mode 100644 --- /dev/null +++ b/src/seeder/main.cpp @@ -0,0 +1,564 @@ +#include + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include +#include + +#include "bitcoin.h" +#include "db.h" + +using namespace std; + +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), nDnsThreads(4), nPort(53), mbox(NULL), ns(NULL), + host(NULL), tor(NULL), fUseTestNet(false), fWipeBan(false), + fWipeIgnore(false), ipv4_proxy(NULL), ipv6_proxy(NULL) {} + + void ParseCommandLine(int argc, char **argv) { + static const char *help = + "Bitcoin-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, NULL, 10); + if (n > 0 && n < 1000) nThreads = n; + break; + } + + case 'd': { + int n = strtol(optarg, NULL, 10); + if (n > 0 && n < 1000) nDnsThreads = n; + break; + } + + case 'p': { + int p = strtol(optarg, NULL, 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 | NODE_BITCOIN_CASH); + filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | + NODE_BLOOM); + filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | + NODE_XTHIN); + filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | + NODE_BLOOM | NODE_XTHIN); + } + if (host != NULL && ns == NULL) 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; + int wait = 5; + db.GetMany(ips, 16, wait); + int64 now = time(NULL); + if (ips.empty()) { + wait *= 1000; + wait += rand() % (500 * *nThreads); + Sleep(wait); + continue; + } + vector addr; + for (int i = 0; i < ips.size(); i++) { + CServiceResult &res = ips[i]; + res.nBanTime = 0; + res.nClientV = 0; + res.nHeight = 0; + res.strClientV = ""; + bool getaddr = res.ourLastSuccess + 86400 < now; + res.fGood = + TestNode(res.service, res.nBanTime, res.nClientV, + res.strClientV, res.nHeight, getaddr ? &addr : NULL); + } + db.ResultMany(ips); + db.Add(addr); + } while (1); + return nullptr; +} + +extern "C" int GetIPList(void *thread, char *requestedHostname, addr_t *addr, + int max, int ipv4, int 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(NULL); + 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))) { + set ips; + db.GetIPs(ips, requestedFlags, 1000, nets); + dbQueries++; + thisflag.cache.clear(); + thisflag.nIPv4 = 0; + thisflag.nIPv6 = 0; + thisflag.cache.reserve(ips.size()); + for (set::iterator it = ips.begin(); it != ips.end(); + it++) { + struct in_addr addr; + struct in6_addr addr6; + if ((*it).GetInAddr(&addr)) { + addr_t a; + a.v = 4; + memcpy(&a.data.v4, &addr, 4); + thisflag.cache.push_back(a); + thisflag.nIPv4++; + } else if ((*it).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" int GetIPList(void *data, char *requestedHostname, addr_t *addr, + int max, int ipv4, int 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]; + unsigned int size = thisflag.cache.size(); + unsigned int maxmax = + (ipv4 ? thisflag.nIPv4 : 0) + (ipv6 ? thisflag.nIPv6 : 0); + if (max > size) max = size; + if (max > maxmax) max = maxmax; + int i = 0; + while (i < max) { + int 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; +} + +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 { + Sleep(100000 << count); // First 100s, than 200s, 400s, 800s, 1600s, and + // then 3200s forever + if (count < 5) count++; + { + vector v = db.GetAll(); + sort(v.begin(), v.end(), StatCompare); + FILE *f = fopen("dnsseed.dat.new", "w+"); + if (f) { + { + CAutoFile cf(f); + 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 (vector::const_iterator it = v.begin(); + it < v.end(); it++) { + CAddrReport rep = *it; + 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(NULL)), 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(NULL); + 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 string mainnet_seeds[] = { + "seed.bitcoinabc.org", "seed-abc.bitcoinforks.org", "seed.bitprim.org", + "seed.deadalnix.me", "seeder.criptolayer.net", ""}; +static const string testnet_seeds[] = {"testnet-seed.bitcoinabc.org", + "testnet-seed-abc.bitcoinforks.org", + "testnet-seed.bitcoinunlimited.info", + "testnet-seed.bitprim.org", + "testnet-seed.deadalnix.me", + "testnet-seeder.criptolayer.net", + ""}; +static const string *seeds = mainnet_seeds; + +extern "C" void *ThreadSeeder(void *) { + if (!fTestNet) { + db.Add(CService("kjy2eqzk4zwi5zd3.onion", 8333), true); + } + do { + for (int i = 0; seeds[i] != ""; i++) { + vector ips; + LookupHost(seeds[i].c_str(), ips); + for (vector::iterator it = ips.begin(); it != ips.end(); + it++) { + db.Add(CService(*it, GetDefaultPort()), true); + } + } + Sleep(1800000); + } while (1); + return nullptr; +} + +int main(int argc, char **argv) { + signal(SIGPIPE, SIG_IGN); + setbuf(stdout, NULL); + 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); + 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); + 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); + 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); + 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, NULL, ThreadDNS, dnsThread[i]); + printf("."); + Sleep(20); + } + printf("done\n"); + } + printf("Starting seeder..."); + pthread_create(&threadSeed, NULL, ThreadSeeder, NULL); + 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, NULL, ThreadStats, NULL); + pthread_create(&threadDump, NULL, ThreadDumper, NULL); + void *res; + pthread_join(threadDump, &res); + return 0; +} diff --git a/src/seeder/netbase.h b/src/seeder/netbase.h new file mode 100644 --- /dev/null +++ b/src/seeder/netbase.h @@ -0,0 +1,149 @@ +// 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_NETBASE_H +#define BITCOIN_NETBASE_H + +#include +#include + +#include "compat.h" +#include "serialize.h" + +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: + unsigned char 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 GetHash() const; + bool GetInAddr(struct in_addr *pipv4Addr) const; + std::vector GetGroup() const; + int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) 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); + + IMPLEMENT_SERIALIZE(READWRITE(FLATDATA(ip));) +}; + +/** A combination of a network address (CNetAddr) and a (TCP) port */ +class CService : 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); + 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); + 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); + + IMPLEMENT_SERIALIZE(CService *pthis = const_cast(this); + READWRITE(FLATDATA(ip)); + unsigned short portN = htons(port); READWRITE(portN); + if (fRead) 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 IsProxy(const CNetAddr &addr); +bool SetNameProxy(CService 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 fAllowLookup = true); +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, + int nTimeout = nConnectTimeout); +bool ConnectSocketByName(CService &addr, SOCKET &hSocketRet, + const char *pszDest, int portDefault = 0, + int nTimeout = nConnectTimeout); + +#endif diff --git a/src/seeder/netbase.cpp b/src/seeder/netbase.cpp new file mode 100644 --- /dev/null +++ b/src/seeder/netbase.cpp @@ -0,0 +1,1070 @@ +// 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 "util.h" + +#ifndef WIN32 +#include +#endif + +#include "strlcpy.h" +#include // for to_lower() + +#define printf my_printf + +using namespace std; + +// Settings +typedef std::pair proxyType; +static proxyType proxyInfo[NET_MAX]; +static proxyType nameproxyInfo; +int nConnectTimeout = 5000; +bool fNameLookup = false; + +static const unsigned char 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; + bool fBracketed = + fHaveColon && + (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and + // in[0]=='[', colon is not 0, + // so in[colon-1] is safe + bool fMultiColon = + fHaveColon && (in.find_last_of(':', colon - 1) != in.npos); + if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) { + char *endp = NULL; + 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; +} + +bool static 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 = NULL; + int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes); + if (nErr) return false; + + struct addrinfo *aiTrav = aiRes; + while (aiTrav != NULL && + (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) { + 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); + return true; +} + +bool Lookup(const char *pszName, CService &addr, int portDefault, + bool fAllowLookup) { + 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) { + return Lookup(pszName, addr, portDefault, false); +} + +bool static Socks4(const CService &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; +} + +bool static Socks5(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"); + } + 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; +} + +bool static ConnectSocketDirectly(const CService &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, NULL, &fdset, NULL, &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) { + 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) { + assert(net >= 0 && net < NET_MAX); + if (!proxyInfo[net].second) return false; + addrProxy = proxyInfo[net].first; + return true; +} + +bool SetNameProxy(CService 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) { + 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, + const char *pszDest, int portDefault, int nTimeout) { + string strDest; + int port = portDefault; + SplitHostPort(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); + } + addr = CService("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 unsigned char pchOnionCat[] = {0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43}; +static const unsigned char 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 unsigned char 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 unsigned char 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 unsigned char 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 unsigned char 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) + unsigned char 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), NULL, 0, NI_NUMERICHOST)) + return std::string(name); + } + if (IsIPv4()) + return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), + GetByte(0)); + else + 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; + } + // for he.net, use /36 groups + else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && + GetByte(13) == 0x04 && GetByte(12) == 0x70) + nBits = 36; + // for the rest of the IPv6 network, use /32 groups + else + 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 CNetAddr::GetHash() const { + uint256 hash = Hash(&ip[0], &ip[16]); + uint64 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; +int static GetExtNetwork(const CNetAddr *addr) { + if (addr == NULL) 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: + return fTunnel ? REACH_IPV6_WEAK + : REACH_IPV6_STRONG; // only prefer giving + // our IPv6 address if + // it's not tunnelled + } + case NET_TOR: + switch (ourNet) { + default: + return REACH_DEFAULT; + case NET_IPV4: + return REACH_IPV4; // Tor users can connect to IPv4 as well + 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: + return REACH_PRIVATE; // assume connections from unroutable + // addresses are + case NET_TOR: + return REACH_PRIVATE; // either from Tor/I2P, or don't care + // about our address + } + } +} + +void CService::Init() { + port = 0; +} + +CService::CService() { + Init(); +} + +CService::CService(const CNetAddr &cip, unsigned short portIn) + : CNetAddr(cip), port(portIn) {} + +CService::CService(const struct in_addr &ipv4Addr, unsigned short portIn) + : CNetAddr(ipv4Addr), port(portIn) {} + +CService::CService(const struct in6_addr &ipv6Addr, unsigned short portIn) + : CNetAddr(ipv6Addr), port(portIn) {} + +CService::CService(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) + : CNetAddr(addr.sin6_addr), port(ntohs(addr.sin6_port)) { + assert(addr.sin6_family == AF_INET6); +} + +bool CService::SetSockAddr(const struct sockaddr *paddr) { + switch (paddr->sa_family) { + case AF_INET: + *this = CService(*(const struct sockaddr_in *)paddr); + return true; + case AF_INET6: + *this = CService(*(const struct sockaddr_in6 *)paddr); + return true; + default: + return false; + } +} + +CService::CService(const char *pszIpPort, bool fAllowLookup) { + Init(); + CService ip; + if (Lookup(pszIpPort, ip, 0, fAllowLookup)) *this = ip; +} + +CService::CService(const char *pszIpPort, int portDefault, bool fAllowLookup) { + Init(); + CService ip; + if (Lookup(pszIpPort, ip, portDefault, fAllowLookup)) *this = ip; +} + +CService::CService(const std::string &strIpPort, bool fAllowLookup) { + Init(); + CService ip; + if (Lookup(strIpPort.c_str(), ip, 0, fAllowLookup)) *this = ip; +} + +CService::CService(const std::string &strIpPort, int portDefault, + bool fAllowLookup) { + Init(); + CService ip; + if (Lookup(strIpPort.c_str(), ip, portDefault, fAllowLookup)) *this = ip; +} + +unsigned short CService::GetPort() const { + return port; +} + +bool operator==(const CService &a, const CService &b) { + return (CNetAddr)a == (CNetAddr)b && a.port == b.port; +} + +bool operator!=(const CService &a, const CService &b) { + return (CNetAddr)a != (CNetAddr)b || a.port != b.port; +} + +bool operator<(const CService &a, const CService &b) { + return (CNetAddr)a < (CNetAddr)b || + ((CNetAddr)a == (CNetAddr)b && a.port < b.port); +} + +bool CService::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 vKey; + vKey.resize(18); + memcpy(&vKey[0], ip, 16); + vKey[16] = port / 0x100; + vKey[17] = port & 0x0FF; + return vKey; +} + +std::string CService::ToStringPort() const { + return strprintf("%u", port); +} + +std::string CService::ToStringIPPort() const { + if (IsIPv4() || IsTor() || IsI2P()) { + return ToStringIP() + ":" + ToStringPort(); + } else { + return "[" + ToStringIP() + "]:" + ToStringPort(); + } +} + +std::string CService::ToString() const { + return ToStringIPPort(); +} + +void CService::print() const { + printf("CService(%s)\n", ToString().c_str()); +} + +void CService::SetPort(unsigned short portIn) { + port = portIn; +} diff --git a/src/seeder/protocol.h b/src/seeder/protocol.h new file mode 100644 --- /dev/null +++ b/src/seeder/protocol.h @@ -0,0 +1,111 @@ +// 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 __INCLUDED_PROTOCOL_H__ +#define __INCLUDED_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 unsigned char pchMessageStart[4]; + +class CMessageHeader { +public: + CMessageHeader(); + CMessageHeader(const char *pszCommand, unsigned int nMessageSizeIn); + + std::string GetCommand() const; + bool IsValid() const; + + IMPLEMENT_SERIALIZE(READWRITE(FLATDATA(pchMessageStart)); + READWRITE(FLATDATA(pchCommand)); + READWRITE(nMessageSize); + if (nVersion >= 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 { +public: + CAddress(); + CAddress(CService ipIn, + uint64_t nServicesIn = NODE_NETWORK | NODE_BITCOIN_CASH); + + void Init(); + + IMPLEMENT_SERIALIZE(CAddress *pthis = const_cast(this); + CService *pip = (CService *)pthis; + if (fRead) pthis->Init(); + if (nType & SER_DISK) READWRITE(nVersion); + if ((nType & SER_DISK) || + (nVersion >= 31402 && !(nType & SER_GETHASH))) + READWRITE(nTime); + READWRITE(nServices); READWRITE(*pip);) + + void print() const; + + // TODO: make private (improves encapsulation) +public: + uint64 nServices; + + // disk and network only + unsigned int nTime; +}; + +class CInv { +public: + CInv(); + CInv(int typeIn, const uint256 &hashIn); + CInv(const std::string &strType, const uint256 &hashIn); + + IMPLEMENT_SERIALIZE(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: + int type; + uint256 hash; +}; + +#endif // __INCLUDED_PROTOCOL_H__ diff --git a/src/seeder/protocol.cpp b/src/seeder/protocol.cpp new file mode 100644 --- /dev/null +++ b/src/seeder/protocol.cpp @@ -0,0 +1,134 @@ +// 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 +#include + +#include "netbase.h" +#include "protocol.h" +#include "util.h" + +#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() { + Init(); +} + +CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(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 = 0; +} + +CInv::CInv(int typeIn, const uint256 &hashIn) { + type = typeIn; + hash = hashIn; +} + +CInv::CInv(const std::string &strType, const uint256 &hashIn) { + int 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/serialize.h b/src/seeder/serialize.h new file mode 100644 --- /dev/null +++ b/src/seeder/serialize.h @@ -0,0 +1,1358 @@ +// 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 BITCOIN_SERIALIZE_H +#define BITCOIN_SERIALIZE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false); else for +#endif + +#ifdef WIN32 +#include +// This is used to attempt to keep keying material out of swap +// Note that VirtualLock does not provide this as a guarantee on Windows, +// but, in practice, memory that has been VirtualLock'd almost never gets +// written to +// the pagefile except in rare circumstances where memory is extremely low. +#include +#define mlock(p, n) VirtualLock((p), (n)); +#define munlock(p, n) VirtualUnlock((p), (n)); +#else +#include +#include +/* This comes from limits.h if it's not defined there set a sane default */ +#ifndef PAGESIZE +#include +#define PAGESIZE sysconf(_SC_PAGESIZE) +#endif +#define mlock(a, b) \ + mlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))), \ + (((((size_t)(a)) + (b)-1) | ((PAGESIZE)-1)) + 1) - \ + (((size_t)(a)) & (~((PAGESIZE)-1)))) +#define munlock(a, b) \ + munlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))), \ + (((((size_t)(a)) + (b)-1) | ((PAGESIZE)-1)) + 1) - \ + (((size_t)(a)) & (~((PAGESIZE)-1)))) +#endif + +class CScript; +class CDataStream; +class CAutoFile; +static const unsigned int MAX_SIZE = 0x02000000; + +static const int PROTOCOL_VERSION = 60000; + +// Used to bypass the rule against non-const reference to temporary +// where it makes sense with wrappers such as CFlatData or CTxDB +template inline T &REF(const T &val) { + return const_cast(val); +} + +///////////////////////////////////////////////////////////////// +// +// Templates for serializing to anything that looks like a stream, +// i.e. anything that supports .read(char*, int) and .write(char*, int) +// + +enum { + // primary actions + SER_NETWORK = (1 << 0), + SER_DISK = (1 << 1), + SER_GETHASH = (1 << 2), + + // modifiers + SER_SKIPSIG = (1 << 16), + SER_BLOCKHEADERONLY = (1 << 17), +}; + +#define IMPLEMENT_SERIALIZE(statements) \ + unsigned int GetSerializeSize(int nType = 0, \ + int nVersion = PROTOCOL_VERSION) const { \ + CSerActionGetSerializeSize ser_action; \ + const bool fGetSize = true; \ + const bool fWrite = false; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + ser_streamplaceholder s; \ + s.nType = nType; \ + s.nVersion = nVersion; \ + { statements } \ + return nSerSize; \ + } \ + template \ + void Serialize(Stream &s, int nType = 0, int nVersion = PROTOCOL_VERSION) \ + const { \ + CSerActionSerialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = true; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + { statements } \ + } \ + template \ + void Unserialize(Stream &s, int nType = 0, \ + int nVersion = PROTOCOL_VERSION) { \ + CSerActionUnserialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = false; \ + const bool fRead = true; \ + unsigned int nSerSize = 0; \ + { statements } \ + } + +#define READWRITE(obj) \ + (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) + +// +// Basic types +// +#define WRITEDATA(s, obj) s.write((char *)&(obj), sizeof(obj)) +#define READDATA(s, obj) s.read((char *)&(obj), sizeof(obj)) + +inline unsigned int GetSerializeSize(char a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(signed char a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(unsigned char a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(signed short a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(unsigned short a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(signed int a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(unsigned int a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(signed long a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(unsigned long a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(int64 a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(uint64 a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(float a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(double a, int, int = 0) { + return sizeof(a); +} + +template +inline void Serialize(Stream &s, char a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, signed char a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, unsigned char a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, signed short a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, unsigned short a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, signed int a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, unsigned int a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, signed long a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, unsigned long a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, int64 a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, uint64 a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, float a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, double a, int, int = 0) { + WRITEDATA(s, a); +} + +template +inline void Unserialize(Stream &s, char &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, signed char &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, unsigned char &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, signed short &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, unsigned short &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, signed int &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, unsigned int &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, signed long &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, unsigned long &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, int64 &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, uint64 &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, float &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, double &a, int, int = 0) { + READDATA(s, a); +} + +inline unsigned int GetSerializeSize(bool a, int, int = 0) { + return sizeof(char); +} +template +inline void Serialize(Stream &s, bool a, int, int = 0) { + char f = a; + WRITEDATA(s, f); +} +template +inline void Unserialize(Stream &s, bool &a, int, int = 0) { + char f; + READDATA(s, f); + a = f; +} + +// +// Compact size +// size < 253 -- 1 byte +// size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) +// size <= UINT_MAX -- 5 bytes (254 + 4 bytes) +// size > UINT_MAX -- 9 bytes (255 + 8 bytes) +// +inline unsigned int GetSizeOfCompactSize(uint64 nSize) { + if (nSize < 253) + return sizeof(unsigned char); + else if (nSize <= USHRT_MAX) + return sizeof(unsigned char) + sizeof(unsigned short); + else if (nSize <= UINT_MAX) + return sizeof(unsigned char) + sizeof(unsigned int); + else + return sizeof(unsigned char) + sizeof(uint64); +} + +template void WriteCompactSize(Stream &os, uint64 nSize) { + if (nSize < 253) { + unsigned char chSize = nSize; + WRITEDATA(os, chSize); + } else if (nSize <= USHRT_MAX) { + unsigned char chSize = 253; + unsigned short xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } else if (nSize <= UINT_MAX) { + unsigned char chSize = 254; + unsigned int xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } else { + unsigned char chSize = 255; + uint64 xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + return; +} + +template uint64 ReadCompactSize(Stream &is) { + unsigned char chSize; + READDATA(is, chSize); + uint64 nSizeRet = 0; + if (chSize < 253) { + nSizeRet = chSize; + } else if (chSize == 253) { + unsigned short xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } else if (chSize == 254) { + unsigned int xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } else { + uint64 xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + if (nSizeRet > (uint64)MAX_SIZE) + throw std::ios_base::failure("ReadCompactSize() : size too large"); + return nSizeRet; +} + +// +// Wrapper for serializing arrays and POD +// There's a clever template way to make arrays serialize normally, but MSVC6 +// doesn't support it +// +#define FLATDATA(obj) \ + REF(CFlatData((char *)&(obj), (char *)&(obj) + sizeof(obj))) +class CFlatData { +protected: + char *pbegin; + char *pend; + +public: + CFlatData(void *pbeginIn, void *pendIn) + : pbegin((char *)pbeginIn), pend((char *)pendIn) {} + char *begin() { return pbegin; } + const char *begin() const { return pbegin; } + char *end() { return pend; } + const char *end() const { return pend; } + + unsigned int GetSerializeSize(int, int = 0) const { return pend - pbegin; } + + template void Serialize(Stream &s, int, int = 0) const { + s.write(pbegin, pend - pbegin); + } + + template void Unserialize(Stream &s, int, int = 0) { + s.read(pbegin, pend - pbegin); + } +}; + +// +// string stored as a fixed length field +// +template class CFixedFieldString { +protected: + const std::string *pcstr; + std::string *pstr; + +public: + explicit CFixedFieldString(const std::string &str) + : pcstr(&str), pstr(NULL) {} + explicit CFixedFieldString(std::string &str) : pcstr(&str), pstr(&str) {} + + unsigned int GetSerializeSize(int, int = 0) const { return LEN; } + + template void Serialize(Stream &s, int, int = 0) const { + char pszBuf[LEN]; + strncpy(pszBuf, pcstr->c_str(), LEN); + s.write(pszBuf, LEN); + } + + template void Unserialize(Stream &s, int, int = 0) { + if (pstr == NULL) + throw std::ios_base::failure("CFixedFieldString::Unserialize : " + "trying to unserialize to const " + "string"); + char pszBuf[LEN + 1]; + s.read(pszBuf, LEN); + pszBuf[LEN] = '\0'; + *pstr = pszBuf; + } +}; + +// +// Forward declarations +// + +// string +template +unsigned int GetSerializeSize(const std::basic_string &str, int, int = 0); +template +void Serialize(Stream &os, const std::basic_string &str, int, int = 0); +template +void Unserialize(Stream &is, std::basic_string &str, int, int = 0); + +// vector +template +unsigned int GetSerializeSize_impl(const std::vector &v, int nType, + int nVersion, const boost::true_type &); +template +unsigned int GetSerializeSize_impl(const std::vector &v, int nType, + int nVersion, const boost::false_type &); +template +inline unsigned int GetSerializeSize(const std::vector &v, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize_impl(Stream &os, const std::vector &v, int nType, + int nVersion, const boost::true_type &); +template +void Serialize_impl(Stream &os, const std::vector &v, int nType, + int nVersion, const boost::false_type &); +template +inline void Serialize(Stream &os, const std::vector &v, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize_impl(Stream &is, std::vector &v, int nType, int nVersion, + const boost::true_type &); +template +void Unserialize_impl(Stream &is, std::vector &v, int nType, int nVersion, + const boost::false_type &); +template +inline void Unserialize(Stream &is, std::vector &v, int nType, + int nVersion = PROTOCOL_VERSION); + +// others derived from vector +extern inline unsigned int GetSerializeSize(const CScript &v, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const CScript &v, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, CScript &v, int nType, + int nVersion = PROTOCOL_VERSION); + +// pair +template +unsigned int GetSerializeSize(const std::pair &item, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const std::pair &item, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, std::pair &item, int nType, + int nVersion = PROTOCOL_VERSION); + +// 3 tuple +template +unsigned int GetSerializeSize(const boost::tuple &item, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const boost::tuple &item, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, boost::tuple &item, int nType, + int nVersion = PROTOCOL_VERSION); + +// 4 tuple +template +unsigned int GetSerializeSize(const boost::tuple &item, + int nType, int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const boost::tuple &item, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, boost::tuple &item, int nType, + int nVersion = PROTOCOL_VERSION); + +// map +template +unsigned int GetSerializeSize(const std::map &m, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const std::map &m, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, std::map &m, int nType, + int nVersion = PROTOCOL_VERSION); + +// set +template +unsigned int GetSerializeSize(const std::set &m, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const std::set &m, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, std::set &m, int nType, + int nVersion = PROTOCOL_VERSION); + +// +// If none of the specialized versions above matched, default to calling member +// function. +// "int nType" is changed to "long nType" to keep from getting an ambiguous +// overload error. +// The compiler will only cast int to long if none of the other templates +// matched. +// Thanks to Boost serialization for this idea. +// +template +inline unsigned int GetSerializeSize(const T &a, long nType, + int nVersion = PROTOCOL_VERSION) { + return a.GetSerializeSize((int)nType, nVersion); +} + +template +inline void Serialize(Stream &os, const T &a, long nType, + int nVersion = PROTOCOL_VERSION) { + a.Serialize(os, (int)nType, nVersion); +} + +template +inline void Unserialize(Stream &is, T &a, long nType, + int nVersion = PROTOCOL_VERSION) { + a.Unserialize(is, (int)nType, nVersion); +} + +// +// string +// +template +unsigned int GetSerializeSize(const std::basic_string &str, int, int) { + return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); +} + +template +void Serialize(Stream &os, const std::basic_string &str, int, int) { + WriteCompactSize(os, str.size()); + if (!str.empty()) os.write((char *)&str[0], str.size() * sizeof(str[0])); +} + +template +void Unserialize(Stream &is, std::basic_string &str, int, int) { + unsigned int nSize = ReadCompactSize(is); + str.resize(nSize); + if (nSize != 0) is.read((char *)&str[0], nSize * sizeof(str[0])); +} + +// +// vector +// +template +unsigned int GetSerializeSize_impl(const std::vector &v, int nType, + int nVersion, const boost::true_type &) { + return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); +} + +template +unsigned int GetSerializeSize_impl(const std::vector &v, int nType, + int nVersion, const boost::false_type &) { + unsigned int nSize = GetSizeOfCompactSize(v.size()); + for (typename std::vector::const_iterator vi = v.begin(); + vi != v.end(); ++vi) + nSize += GetSerializeSize((*vi), nType, nVersion); + return nSize; +} + +template +inline unsigned int GetSerializeSize(const std::vector &v, int nType, + int nVersion) { + return GetSerializeSize_impl(v, nType, nVersion, + boost::is_fundamental()); +} + +template +void Serialize_impl(Stream &os, const std::vector &v, int nType, + int nVersion, const boost::true_type &) { + WriteCompactSize(os, v.size()); + if (!v.empty()) os.write((char *)&v[0], v.size() * sizeof(T)); +} + +template +void Serialize_impl(Stream &os, const std::vector &v, int nType, + int nVersion, const boost::false_type &) { + WriteCompactSize(os, v.size()); + for (typename std::vector::const_iterator vi = v.begin(); + vi != v.end(); ++vi) + ::Serialize(os, (*vi), nType, nVersion); +} + +template +inline void Serialize(Stream &os, const std::vector &v, int nType, + int nVersion) { + Serialize_impl(os, v, nType, nVersion, boost::is_fundamental()); +} + +template +void Unserialize_impl(Stream &is, std::vector &v, int nType, int nVersion, + const boost::true_type &) { + // unsigned int nSize = ReadCompactSize(is); + // v.resize(nSize); + // is.read((char*)&v[0], nSize * sizeof(T)); + + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) { + unsigned int blk = + std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize(i + blk); + is.read((char *)&v[i], blk * sizeof(T)); + i += blk; + } +} + +template +void Unserialize_impl(Stream &is, std::vector &v, int nType, int nVersion, + const boost::false_type &) { + // unsigned int nSize = ReadCompactSize(is); + // v.resize(nSize); + // for (std::vector::iterator vi = v.begin(); vi != v.end(); ++vi) + // Unserialize(is, (*vi), nType, nVersion); + + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + unsigned int nMid = 0; + while (nMid < nSize) { + nMid += 5000000 / sizeof(T); + if (nMid > nSize) nMid = nSize; + v.resize(nMid); + for (; i < nMid; i++) + Unserialize(is, v[i], nType, nVersion); + } +} + +template +inline void Unserialize(Stream &is, std::vector &v, int nType, + int nVersion) { + Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental()); +} + +// +// others derived from vector +// +inline unsigned int GetSerializeSize(const CScript &v, int nType, + int nVersion) { + return GetSerializeSize((const std::vector &)v, nType, + nVersion); +} + +template +void Serialize(Stream &os, const CScript &v, int nType, int nVersion) { + Serialize(os, (const std::vector &)v, nType, nVersion); +} + +template +void Unserialize(Stream &is, CScript &v, int nType, int nVersion) { + Unserialize(is, (std::vector &)v, nType, nVersion); +} + +// +// pair +// +template +unsigned int GetSerializeSize(const std::pair &item, int nType, + int nVersion) { + return GetSerializeSize(item.first, nType, nVersion) + + GetSerializeSize(item.second, nType, nVersion); +} + +template +void Serialize(Stream &os, const std::pair &item, int nType, + int nVersion) { + Serialize(os, item.first, nType, nVersion); + Serialize(os, item.second, nType, nVersion); +} + +template +void Unserialize(Stream &is, std::pair &item, int nType, int nVersion) { + Unserialize(is, item.first, nType, nVersion); + Unserialize(is, item.second, nType, nVersion); +} + +// +// 3 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple &item, int nType, + int nVersion) { + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream &os, const boost::tuple &item, int nType, + int nVersion) { + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); +} + +template +void Unserialize(Stream &is, boost::tuple &item, int nType, + int nVersion) { + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); +} + +// +// 4 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple &item, + int nType, int nVersion) { + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<3>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream &os, const boost::tuple &item, int nType, + int nVersion) { + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); + Serialize(os, boost::get<3>(item), nType, nVersion); +} + +template +void Unserialize(Stream &is, boost::tuple &item, int nType, + int nVersion) { + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); + Unserialize(is, boost::get<3>(item), nType, nVersion); +} + +// +// map +// +template +unsigned int GetSerializeSize(const std::map &m, int nType, + int nVersion) { + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::map::const_iterator mi = m.begin(); + mi != m.end(); ++mi) + nSize += GetSerializeSize((*mi), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream &os, const std::map &m, int nType, + int nVersion) { + WriteCompactSize(os, m.size()); + for (typename std::map::const_iterator mi = m.begin(); + mi != m.end(); ++mi) + Serialize(os, (*mi), nType, nVersion); +} + +template +void Unserialize(Stream &is, std::map &m, int nType, + int nVersion) { + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::map::iterator mi = m.begin(); + for (unsigned int i = 0; i < nSize; i++) { + std::pair item; + Unserialize(is, item, nType, nVersion); + mi = m.insert(mi, item); + } +} + +// +// set +// +template +unsigned int GetSerializeSize(const std::set &m, int nType, + int nVersion) { + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::set::const_iterator it = m.begin(); + it != m.end(); ++it) + nSize += GetSerializeSize((*it), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream &os, const std::set &m, int nType, + int nVersion) { + WriteCompactSize(os, m.size()); + for (typename std::set::const_iterator it = m.begin(); + it != m.end(); ++it) + Serialize(os, (*it), nType, nVersion); +} + +template +void Unserialize(Stream &is, std::set &m, int nType, int nVersion) { + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::set::iterator it = m.begin(); + for (unsigned int i = 0; i < nSize; i++) { + K key; + Unserialize(is, key, nType, nVersion); + it = m.insert(it, key); + } +} + +// +// Support for IMPLEMENT_SERIALIZE and READWRITE macro +// +class CSerActionGetSerializeSize {}; +class CSerActionSerialize {}; +class CSerActionUnserialize {}; + +template +inline unsigned int SerReadWrite(Stream &s, const T &obj, int nType, + int nVersion, + CSerActionGetSerializeSize ser_action) { + return ::GetSerializeSize(obj, nType, nVersion); +} + +template +inline unsigned int SerReadWrite(Stream &s, const T &obj, int nType, + int nVersion, CSerActionSerialize ser_action) { + ::Serialize(s, obj, nType, nVersion); + return 0; +} + +template +inline unsigned int SerReadWrite(Stream &s, T &obj, int nType, int nVersion, + CSerActionUnserialize ser_action) { + ::Unserialize(s, obj, nType, nVersion); + return 0; +} + +struct ser_streamplaceholder { + int nType; + int nVersion; +}; + +// +// Allocator that locks its contents from being paged +// out of memory and clears its contents before deletion. +// +template struct secure_allocator : public std::allocator { + // MSVC8 default copy constructor is broken + typedef std::allocator base; + typedef typename base::size_type size_type; + typedef typename base::difference_type difference_type; + typedef typename base::pointer pointer; + typedef typename base::const_pointer const_pointer; + typedef typename base::reference reference; + typedef typename base::const_reference const_reference; + typedef typename base::value_type value_type; + secure_allocator() throw() {} + secure_allocator(const secure_allocator &a) throw() : base(a) {} + template + secure_allocator(const secure_allocator &a) throw() : base(a) {} + ~secure_allocator() throw() {} + template struct rebind { + typedef secure_allocator<_Other> other; + }; + + T *allocate(std::size_t n, const void *hint = 0) { + T *p; + p = std::allocator::allocate(n, hint); + if (p != NULL) mlock(p, sizeof(T) * n); + return p; + } + + void deallocate(T *p, std::size_t n) { + if (p != NULL) { + memset(p, 0, sizeof(T) * n); + munlock(p, sizeof(T) * n); + } + std::allocator::deallocate(p, n); + } +}; + +// +// Double ended buffer combining vector and stream-like interfaces. +// >> and << read and write unformatted data using the above serialization +// templates. +// Fills with data in linear time; some stringstream implementations take N^2 +// time. +// +class CDataStream { +protected: + typedef std::vector> vector_type; + vector_type vch; + unsigned int nReadPos; + short state; + short exceptmask; + +public: + int nType; + int nVersion; + + typedef vector_type::allocator_type allocator_type; + typedef vector_type::size_type size_type; + typedef vector_type::difference_type difference_type; + typedef vector_type::reference reference; + typedef vector_type::const_reference const_reference; + typedef vector_type::value_type value_type; + typedef vector_type::iterator iterator; + typedef vector_type::const_iterator const_iterator; + typedef vector_type::reverse_iterator reverse_iterator; + + explicit CDataStream(int nTypeIn = SER_NETWORK, + int nVersionIn = PROTOCOL_VERSION) { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const_iterator pbegin, const_iterator pend, + int nTypeIn = SER_NETWORK, int nVersionIn = PROTOCOL_VERSION) + : vch(pbegin, pend) { + Init(nTypeIn, nVersionIn); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + CDataStream(const char *pbegin, const char *pend, int nTypeIn = SER_NETWORK, + int nVersionIn = PROTOCOL_VERSION) + : vch(pbegin, pend) { + Init(nTypeIn, nVersionIn); + } +#endif + + CDataStream(const vector_type &vchIn, int nTypeIn = SER_NETWORK, + int nVersionIn = PROTOCOL_VERSION) + : vch(vchIn.begin(), vchIn.end()) { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector &vchIn, int nTypeIn = SER_NETWORK, + int nVersionIn = PROTOCOL_VERSION) + : vch(vchIn.begin(), vchIn.end()) { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector &vchIn, + int nTypeIn = SER_NETWORK, int nVersionIn = PROTOCOL_VERSION) + : vch((char *)&vchIn.begin()[0], (char *)&vchIn.end()[0]) { + Init(nTypeIn, nVersionIn); + } + + void Init(int nTypeIn = SER_NETWORK, int nVersionIn = PROTOCOL_VERSION) { + nReadPos = 0; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + CDataStream &operator+=(const CDataStream &b) { + vch.insert(vch.end(), b.begin(), b.end()); + return *this; + } + + friend CDataStream operator+(const CDataStream &a, const CDataStream &b) { + CDataStream ret = a; + ret += b; + return (ret); + } + + std::string str() const { return (std::string(begin(), end())); } + + // + // Vector subset + // + const_iterator begin() const { return vch.begin() + nReadPos; } + iterator begin() { return vch.begin() + nReadPos; } + const_iterator end() const { return vch.end(); } + iterator end() { return vch.end(); } + size_type size() const { return vch.size() - nReadPos; } + bool empty() const { return vch.size() == nReadPos; } + void resize(size_type n, value_type c = 0) { vch.resize(n + nReadPos, c); } + void reserve(size_type n) { vch.reserve(n + nReadPos); } + const_reference operator[](size_type pos) const { + return vch[pos + nReadPos]; + } + reference operator[](size_type pos) { return vch[pos + nReadPos]; } + void clear() { + vch.clear(); + nReadPos = 0; + } + iterator insert(iterator it, const char &x = char()) { + return vch.insert(it, x); + } + void insert(iterator it, size_type n, const char &x) { + vch.insert(it, n, x); + } + + void insert(iterator it, std::vector::const_iterator first, + std::vector::const_iterator last) { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } else + vch.insert(it, first, last); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + void insert(iterator it, const char *first, const char *last) { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } else + vch.insert(it, first, last); + } +#endif + + iterator erase(iterator it) { + if (it == vch.begin() + nReadPos) { + // special case for erasing from the front + if (++nReadPos >= vch.size()) { + // whenever we reach the end, we take the opportunity to clear + // the buffer + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + return vch.begin() + nReadPos; + } else + return vch.erase(it); + } + + iterator erase(iterator first, iterator last) { + if (first == vch.begin() + nReadPos) { + // special case for erasing from the front + if (last == vch.end()) { + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } else { + nReadPos = (last - vch.begin()); + return last; + } + } else + return vch.erase(first, last); + } + + inline void Compact() { + vch.erase(vch.begin(), vch.begin() + nReadPos); + nReadPos = 0; + } + + bool Rewind(size_type n) { + // Rewind by n characters if the buffer hasn't been compacted yet + if (n > nReadPos) return false; + nReadPos -= n; + return true; + } + + // + // Stream subset + // + void setstate(short bits, const char *psz) { + state |= bits; + if (state & exceptmask) throw std::ios_base::failure(psz); + } + + bool eof() const { return size() == 0; } + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return !eof() && (state == 0); } + void clear(short n) { state = n; } // name conflict with vector clear() + short exceptions() { return exceptmask; } + short exceptions(short mask) { + short prev = exceptmask; + exceptmask = mask; + setstate(0, "CDataStream"); + return prev; + } + CDataStream *rdbuf() { return this; } + int in_avail() { return size(); } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CDataStream &read(char *pch, int nSize) { + // Read from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) { + if (nReadPosNext > vch.size()) { + setstate(std::ios::failbit, + "CDataStream::read() : end of data"); + memset(pch, 0, nSize); + nSize = vch.size() - nReadPos; + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = 0; + vch.clear(); + return (*this); + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream &ignore(int nSize) { + // Ignore from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) { + if (nReadPosNext > vch.size()) { + setstate(std::ios::failbit, + "CDataStream::ignore() : end of data"); + nSize = vch.size() - nReadPos; + } + nReadPos = 0; + vch.clear(); + return (*this); + } + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream &write(const char *pch, int nSize) { + // Write to the end of the buffer + assert(nSize >= 0); + vch.insert(vch.end(), pch, pch + nSize); + return (*this); + } + + template + void Serialize(Stream &s, int nType = 0, + int nVersion = PROTOCOL_VERSION) const { + // Special case: stream << stream concatenates like stream += stream + if (!vch.empty()) s.write((char *)&vch[0], vch.size() * sizeof(vch[0])); + } + + template unsigned int GetSerializeSize(const T &obj) { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template CDataStream &operator<<(const T &obj) { + // Serialize to this stream + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template CDataStream &operator>>(T &obj) { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#ifdef TESTCDATASTREAM +// VC6sp6 +// CDataStream: +// n=1000 0 seconds +// n=2000 0 seconds +// n=4000 0 seconds +// n=8000 0 seconds +// n=16000 0 seconds +// n=32000 0 seconds +// n=64000 1 seconds +// n=128000 1 seconds +// n=256000 2 seconds +// n=512000 4 seconds +// n=1024000 8 seconds +// n=2048000 16 seconds +// n=4096000 32 seconds +// stringstream: +// n=1000 1 seconds +// n=2000 1 seconds +// n=4000 13 seconds +// n=8000 87 seconds +// n=16000 400 seconds +// n=32000 1660 seconds +// n=64000 6749 seconds +// n=128000 27241 seconds +// n=256000 109804 seconds +#include +int main(int argc, char *argv[]) { + vector vch(0xcc, 250); + printf("CDataStream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) { + CDataStream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char *)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } + printf("stringstream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) { + stringstream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char *)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } +} +#endif + +// +// Automatic closing wrapper for FILE* +// - Will automatically close the file when it goes out of scope if not null. +// - If you're returning the file pointer, return file.release(). +// - If you need to close the file early, use file.fclose() instead of +// fclose(file). +// +class CAutoFile { +protected: + FILE *file; + short state; + short exceptmask; + +public: + int nType; + int nVersion; + + typedef FILE element_type; + + CAutoFile(FILE *filenew = NULL, int nTypeIn = SER_DISK, + int nVersionIn = PROTOCOL_VERSION) { + file = filenew; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + ~CAutoFile() { fclose(); } + + void fclose() { + if (file != NULL && file != stdin && file != stdout && file != stderr) + ::fclose(file); + file = NULL; + } + + FILE *release() { + FILE *ret = file; + file = NULL; + return ret; + } + operator FILE *() { return file; } + FILE *operator->() { return file; } + FILE &operator*() { return *file; } + FILE **operator&() { return &file; } + FILE *operator=(FILE *pnew) { return file = pnew; } + bool operator!() { return (file == NULL); } + + // + // Stream subset + // + void setstate(short bits, const char *psz) { + state |= bits; + if (state & exceptmask) throw std::ios_base::failure(psz); + } + + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return state == 0; } + void clear(short n = 0) { state = n; } + short exceptions() { return exceptmask; } + short exceptions(short mask) { + short prev = exceptmask; + exceptmask = mask; + setstate(0, "CAutoFile"); + return prev; + } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CAutoFile &read(char *pch, int nSize) { + if (!file) + throw std::ios_base::failure( + "CAutoFile::read : file handle is NULL"); + if (fread(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, feof(file) + ? "CAutoFile::read : end of file" + : "CAutoFile::read : fread failed"); + return (*this); + } + + CAutoFile &write(const char *pch, int nSize) { + if (!file) + throw std::ios_base::failure( + "CAutoFile::write : file handle is NULL"); + if (fwrite(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, "CAutoFile::write : write failed"); + return (*this); + } + + template unsigned int GetSerializeSize(const T &obj) { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template CAutoFile &operator<<(const T &obj) { + // Serialize to this stream + if (!file) + throw std::ios_base::failure( + "CAutoFile::operator<< : file handle is NULL"); + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template CAutoFile &operator>>(T &obj) { + // Unserialize from this stream + if (!file) + throw std::ios_base::failure( + "CAutoFile::operator>> : file handle is NULL"); + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#endif diff --git a/src/seeder/strlcpy.h b/src/seeder/strlcpy.h new file mode 100644 --- /dev/null +++ b/src/seeder/strlcpy.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BITCOIN_STRLCPY_H +#define BITCOIN_STRLCPY_H + +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +inline size_t strlcpy(char *dst, const char *src, size_t siz) { + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return (s - src - 1); /* count does not include NUL */ +} + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +inline size_t strlcat(char *dst, const char *src, size_t siz) { + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) return (dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return (dlen + (s - src)); /* count does not include NUL */ +} +#endif diff --git a/src/seeder/uint256.h b/src/seeder/uint256.h new file mode 100644 --- /dev/null +++ b/src/seeder/uint256.h @@ -0,0 +1,832 @@ +// 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 BITCOIN_UINT256_H +#define BITCOIN_UINT256_H + +#include "serialize.h" + +#include +#include +#include + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false); else for +#endif + +inline int Testuint256AdHoc(std::vector vArg); + +// We have to keep a separate base class without constructors +// so the compiler will let us use it in a union +template class base_uint { +protected: + enum { WIDTH = BITS / 32 }; + unsigned int pn[WIDTH]; + +public: + bool operator!() const { + for (int i = 0; i < WIDTH; i++) + if (pn[i] != 0) return false; + return true; + } + + const base_uint operator~() const { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + return ret; + } + + const base_uint operator-() const { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + ret++; + return ret; + } + + base_uint &operator=(uint64 b) { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + base_uint &operator^=(const base_uint &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] ^= b.pn[i]; + return *this; + } + + base_uint &operator&=(const base_uint &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] &= b.pn[i]; + return *this; + } + + base_uint &operator|=(const base_uint &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] |= b.pn[i]; + return *this; + } + + base_uint &operator^=(uint64 b) { + pn[0] ^= (unsigned int)b; + pn[1] ^= (unsigned int)(b >> 32); + return *this; + } + + base_uint &operator&=(uint64 b) { + pn[0] &= (unsigned int)b; + pn[1] &= (unsigned int)(b >> 32); + return *this; + } + + base_uint &operator|=(uint64 b) { + pn[0] |= (unsigned int)b; + pn[1] |= (unsigned int)(b >> 32); + return *this; + } + + base_uint &operator<<=(unsigned int shift) { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) { + if (i + k + 1 < WIDTH && shift != 0) + pn[i + k + 1] |= (a.pn[i] >> (32 - shift)); + if (i + k < WIDTH) pn[i + k] |= (a.pn[i] << shift); + } + return *this; + } + + base_uint &operator>>=(unsigned int shift) { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) { + if (i - k - 1 >= 0 && shift != 0) + pn[i - k - 1] |= (a.pn[i] << (32 - shift)); + if (i - k >= 0) pn[i - k] |= (a.pn[i] >> shift); + } + return *this; + } + + base_uint &operator+=(const base_uint &b) { + uint64 carry = 0; + for (int i = 0; i < WIDTH; i++) { + uint64 n = carry + pn[i] + b.pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; + } + + base_uint &operator-=(const base_uint &b) { + *this += -b; + return *this; + } + + base_uint &operator+=(uint64 b64) { + base_uint b; + b = b64; + *this += b; + return *this; + } + + base_uint &operator-=(uint64 b64) { + base_uint b; + b = b64; + *this += -b; + return *this; + } + + base_uint &operator++() { + // prefix operator + int i = 0; + while (++pn[i] == 0 && i < WIDTH - 1) + i++; + return *this; + } + + const base_uint operator++(int) { + // postfix operator + const base_uint ret = *this; + ++(*this); + return ret; + } + + base_uint &operator--() { + // prefix operator + int i = 0; + while (--pn[i] == -1 && i < WIDTH - 1) + i++; + return *this; + } + + const base_uint operator--(int) { + // postfix operator + const base_uint ret = *this; + --(*this); + return ret; + } + + friend inline bool operator<(const base_uint &a, const base_uint &b) { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator<=(const base_uint &a, const base_uint &b) { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator>(const base_uint &a, const base_uint &b) { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator>=(const base_uint &a, const base_uint &b) { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator==(const base_uint &a, const base_uint &b) { + for (int i = 0; i < base_uint::WIDTH; i++) + if (a.pn[i] != b.pn[i]) return false; + return true; + } + + friend inline bool operator==(const base_uint &a, uint64 b) { + if (a.pn[0] != (unsigned int)b) return false; + if (a.pn[1] != (unsigned int)(b >> 32)) return false; + for (int i = 2; i < base_uint::WIDTH; i++) + if (a.pn[i] != 0) return false; + return true; + } + + friend inline bool operator!=(const base_uint &a, const base_uint &b) { + return (!(a == b)); + } + + friend inline bool operator!=(const base_uint &a, uint64 b) { + return (!(a == b)); + } + + std::string GetHex() const { + char psz[sizeof(pn) * 2 + 1]; + for (int i = 0; i < sizeof(pn); i++) + sprintf(psz + i * 2, "%02x", + ((unsigned char *)pn)[sizeof(pn) - i - 1]); + return std::string(psz, psz + sizeof(pn) * 2); + } + + void SetHex(const char *psz) { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') psz += 2; + + // hex string to uint + static char phexdigit[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + const char *pbegin = psz; + while (phexdigit[(unsigned char)*psz] || *psz == '0') + psz++; + psz--; + unsigned char *p1 = (unsigned char *)pn; + unsigned char *pend = p1 + WIDTH * 4; + while (psz >= pbegin && p1 < pend) { + *p1 = phexdigit[(unsigned char)*psz--]; + if (psz >= pbegin) { + *p1 |= (phexdigit[(unsigned char)*psz--] << 4); + p1++; + } + } + } + + void SetHex(const std::string &str) { SetHex(str.c_str()); } + + std::string ToString() const { return (GetHex()); } + + unsigned char *begin() { return (unsigned char *)&pn[0]; } + + unsigned char *end() { return (unsigned char *)&pn[WIDTH]; } + + unsigned int size() { return sizeof(pn); } + + unsigned int GetSerializeSize(int nType = 0, + int nVersion = PROTOCOL_VERSION) const { + return sizeof(pn); + } + + template + void Serialize(Stream &s, int nType = 0, + int nVersion = PROTOCOL_VERSION) const { + s.write((char *)pn, sizeof(pn)); + } + + template + void Unserialize(Stream &s, int nType = 0, + int nVersion = PROTOCOL_VERSION) { + s.read((char *)pn, sizeof(pn)); + } + + friend class uint160; + friend class uint256; + friend inline int Testuint256AdHoc(std::vector vArg); +}; + +typedef base_uint<160> base_uint160; +typedef base_uint<256> base_uint256; + +// +// uint160 and uint256 could be implemented as templates, but to keep +// compile errors and debugging cleaner, they're copy and pasted. +// + +////////////////////////////////////////////////////////////////////////////// +// +// uint160 +// + +class uint160 : public base_uint160 { +public: + typedef base_uint160 basetype; + + uint160() { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint160(const basetype &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint160 &operator=(const basetype &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint160(uint64 b) { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint160 &operator=(uint64 b) { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint160(const std::string &str) { SetHex(str); } + + explicit uint160(const std::vector &vch) { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint160 &a, uint64 b) { + return (base_uint160)a == b; +} +inline bool operator!=(const uint160 &a, uint64 b) { + return (base_uint160)a != b; +} +inline const uint160 operator<<(const base_uint160 &a, unsigned int shift) { + return uint160(a) <<= shift; +} +inline const uint160 operator>>(const base_uint160 &a, unsigned int shift) { + return uint160(a) >>= shift; +} +inline const uint160 operator<<(const uint160 &a, unsigned int shift) { + return uint160(a) <<= shift; +} +inline const uint160 operator>>(const uint160 &a, unsigned int shift) { + return uint160(a) >>= shift; +} + +inline const uint160 operator^(const base_uint160 &a, const base_uint160 &b) { + return uint160(a) ^= b; +} +inline const uint160 operator&(const base_uint160 &a, const base_uint160 &b) { + return uint160(a) &= b; +} +inline const uint160 operator|(const base_uint160 &a, const base_uint160 &b) { + return uint160(a) |= b; +} +inline const uint160 operator+(const base_uint160 &a, const base_uint160 &b) { + return uint160(a) += b; +} +inline const uint160 operator-(const base_uint160 &a, const base_uint160 &b) { + return uint160(a) -= b; +} + +inline bool operator<(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a < (base_uint160)b; +} +inline bool operator<=(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a <= (base_uint160)b; +} +inline bool operator>(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a > (base_uint160)b; +} +inline bool operator>=(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a >= (base_uint160)b; +} +inline bool operator==(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a == (base_uint160)b; +} +inline bool operator!=(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a != (base_uint160)b; +} +inline const uint160 operator^(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a ^ (base_uint160)b; +} +inline const uint160 operator&(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a & (base_uint160)b; +} +inline const uint160 operator|(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a | (base_uint160)b; +} +inline const uint160 operator+(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a + (base_uint160)b; +} +inline const uint160 operator-(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a - (base_uint160)b; +} + +inline bool operator<(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a < (base_uint160)b; +} +inline bool operator<=(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a <= (base_uint160)b; +} +inline bool operator>(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a > (base_uint160)b; +} +inline bool operator>=(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a >= (base_uint160)b; +} +inline bool operator==(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a == (base_uint160)b; +} +inline bool operator!=(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a != (base_uint160)b; +} +inline const uint160 operator^(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a ^ (base_uint160)b; +} +inline const uint160 operator&(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a & (base_uint160)b; +} +inline const uint160 operator|(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a | (base_uint160)b; +} +inline const uint160 operator+(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a + (base_uint160)b; +} +inline const uint160 operator-(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a - (base_uint160)b; +} + +inline bool operator<(const uint160 &a, const uint160 &b) { + return (base_uint160)a < (base_uint160)b; +} +inline bool operator<=(const uint160 &a, const uint160 &b) { + return (base_uint160)a <= (base_uint160)b; +} +inline bool operator>(const uint160 &a, const uint160 &b) { + return (base_uint160)a > (base_uint160)b; +} +inline bool operator>=(const uint160 &a, const uint160 &b) { + return (base_uint160)a >= (base_uint160)b; +} +inline bool operator==(const uint160 &a, const uint160 &b) { + return (base_uint160)a == (base_uint160)b; +} +inline bool operator!=(const uint160 &a, const uint160 &b) { + return (base_uint160)a != (base_uint160)b; +} +inline const uint160 operator^(const uint160 &a, const uint160 &b) { + return (base_uint160)a ^ (base_uint160)b; +} +inline const uint160 operator&(const uint160 &a, const uint160 &b) { + return (base_uint160)a & (base_uint160)b; +} +inline const uint160 operator|(const uint160 &a, const uint160 &b) { + return (base_uint160)a | (base_uint160)b; +} +inline const uint160 operator+(const uint160 &a, const uint160 &b) { + return (base_uint160)a + (base_uint160)b; +} +inline const uint160 operator-(const uint160 &a, const uint160 &b) { + return (base_uint160)a - (base_uint160)b; +} + +////////////////////////////////////////////////////////////////////////////// +// +// uint256 +// + +class uint256 : public base_uint256 { +public: + typedef base_uint256 basetype; + + uint256() { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint256(const basetype &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint256 &operator=(const basetype &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint256(uint64 b) { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint256 &operator=(uint64 b) { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint256(const std::string &str) { SetHex(str); } + + explicit uint256(const std::vector &vch) { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint256 &a, uint64 b) { + return (base_uint256)a == b; +} +inline bool operator!=(const uint256 &a, uint64 b) { + return (base_uint256)a != b; +} +inline const uint256 operator<<(const base_uint256 &a, unsigned int shift) { + return uint256(a) <<= shift; +} +inline const uint256 operator>>(const base_uint256 &a, unsigned int shift) { + return uint256(a) >>= shift; +} +inline const uint256 operator<<(const uint256 &a, unsigned int shift) { + return uint256(a) <<= shift; +} +inline const uint256 operator>>(const uint256 &a, unsigned int shift) { + return uint256(a) >>= shift; +} + +inline const uint256 operator^(const base_uint256 &a, const base_uint256 &b) { + return uint256(a) ^= b; +} +inline const uint256 operator&(const base_uint256 &a, const base_uint256 &b) { + return uint256(a) &= b; +} +inline const uint256 operator|(const base_uint256 &a, const base_uint256 &b) { + return uint256(a) |= b; +} +inline const uint256 operator+(const base_uint256 &a, const base_uint256 &b) { + return uint256(a) += b; +} +inline const uint256 operator-(const base_uint256 &a, const base_uint256 &b) { + return uint256(a) -= b; +} + +inline bool operator<(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a < (base_uint256)b; +} +inline bool operator<=(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a <= (base_uint256)b; +} +inline bool operator>(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a > (base_uint256)b; +} +inline bool operator>=(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a >= (base_uint256)b; +} +inline bool operator==(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a == (base_uint256)b; +} +inline bool operator!=(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a != (base_uint256)b; +} +inline const uint256 operator^(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a ^ (base_uint256)b; +} +inline const uint256 operator&(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a & (base_uint256)b; +} +inline const uint256 operator|(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a | (base_uint256)b; +} +inline const uint256 operator+(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a + (base_uint256)b; +} +inline const uint256 operator-(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a - (base_uint256)b; +} + +inline bool operator<(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a < (base_uint256)b; +} +inline bool operator<=(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a <= (base_uint256)b; +} +inline bool operator>(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a > (base_uint256)b; +} +inline bool operator>=(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a >= (base_uint256)b; +} +inline bool operator==(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a == (base_uint256)b; +} +inline bool operator!=(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a != (base_uint256)b; +} +inline const uint256 operator^(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a ^ (base_uint256)b; +} +inline const uint256 operator&(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a & (base_uint256)b; +} +inline const uint256 operator|(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a | (base_uint256)b; +} +inline const uint256 operator+(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a + (base_uint256)b; +} +inline const uint256 operator-(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a - (base_uint256)b; +} + +inline bool operator<(const uint256 &a, const uint256 &b) { + return (base_uint256)a < (base_uint256)b; +} +inline bool operator<=(const uint256 &a, const uint256 &b) { + return (base_uint256)a <= (base_uint256)b; +} +inline bool operator>(const uint256 &a, const uint256 &b) { + return (base_uint256)a > (base_uint256)b; +} +inline bool operator>=(const uint256 &a, const uint256 &b) { + return (base_uint256)a >= (base_uint256)b; +} +inline bool operator==(const uint256 &a, const uint256 &b) { + return (base_uint256)a == (base_uint256)b; +} +inline bool operator!=(const uint256 &a, const uint256 &b) { + return (base_uint256)a != (base_uint256)b; +} +inline const uint256 operator^(const uint256 &a, const uint256 &b) { + return (base_uint256)a ^ (base_uint256)b; +} +inline const uint256 operator&(const uint256 &a, const uint256 &b) { + return (base_uint256)a & (base_uint256)b; +} +inline const uint256 operator|(const uint256 &a, const uint256 &b) { + return (base_uint256)a | (base_uint256)b; +} +inline const uint256 operator+(const uint256 &a, const uint256 &b) { + return (base_uint256)a + (base_uint256)b; +} +inline const uint256 operator-(const uint256 &a, const uint256 &b) { + return (base_uint256)a - (base_uint256)b; +} + +inline int Testuint256AdHoc(std::vector vArg) { + uint256 g(0); + + printf("%s\n", g.ToString().c_str()); + g--; + printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g--; + printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g++; + printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; + printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; + printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; + printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + + uint256 a(7); + printf("a=7\n"); + printf("%s\n", a.ToString().c_str()); + + uint256 b; + printf("b undefined\n"); + printf("%s\n", b.ToString().c_str()); + int c = 3; + + a = c; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + uint256 k(c); + + a = 5; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + b = 1; + b <<= 52; + + a |= b; + + a ^= 0x500; + + printf("a %s\n", a.ToString().c_str()); + + a = a | b | (uint256)0x1000; + + printf("a %s\n", a.ToString().c_str()); + printf("b %s\n", b.ToString().c_str()); + + a = 0xfffffffe; + a.pn[4] = 9; + + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + uint256 d = a--; + printf("%s\n", d.ToString().c_str()); + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + + d = a; + + printf("%s\n", d.ToString().c_str()); + for (int i = uint256::WIDTH - 1; i >= 0; i--) + printf("%08x", d.pn[i]); + printf("\n"); + + uint256 neg = d; + neg = ~neg; + printf("%s\n", neg.ToString().c_str()); + + uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + printf("\n"); + printf("%s\n", e.ToString().c_str()); + + printf("\n"); + uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + uint256 x2; + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) { + x2 = x1 << i; + printf("%s\n", x2.ToString().c_str()); + } + + printf("\n"); + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) { + x2 = x1; + x2 >>= i; + printf("%s\n", x2.ToString().c_str()); + } + + for (int i = 0; i < 100; i++) { + uint256 k = (~uint256(0) >> i); + printf("%s\n", k.ToString().c_str()); + } + + for (int i = 0; i < 100; i++) { + uint256 k = (~uint256(0) << i); + printf("%s\n", k.ToString().c_str()); + } + + return (0); +} + +#endif diff --git a/src/seeder/util.h b/src/seeder/util.h new file mode 100644 --- /dev/null +++ b/src/seeder/util.h @@ -0,0 +1,108 @@ +#ifndef _UTIL_H_ +#define _UTIL_H_ 1 + +#include +#include +#include +#include + +#include "uint256.h" + +#define loop for (;;) +#define BEGIN(a) ((char *)&(a)) +#define END(a) ((char *)&((&(a))[1])) +#define UBEGIN(a) ((unsigned char *)&(a)) +#define UEND(a) ((unsigned char *)&((&(a))[1])) +#define ARRAYLEN(array) (sizeof(array) / sizeof((array)[0])) + +#define WSAGetLastError() errno +#define WSAEINVAL EINVAL +#define WSAEALREADY EALREADY +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSAEINTR EINTR +#define WSAEINPROGRESS EINPROGRESS +#define WSAEADDRINUSE EADDRINUSE +#define WSAENOTSOCK EBADF +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR -1 + +// Wrapper to automatically initialize mutex +class CCriticalSection { +protected: + pthread_rwlock_t mutex; + +public: + explicit CCriticalSection() { pthread_rwlock_init(&mutex, NULL); } + ~CCriticalSection() { pthread_rwlock_destroy(&mutex); } + void Enter(bool fShared = false) { + if (fShared) { + pthread_rwlock_rdlock(&mutex); + } else { + pthread_rwlock_wrlock(&mutex); + } + } + void Leave() { pthread_rwlock_unlock(&mutex); } +}; + +// Automatically leave critical section when leaving block, needed for exception +// safety +class CCriticalBlock { +protected: + CCriticalSection *pcs; + +public: + CCriticalBlock(CCriticalSection &cs, bool fShared = false) : pcs(&cs) { + pcs->Enter(fShared); + } + operator bool() const { return true; } + ~CCriticalBlock() { pcs->Leave(); } +}; + +#define CRITICAL_BLOCK(cs) \ + if (CCriticalBlock criticalblock = CCriticalBlock(cs)) + +#define SHARED_CRITICAL_BLOCK(cs) \ + if (CCriticalBlock criticalblock = CCriticalBlock(cs, true)) + +template inline uint256 Hash(const T1 pbegin, const T1 pend) { + static unsigned char pblank[1]; + uint256 hash1; + SHA256((pbegin == pend ? pblank : (unsigned char *)&pbegin[0]), + (pend - pbegin) * sizeof(pbegin[0]), (unsigned char *)&hash1); + uint256 hash2; + SHA256((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); + return hash2; +} + +void static inline Sleep(int nMilliSec) { + struct timespec wa; + wa.tv_sec = nMilliSec / 1000; + wa.tv_nsec = (nMilliSec % 1000) * 1000000; + nanosleep(&wa, NULL); +} + +std::string vstrprintf(const std::string &format, va_list ap); + +std::string static inline strprintf(const std::string &format, ...) { + va_list arg_ptr; + va_start(arg_ptr, format); + std::string ret = vstrprintf(format, arg_ptr); + va_end(arg_ptr); + return ret; +} + +bool static inline error(std::string err, ...) { + return false; +} + +bool static inline my_printf(std::string err, ...) { + return true; +} + +std::vector DecodeBase32(const char *p, bool *pfInvalid = NULL); +std::string DecodeBase32(const std::string &str); +std::string EncodeBase32(const unsigned char *pch, size_t len); +std::string EncodeBase32(const std::string &str); + +#endif diff --git a/src/seeder/util.cpp b/src/seeder/util.cpp new file mode 100644 --- /dev/null +++ b/src/seeder/util.cpp @@ -0,0 +1,205 @@ +#include "util.h" +#include + +using namespace std; + +string vstrprintf(const std::string &format, va_list ap) { + char buffer[50000]; + char *p = buffer; + int limit = sizeof(buffer); + int ret; + loop { + va_list arg_ptr; + va_copy(arg_ptr, ap); + ret = vsnprintf(p, limit, format.c_str(), arg_ptr); + va_end(arg_ptr); + if (ret >= 0 && ret < limit) break; + if (p != buffer) delete[] p; + limit *= 2; + p = new char[limit]; + if (p == NULL) throw std::bad_alloc(); + } + string str(p, p + ret); + if (p != buffer) delete[] p; + return str; +} + +string EncodeBase32(const unsigned char *pch, size_t len) { + static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; + + string strRet = ""; + strRet.reserve((len + 4) / 5 * 8); + + int mode = 0, left = 0; + const unsigned char *pchEnd = pch + len; + + while (pch < pchEnd) { + int enc = *(pch++); + switch (mode) { + case 0: // we have no bits + strRet += pbase32[enc >> 3]; + left = (enc & 7) << 2; + mode = 1; + break; + + case 1: // we have three bits + strRet += pbase32[left | (enc >> 6)]; + strRet += pbase32[(enc >> 1) & 31]; + left = (enc & 1) << 4; + mode = 2; + break; + + case 2: // we have one bit + strRet += pbase32[left | (enc >> 4)]; + left = (enc & 15) << 1; + mode = 3; + break; + + case 3: // we have four bits + strRet += pbase32[left | (enc >> 7)]; + strRet += pbase32[(enc >> 2) & 31]; + left = (enc & 3) << 3; + mode = 4; + break; + + case 4: // we have two bits + strRet += pbase32[left | (enc >> 5)]; + strRet += pbase32[enc & 31]; + mode = 0; + } + } + + static const int nPadding[5] = {0, 6, 4, 3, 1}; + if (mode) { + strRet += pbase32[left]; + for (int n = 0; n < nPadding[mode]; n++) + strRet += '='; + } + + return strRet; +} + +string EncodeBase32(const string &str) { + return EncodeBase32((const unsigned char *)str.c_str(), str.size()); +} + +vector DecodeBase32(const char *p, bool *pfInvalid) { + static const int decode32_table[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, + 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1}; + + if (pfInvalid) *pfInvalid = false; + + vector vchRet; + vchRet.reserve((strlen(p)) * 5 / 8); + + int mode = 0; + int left = 0; + + while (1) { + int dec = decode32_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) { + case 0: // we have no bits and get 5 + left = dec; + mode = 1; + break; + + case 1: // we have 5 bits and keep 2 + vchRet.push_back((left << 3) | (dec >> 2)); + left = dec & 3; + mode = 2; + break; + + case 2: // we have 2 bits and keep 7 + left = left << 5 | dec; + mode = 3; + break; + + case 3: // we have 7 bits and keep 4 + vchRet.push_back((left << 1) | (dec >> 4)); + left = dec & 15; + mode = 4; + break; + + case 4: // we have 4 bits, and keep 1 + vchRet.push_back((left << 4) | (dec >> 1)); + left = dec & 1; + mode = 5; + break; + + case 5: // we have 1 bit, and keep 6 + left = left << 5 | dec; + mode = 6; + break; + + case 6: // we have 6 bits, and keep 3 + vchRet.push_back((left << 2) | (dec >> 3)); + left = dec & 7; + mode = 7; + break; + + case 7: // we have 3 bits, and keep 0 + vchRet.push_back((left << 5) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) switch (mode) { + case 0: // 8n base32 characters processed: ok + break; + + case 1: // 8n+1 base32 characters processed: impossible + case 3: // +3 + case 6: // +6 + *pfInvalid = true; + break; + + case 2: // 8n+2 base32 characters processed: require '======' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || + p[3] != '=' || p[4] != '=' || p[5] != '=' || + decode32_table[(unsigned char)p[6]] != -1) + *pfInvalid = true; + break; + + case 4: // 8n+4 base32 characters processed: require '====' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || + p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1) + *pfInvalid = true; + break; + + case 5: // 8n+5 base32 characters processed: require '===' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || + decode32_table[(unsigned char)p[3]] != -1) + *pfInvalid = true; + break; + + case 7: // 8n+7 base32 characters processed: require '=' + if (left || p[0] != '=' || + decode32_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase32(const string &str) { + vector vchRet = DecodeBase32(str.c_str()); + return string((const char *)&vchRet[0], vchRet.size()); +}