From 7d700b34ba4213906a7429292a7f3aae1f30d677 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 15 Aug 2013 11:17:14 +0000 Subject: [PATCH] Initial commit Comit of novacoin-seeder sources. Novacoin-seeder is the bitcoin-seeder adaptation. --- COPYING | 20 + Makefile | 13 + README | 45 ++ bitcoin.cpp | 305 ++++++++++++++ bitcoin.h | 8 + combine.pl | 65 +++ compat.h | 63 +++ db.cpp | 216 ++++++++++ db.h | 305 ++++++++++++++ dns.c | 442 ++++++++++++++++++++ dns.h | 28 ++ main.cpp | 458 ++++++++++++++++++++ netbase.cpp | 1165 +++++++++++++++++++++++++++++++++++++++++++++++++++ netbase.h | 151 +++++++ protocol.cpp | 161 +++++++ protocol.h | 125 ++++++ serialize.h | 1312 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ strlcpy.h | 90 ++++ test.pl | 67 +++ uint256.h | 766 ++++++++++++++++++++++++++++++++++ util.cpp | 218 ++++++++++ util.h | 106 +++++ 22 files changed, 6129 insertions(+), 0 deletions(-) create mode 100644 COPYING create mode 100644 Makefile create mode 100644 README create mode 100644 bitcoin.cpp create mode 100644 bitcoin.h create mode 100644 combine.pl create mode 100644 compat.h create mode 100644 db.cpp create mode 100644 db.h create mode 100644 dns.c create mode 100644 dns.h create mode 100644 main.cpp create mode 100644 netbase.cpp create mode 100644 netbase.h create mode 100644 protocol.cpp create mode 100644 protocol.h create mode 100644 serialize.h create mode 100644 strlcpy.h create mode 100644 test.pl create mode 100644 uint256.h create mode 100644 util.cpp create mode 100644 util.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..bd2b8bd --- /dev/null +++ b/COPYING @@ -0,0 +1,20 @@ +Copyright (c) 2011-2012 Pieter Wuille and contributers +Copyright (c) 2009-2012 Bitcoin Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3bbd7f7 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CXXFLAGS = -O3 -g0 -march=native +LDFLAGS = $(CXXFLAGS) + +dnsseed: dns.o bitcoin.o netbase.o protocol.o db.o main.o util.o + g++ -pthread $(LDFLAGS) -o dnsseed dns.o bitcoin.o netbase.o protocol.o db.o main.o util.o -lcrypto + +%.o: %.cpp bitcoin.h netbase.h protocol.h db.h serialize.h uint256.h util.h + g++ -DUSE_IPV6 -pthread $(CXXFLAGS) -Wno-invalid-offsetof -c -o $@ $< + +dns.o: dns.c + gcc -pthread -std=c99 $(CXXFLAGS) dns.c -c -o dns.o + +%.o: %.cpp diff --git a/README b/README new file mode 100644 index 0000000..e6db563 --- /dev/null +++ b/README @@ -0,0 +1,45 @@ +novacoin-seeder +============== + +NovaCoin-seeder is a crawler for the NovaCoin 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 +* 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). + +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: + +./dnsseed -h dnsseed.example.com -n vps.example.com + +If you want the DNS server to report SOA records, please provide an +e-mailadres (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/bitcoin.cpp b/bitcoin.cpp new file mode 100644 index 0000000..3ade33d --- /dev/null +++ b/bitcoin.cpp @@ -0,0 +1,305 @@ +#include + +#include "db.h" +#include "netbase.h" +#include "protocol.h" +#include "serialize.h" +#include "uint256.h" + +#define BITCOIN_SEED_NONCE 0x0539a019ca550825 +#define REQUIRE_HEIGHT 0 +#define MIN_VERSION 40000 + +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 60; + else + return 10; + } + + 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 = REQUIRE_HEIGHT; + string ver = "/novacoin-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); + BeginMessage("getaddr"); + EndMessage(); + doneAfter = time(NULL) + GetTimeout(); + } + + 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") { + 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 (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/bitcoin.h b/bitcoin.h new file mode 100644 index 0000000..609caa3 --- /dev/null +++ b/bitcoin.h @@ -0,0 +1,8 @@ +#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/combine.pl b/combine.pl new file mode 100644 index 0000000..6493724 --- /dev/null +++ b/combine.pl @@ -0,0 +1,65 @@ +#!/usr/bin/perl -w + +use strict; + +sub loadFile { + my ($file) = @_; + my %ret; + my $max = 0; + open FILE,$file; + while () { + my ($addr,$p2h,$p8h,$p1d,$p1w,$p1m) = split(/\s+/,$_); + if ($p1m =~ /\A([1-9.]+)%\Z/) { + my $x = $1*0.01; + $max=$x if ($x > $max); + $ret{$addr} = $x; + } + } + for my $k (keys %ret) { + $ret{$k} /= $max; + } + close FILE; + return \%ret; +} + +sub merge { + my ($a,$b) = @_; + return 1-(1-$a)*(1-$b); +} + +sub combine { + my ($f1,$f2) = @_; + my %ret; + for my $k1 (keys %{$f1}) { + if (defined $f2->{$k1}) { + $ret{$k1} = merge($f1->{$k1}, $f2->{$k1}); + } else { + $ret{$k1} = merge($f1->{$k1}, 0); + } + } + for my $k2 (keys %{$f2}) { + if (!defined $f1->{$k2}) { + $ret{$k2} = merge(0, $f2->{$k2}); + } + } + return \%ret; +} + +my $res; +my $n=0; +for my $file (@ARGV) { + my $r = loadFile($file); + if ($res) { + $res = combine($res,$r); + } else { + $res = $r; + } + $n++; +} + +for my $addr (sort { $res->{$b} <=> $res->{$a} } (keys %{$res})) { + if ($addr =~ /\A(\d+)\.(\d+)\.(\d+)\.(\d+):8333/) { + my $a = $1*0x1000000 + $2*0x10000 + $3*0x100 + $4; + printf "0x%08x %s %g%%\n",$a,$addr,(1-((1-$res->{$addr}) ** (1/$n)))*100; + } +} diff --git a/compat.h b/compat.h new file mode 100644 index 0000000..79ebb93 --- /dev/null +++ b/compat.h @@ -0,0 +1,63 @@ +// 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 +#endif + +typedef u_int SOCKET; +#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/db.cpp b/db.cpp new file mode 100644 index 0000000..98b467d --- /dev/null +++ b/db.cpp @@ -0,0 +1,216 @@ +#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++; + 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_(CService &ip, int &wait) { + int64 now = time(NULL); + int cont = 0; + int tot = unkId.size(); + do { + deque::iterator it = ourId.begin(); + while (it < ourId.end()) { + if (now - idToInfo[*it].ourLastTry > MIN_RETRY) { + tot++; + it++; + } else { + break; + } + } + if (tot == 0) { + if (ourId.size() > 0) { + wait = MIN_RETRY - (now - idToInfo[ourId.front()].ourLastTry); + } else { + wait = 5; + } + return false; + } + int rnd = rand() % tot; + int ret; + if (rnd < unkId.size()) { + if (rnd*10 < unkId.size()) { + // once every 10 attempts, restart with the oldest unknown IP + set::iterator it = unkId.begin(); + ret = *it; + } else { + // 90% of the time try the last learned IP + set::reverse_iterator it = unkId.rbegin(); + ret = *it; + } + unkId.erase(ret); + } 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 = idToInfo[ret].ip; + 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, 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) { + ips.insert(idToInfo[id].ip); + } + return; + } + if (max > goodId.size() / 2) + max = goodId.size() / 2; + if (max < 1) + max = 1; + int low = *goodId.begin(); + int high = *goodId.rbegin(); + set ids; + while (ids.size() < max) { + int range = high-low+1; + int pos = low + (rand() % range); + int id = *(goodId.lower_bound(pos)); + ids.insert(id); + } + 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/db.h b/db.h new file mode 100644 index 0000000..3046778 --- /dev/null +++ b/db.h @@ -0,0 +1,305 @@ +#include +#include + +#include +#include +#include +#include + +#include "netbase.h" +#include "protocol.h" +#include "util.h" + +#define MIN_RETRY 1000 + +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; +}; + + +class CAddrInfo { +private: + CService ip; + uint64_t services; + int64 lastTry; + int64 ourLastTry; + 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), 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; + return ret; + } + + bool IsGood() const { + if (ip.GetPort() != ::nP2Port) return false; + if (!(services & NODE_NETWORK)) return false; + if (!ip.IsRoutable()) return false; + if (clientVersion && clientVersion < 32400) 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; } + return 0; + } + int GetIgnoreTime() const { + if (IsGood()) return 0; + if (stat1M.reliability - stat1M.weight + 1.0 < 0.08 && stat1D.count > 48) { return 8*3600; } + if (stat1W.reliability - stat1W.weight + 1.0 < 0.12 && stat8H.count > 24) { return 4*3600; } + if (stat1D.reliability - stat1D.weight + 1.0 < 0.16 && stat1D.count > 12) { return 2*3600; } + if (stat8H.reliability - stat8H.weight + 1.0 < 0.20 && stat8H.count > 6) { return 1*3600; } + return 0; + } + + void Update(bool good); + + friend class CAddrDb; + + IMPLEMENT_SERIALIZE ( + unsigned char version = 3; + 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); + } + ) +}; + +class CAddrDbStats { +public: + int nBanned; + int nAvail; + int nTracked; + int nNew; + int nGood; + int nAge; +}; + +// 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_(CService &ip, int& wait); // get an IP to test (must call Good_, Bad_, or Skipped_ on result afterwards) + 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, 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; inId++; + 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& ips, int max, const bool *nets) { + SHARED_CRITICAL_BLOCK(cs) + GetIPs_(ips, max, nets); + } +}; diff --git a/dns.c b/dns.c new file mode 100644 index 0000000..218f1e5 --- /dev/null +++ b/dns.c @@ -0,0 +1,442 @@ +#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 in_addr))) +# define dstaddr(x) (CMSG_DATA(x)) +#elif defined IP_PKTINFO +struct in_pktinfo { + unsigned int ipi_ifindex; /* Interface index */ + struct in_addr ipi_spec_dst; /* Local address */ + struct in_addr ipi_addr; /* Header Destination address */ +}; + +# define DSTADDR_SOCKOPT IP_PKTINFO +# define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in_pktinfo))) +# define dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_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) return -1; + const unsigned char *newbuf = inbuf + ref; + return parse_name(&newbuf, *inpos, 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; } + // check Z + if (((inbuf[3] & 112) >> 4) != 0) { /* printf("Z nonzero?\n"); */ error = 1; 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 (strcmp(name, opt->host) && (namelhost))) { 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 size of authority section + + int auth_size = 0; + + if (!((typ == TYPE_NS || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY))) { + // authority section will be necessary + unsigned char *oldpos = outpos; + write_record_ns(&oldpos, outend, "", offset, CLASS_IN, 0, opt->ns); + auth_size = oldpos - outpos; +// printf("Authority section will claim %i bytes\n", 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 - 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 - 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, 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 - auth_size, "", offset, CLASS_IN, opt->datattl, &addr[n]); + else if (addr[n].v == 6) + ret = write_record_aaaa(&outpos, outend - 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) { + 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]++; + } + } + + // 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_in si_other; + int senderSocket = -1; + senderSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (senderSocket == -1) + return -3; + + int replySocket; + if (listenSocket == -1) { + struct sockaddr_in si_me; + if ((listenSocket=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) { + listenSocket = -1; + return -1; + } + replySocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (replySocket == -1) + { + close(listenSocket); + return -1; + } + int sockopt = 1; + setsockopt(listenSocket, IPPROTO_IP, DSTADDR_SOCKOPT, &sockopt, sizeof sockopt); + memset((char *) &si_me, 0, sizeof(si_me)); + si_me.sin_family = AF_INET; + si_me.sin_port = htons(opt->port); + si_me.sin_addr.s_addr = INADDR_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; + sendmsg(listenSocket, &msg, 0); + msg.msg_iov[0].iov_base = inbuf; + handled = true; + } + } + if (!handled) + sendto(listenSocket, outbuf, ret, 0, (struct sockaddr*)&si_other, sizeof(si_other)); + } + return 0; +} diff --git a/dns.h b/dns.h new file mode 100644 index 0000000..97dd321 --- /dev/null +++ b/dns.h @@ -0,0 +1,28 @@ +#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, 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/main.cpp b/main.cpp new file mode 100644 index 0000000..5a6cb9f --- /dev/null +++ b/main.cpp @@ -0,0 +1,458 @@ +#include + +#include +#include +#include +#include +#include + +#include "bitcoin.h" +#include "db.h" + +#define NTHREADS 24 + +using namespace std; + +class CDnsSeedOpts { +public: + int nThreads; + int nPort; + int nP2Port; + int nDnsThreads; + int fWipeBan; + int fWipeIgnore; + const char *mbox; + const char *ns; + const char *host; + const char *tor; + const char *magic; + vector vSeeds; + + CDnsSeedOpts() : nThreads(24), nDnsThreads(24), nPort(53), fWipeBan(0), fWipeIgnore(0), mbox(NULL), ns(NULL), host(NULL), tor(NULL), magic(NULL), vSeeds() + { nP2Port = GetDefaultPort(); } + + void ParseCommandLine(int argc, char **argv) { + static const char *help = "NovaCoin-seeder\n" + "Usage: %s -h -n [-m ] [-t ] [-p ]\n" + "\n" + "Options:\n" + "-s Seed node to collect peers from (replaces default)\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 24)\n" + "-d Number of DNS server threads (default 24)\n" + "-p UDP port to listen on (default 53)\n" + "-o Tor proxy IP/Port\n" + "--p2port P2P port to connect to\n" + "--magic Magic string/network prefix\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[] = { + {"seed", required_argument, 0, 's'}, + {"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'}, + {"p2port", required_argument, 0, 'b'}, + {"magic", required_argument, 0, 'k'}, + {"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, "s:h:n:m:t:p:d:o:b:k:", long_options, &option_index); + if (c == -1) break; + switch (c) { + case 's': { + vSeeds.push_back(optarg); + break; + } + + 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 'b': { + int p = strtol(optarg, NULL, 10); + if (p > 0 && p < 65536) nP2Port = p; + break; + } + + case 'k': { + long int n; + unsigned int c; + magic = optarg; + if (strlen(magic)!=8) + break; + n = strtol(magic, NULL, 16); + if (n==0 && strcmp(magic, "00000000")) + break; + for (n=0; n<4; ++n) { + sscanf(&magic[n*2], "%2x", &c); + pchMessageStart[n] = (unsigned char) (c & 0xff); + } + break; + } + + case '?': { + showHelp = true; + break; + } + } + } + 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) { + do { + CService ip; + int wait = 5; + if (!db.Get(ip, wait)) { + wait *= 1000; + wait += rand() % (500 * NTHREADS); + Sleep(wait); + continue; + } + int ban = 0; + vector addr; + int clientV = 0; + int blocks = 0; + std::string clientSV; + bool ret = TestNode(ip,ban,clientV,clientSV,blocks,addr); + db.Add(addr); + if (ret) { + db.Good(ip, clientV, clientSV, blocks); + } else { + db.Bad(ip, ban); + } + } while(1); +} + +extern "C" int GetIPList(void *thread, addr_t *addr, int max, int ipv4, int ipv6); + +class CDnsThread { +public: + dns_opt_t dns_opt; // must be first + const int id; + vector cache; + int nIPv4, nIPv6; + time_t cacheTime; + unsigned int cacheHits; + uint64_t dbQueries; + + void cacheHit(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); + cacheHits++; + if (force || cacheHits > (cache.size()*cache.size()/400) || (cacheHits*cacheHits > cache.size() / 20 && (now - cacheTime > 5))) { + set ips; + db.GetIPs(ips, 1000, nets); + dbQueries++; + cache.clear(); + nIPv4 = 0; + nIPv6 = 0; + 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); + cache.push_back(a); + nIPv4++; +#ifdef USE_IPV6 + } else if ((*it).GetIn6Addr(&addr6)) { + addr_t a; + a.v = 6; + memcpy(&a.data.v6, &addr6, 16); + cache.push_back(a); + nIPv6++; +#endif + } + } + cacheHits = 0; + 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 = 60; + dns_opt.nsttl = 40000; + dns_opt.cb = GetIPList; + dns_opt.port = opts->nPort; + dns_opt.nRequests = 0; + cache.clear(); + cache.reserve(1000); + cacheTime = 0; + cacheHits = 0; + dbQueries = 0; + nIPv4 = 0; + nIPv6 = 0; + cacheHit(true); + } + + void run() { + dnsserver(&dns_opt); + } +}; + +extern "C" int GetIPList(void *data, addr_t* addr, int max, int ipv4, int ipv6) { + CDnsThread *thread = (CDnsThread*)data; + thread->cacheHit(); + unsigned int size = thread->cache.size(); + unsigned int maxmax = (ipv4 ? thread->nIPv4 : 0) + (ipv6 ? thread->nIPv6 : 0); + if (max > size) + max = size; + if (max > maxmax) + max = maxmax; + int i=0; + while (icache[j].v == 4) || + (ipv6 && thread->cache[j].v == 6); + if (ok) break; + j++; + if (j==size) + j=i; + } while(1); + addr[i] = thread->cache[j]; + thread->cache[j] = thread->cache[i]; + thread->cache[i] = addr[i]; + i++; + } + return max; +} + +vector dnsThread; + +extern "C" void* ThreadDNS(void* arg) { + CDnsThread *thread = (CDnsThread*)arg; + thread->run(); +} + +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*) { + do { + Sleep(100000); + { + 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"); + vector v = db.GetAll(); + sort(v.begin(), v.end(), StatCompare); + fprintf(d, "# address \t%%(2h)\t%%(8h)\t%%(1d)\t%%(7d)\t%%(30d)\tblocks\tversion\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, "%s\t%.2f%%\t%.2f%%\t%.2f%%\t%.2f%%\t%.2f%%\t%i\t%i \"%s\"\n", rep.ip.ToString().c_str(), 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.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); +} + +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; idns_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); +} + +static vector vSeeds; +unsigned short nP2Port; + +extern "C" void* ThreadSeeder(void*) { + vector vDnsSeeds; + vector::iterator itr; + for (itr = vSeeds.begin(); itr != vSeeds.end(); itr++) { + size_t len = itr->length(); + if (len>=6 && !itr->compare(len-6, 6, ".onion")) + db.Add(CService(itr->c_str(), nP2Port), true); + else + vDnsSeeds.push_back(*itr); + } + do { + for (itr = vDnsSeeds.begin(); itr != vDnsSeeds.end(); itr++) { + vector ips; + LookupHost(itr->c_str(), ips); + for (vector::iterator it = ips.begin(); it != ips.end(); it++) { + db.Add(CService(*it, nP2Port), true); + } + } + Sleep(1800000); + } while(1); +} + +int main(int argc, char **argv) { + signal(SIGPIPE, SIG_IGN); + setbuf(stdout, NULL); + CDnsSeedOpts opts; + opts.ParseCommandLine(argc, argv); + nP2Port = opts.nP2Port; + vSeeds.reserve(vSeeds.size() + opts.vSeeds.size()); + vSeeds.insert(vSeeds.end(), opts.vSeeds.begin(), opts.vSeeds.end()); + if (opts.vSeeds.empty()) { + vSeeds.push_back("novacoin.ru"); + vSeeds.push_back("itzod.ru"); + } + 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); + } + } + bool fDNS = 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); + } + 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; + printf("Starting seeder..."); + pthread_create(&threadSeed, NULL, ThreadSeeder, NULL); + printf("done\n"); + printf("Starting %i crawler threads...", opts.nThreads); + for (int i=0; i +#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 +# ifdef USE_IPV6 + aiHint.ai_family = AF_UNSPEC; +# else + aiHint.ai_family = AF_INET; +# endif + aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST; +#else +# ifdef USE_IPV6 + aiHint.ai_family = AF_UNSPEC; +# else + aiHint.ai_family = AF_INET; +# endif + 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)); + } + +#ifdef USE_IPV6 + 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)); + } +#endif + + 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; + +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + 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; i6 && 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); +} + +#ifdef USE_IPV6 +CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr) +{ + memcpy(ip, &ipv6Addr, 16); +} +#endif + +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::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() && !(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); +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + 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; +} + +#ifdef USE_IPV6 +bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const +{ + memcpy(pipv6Addr, ip, 16); + return true; +} +#endif + +// 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) +{ +} + +#ifdef USE_IPV6 +CService::CService(const struct in6_addr& ipv6Addr, unsigned short portIn) : CNetAddr(ipv6Addr), port(portIn) +{ +} +#endif + +CService::CService(const struct sockaddr_in& addr) : CNetAddr(addr.sin_addr), port(ntohs(addr.sin_port)) +{ + assert(addr.sin_family == AF_INET); +} + +#ifdef USE_IPV6 +CService::CService(const struct sockaddr_in6 &addr) : CNetAddr(addr.sin6_addr), port(ntohs(addr.sin6_port)) +{ + assert(addr.sin6_family == AF_INET6); +} +#endif + +bool CService::SetSockAddr(const struct sockaddr *paddr) +{ + switch (paddr->sa_family) { + case AF_INET: + *this = CService(*(const struct sockaddr_in*)paddr); + return true; +#ifdef USE_IPV6 + case AF_INET6: + *this = CService(*(const struct sockaddr_in6*)paddr); + return true; +#endif + 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; + } +#ifdef USE_IPV6 + 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; + } +#endif + 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/netbase.h b/netbase.h new file mode 100644 index 0000000..560e218 --- /dev/null +++ b/netbase.h @@ -0,0 +1,151 @@ +// 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 "serialize.h" +#include "compat.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 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; + +#ifdef USE_IPV6 + CNetAddr(const struct in6_addr& pipv6Addr); + bool GetIn6Addr(struct in6_addr* pipv6Addr) const; +#endif + + 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; + +#ifdef USE_IPV6 + CService(const struct in6_addr& ipv6Addr, unsigned short port); + CService(const struct sockaddr_in6& addr); +#endif + + 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/protocol.cpp b/protocol.cpp new file mode 100644 index 0000000..bb43761 --- /dev/null +++ b/protocol.cpp @@ -0,0 +1,161 @@ +// 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 "protocol.h" +#include "util.h" +#include "netbase.h" + + +#ifndef WIN32 +# include +#endif + +bool fTestNet = false; + +static const char* ppszTypeName[] = +{ + "ERROR", + "tx", + "block", +}; + +unsigned char pchMessageStart[4] = { 0xe4, 0xe8, 0xe9, 0xe5 }; + +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 nServicesIn) : CService(ipIn) +{ + Init(); + nServices = nServicesIn; +} + +void CAddress::Init() +{ + nServices = NODE_NETWORK; + 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/protocol.h b/protocol.h new file mode 100644 index 0000000..7cc680f --- /dev/null +++ b/protocol.h @@ -0,0 +1,125 @@ +// 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 +#include "uint256.h" + +extern unsigned short nP2Port; + +extern bool fTestNet; +static inline unsigned short GetDefaultPort(const bool testnet = fTestNet) +{ + return testnet ? 17777 : 7777; +} + +// +// 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 +{ + NODE_NETWORK = (1 << 0), +}; + +class CAddress : public CService +{ + public: + CAddress(); + CAddress(CService ipIn, uint64 nServicesIn=NODE_NETWORK); + + 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/serialize.h b/serialize.h new file mode 100644 index 0000000..d3f6b7d --- /dev/null +++ b/serialize.h @@ -0,0 +1,1312 @@ +// 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, const_iterator first, 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); + } + + 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/strlcpy.h b/strlcpy.h new file mode 100644 index 0000000..2cc786e --- /dev/null +++ b/strlcpy.h @@ -0,0 +1,90 @@ +/* + * 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/test.pl b/test.pl new file mode 100644 index 0000000..1f34b9a --- /dev/null +++ b/test.pl @@ -0,0 +1,67 @@ +#!/usr/bin/perl + +use threads; +use threads::shared; +use bytes; +use IO::Socket; +use strict; + +my @dom = ("seed","bitcoin","sipa","be"); + +my $run :shared = 1; + +sub go { + my ($idx) = @_; + + my $runs = 0; + + my $sock = IO::Socket::INET->new( + Proto => 'udp', + PeerPort => 53, + PeerAddr => "vps.sipa.be", + ) or die "Could not create socket: $!\n"; + + while($run) { + + my $id = int(rand(65536)); + my $qr = 0; + my $opcode = 0; + my $aa = 0; + my $tc = 0; + my $rd = 0; + my $ra = 0; + my $z = 0; + my $rcode = 0; + my $qdcount = 1; + my $ancount = 0; + my $nscount = 0; + my $arcount = 0; + my $header = pack('nnnnnn',$id,1*$qr + 2*$opcode + 32*$aa + 64*$tc + 128*$rd + 256*$ra + 512*$z + 4096*$rcode, $qdcount, $ancount, $nscount, $arcount); + my $qtype = 1; # A record + my $qclass = 1; # IN class + my $query = (join("", map { chr(length($_)) . $_ } (@dom,""))) . pack('nn',$qtype,$qclass); + my $msg = $header . $query; + $sock->send($msg); + my $resp; + $runs++ if ($sock->recv($resp, 512, 0)); + +# $sock->close(); + } + return $runs; +} + +my @threads; + +for my $i (0..500) { + $threads[$i] = threads->create(\&go, $i); +} + +sleep 10; + +$run=0; +my $runs = 0; +foreach my $thr (@threads) { + $runs += $thr->join(); +} + +print "$runs runs\n"; diff --git a/uint256.h b/uint256.h new file mode 100644 index 0000000..b613282 --- /dev/null +++ b/uint256.h @@ -0,0 +1,766 @@ +// 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[*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/util.cpp b/util.cpp new file mode 100644 index 0000000..3a87429 --- /dev/null +++ b/util.cpp @@ -0,0 +1,218 @@ +#include +#include "util.h" + +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> 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 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()); +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..fbcbd1f --- /dev/null +++ b/util.h @@ -0,0 +1,106 @@ +#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 -- 1.7.1