From: root Date: Thu, 15 Aug 2013 11:17:14 +0000 (+0000) Subject: Initial commit X-Git-Url: https://git.novaco.in/?p=novacoin-seeder.git;a=commitdiff_plain;h=7d700b34ba4213906a7429292a7f3aae1f30d677 Initial commit Comit of novacoin-seeder sources. Novacoin-seeder is the bitcoin-seeder adaptation. --- 7d700b34ba4213906a7429292a7f3aae1f30d677 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