Initial commit
authorroot <root@ltcmine.ru>
Thu, 15 Aug 2013 11:17:14 +0000 (11:17 +0000)
committerroot <root@ltcmine.ru>
Thu, 15 Aug 2013 11:17:14 +0000 (11:17 +0000)
Comit of novacoin-seeder sources. Novacoin-seeder is the bitcoin-seeder adaptation.

22 files changed:
COPYING [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
bitcoin.cpp [new file with mode: 0644]
bitcoin.h [new file with mode: 0644]
combine.pl [new file with mode: 0644]
compat.h [new file with mode: 0644]
db.cpp [new file with mode: 0644]
db.h [new file with mode: 0644]
dns.c [new file with mode: 0644]
dns.h [new file with mode: 0644]
main.cpp [new file with mode: 0644]
netbase.cpp [new file with mode: 0644]
netbase.h [new file with mode: 0644]
protocol.cpp [new file with mode: 0644]
protocol.h [new file with mode: 0644]
serialize.h [new file with mode: 0644]
strlcpy.h [new file with mode: 0644]
test.pl [new file with mode: 0644]
uint256.h [new file with mode: 0644]
util.cpp [new file with mode: 0644]
util.h [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
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 (file)
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 (file)
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 (file)
index 0000000..3ade33d
--- /dev/null
@@ -0,0 +1,305 @@
+#include <algorithm>
+
+#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<CAddress> *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<CAddress> vAddrNew;
+      vRecv >> vAddrNew;
+      // printf("%s: got %i addresses\n", ToString(you).c_str(), (int)vAddrNew.size());
+      int64 now = time(NULL);
+      vector<CAddress>::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<char> 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<CAddress>& 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<CAddress>& 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<CAddress> 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 (file)
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<CAddress>& vAddr);
+
+#endif
diff --git a/combine.pl b/combine.pl
new file mode 100644 (file)
index 0000000..6493724
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+sub loadFile {
+  my ($file) = @_;
+  my %ret;
+  my $max = 0;
+  open FILE,$file;
+  while (<FILE>) {
+    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 (file)
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 <winsock2.h>
+#include <mswsock.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <ifaddrs.h>
+#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 (file)
index 0000000..98b467d
--- /dev/null
+++ b/db.cpp
@@ -0,0 +1,216 @@
+#include "db.h"
+#include <stdlib.h>
+
+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<int>::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<int>::iterator it = unkId.begin();
+        ret = *it;
+      } else {
+        // 90% of the time try the last learned IP
+        set<int>::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<CNetAddr>& 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<int> 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<int>::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 (file)
index 0000000..3046778
--- /dev/null
+++ b/db.h
@@ -0,0 +1,305 @@
+#include <stdint.h>
+#include <math.h>
+
+#include <set>
+#include <map>
+#include <vector>
+#include <deque>
+
+#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<int, CAddrInfo> idToInfo; // map address id to address info (b,c,d,e)
+  std::map<CService, int> ipToId; // map ip to id (b,c,d,e)
+  std::deque<int> ourId; // sequence of tried nodes, in order we have tried connecting to them (c,d)
+  std::set<int> unkId; // set of nodes not yet tried (b)
+  std::set<int> 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<CNetAddr>& ips, int max, const bool *nets); // get a random set of IPs (shared lock only)
+
+public:
+  std::map<CService, time_t> 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<int, CAddrInfo>::iterator it = idToInfo.begin(); it != idToInfo.end(); it++) {
+           (*it).second.ignoreTill = 0;
+      }
+  }
+  
+  std::vector<CAddrReport> GetAll() {
+    std::vector<CAddrReport> ret;
+    SHARED_CRITICAL_BLOCK(cs) {
+      for (std::deque<int>::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<CAddrDb*>(this);
+        int n = ourId.size() + unkId.size();
+        READWRITE(n);
+        for (std::deque<int>::const_iterator it = ourId.begin(); it != ourId.end(); it++) {
+          std::map<int, CAddrInfo>::iterator ci = db->idToInfo.find(*it);
+          READWRITE((*ci).second);
+        }
+        for (std::set<int>::const_iterator it = unkId.begin(); it != unkId.end(); it++) {
+          std::map<int, CAddrInfo>::iterator ci = db->idToInfo.find(*it);
+          READWRITE((*ci).second);
+        }
+      } else {
+        CAddrDb *db = const_cast<CAddrDb*>(this);
+        db->nId = 0;
+        int n;
+        READWRITE(n);
+        for (int i=0; i<n; i++) {
+          CAddrInfo info;
+          READWRITE(info);
+          if (!info.GetBanTime()) {
+            int id = db->nId++;
+            db->idToInfo[id] = info;
+            db->ipToId[info.ip] = id;
+            if (info.ourLastTry) {
+              db->ourId.push_back(id);
+              if (info.IsGood()) db->goodId.insert(id);
+            } else {
+              db->unkId.insert(id);
+            }
+          }
+        }
+        db->nDirty++;
+      }
+      READWRITE(banned);
+    }
+  });)
+
+  void Add(const CAddress &addr, bool fForce = false) {
+    CRITICAL_BLOCK(cs)
+      Add_(addr, fForce);
+  }
+  void Add(const std::vector<CAddress> &vAddr, bool fForce = false) {
+    CRITICAL_BLOCK(cs)
+      for (int i=0; i<vAddr.size(); i++)
+        Add_(vAddr[i], fForce);
+  }
+  void Good(const CService &addr, int clientVersion, std::string clientSubVersion, int blocks) {
+    CRITICAL_BLOCK(cs)
+      Good_(addr, clientVersion, clientSubVersion, blocks);
+  }
+  void Skipped(const CService &addr) {
+    CRITICAL_BLOCK(cs)
+      Skipped_(addr);
+  }
+  void Bad(const CService &addr, int ban = 0) {
+    CRITICAL_BLOCK(cs)
+      Bad_(addr, ban);
+  }
+  bool Get(CService &ip, int& wait) {
+    CRITICAL_BLOCK(cs)
+      return Get_(ip, wait);
+  }
+  void GetIPs(std::set<CNetAddr>& 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 (file)
index 0000000..218f1e5
--- /dev/null
+++ b/dns.c
@@ -0,0 +1,442 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <time.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#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) && (namel<hostl+2 || name[namel-hostl-1]!='.' || strcmp(name+namel-hostl,opt->host))) { error = 5; goto error; }
+  if (inend - inpos < 4) { error = 1; goto error; }
+  // copy question to output
+  memcpy(outbuf+12, inbuf+12, inpos+4 - (inbuf+12));
+  // set counts
+  outbuf[4] = 0;  outbuf[5] = 1;
+  outbuf[6] = 0;  outbuf[7] = 0;
+  outbuf[8] = 0;  outbuf[9] = 0;
+  outbuf[10] = 0; outbuf[11] = 0;
+  // set qr
+  outbuf[2] |= 128;
+  
+  int typ = (inpos[0] << 8) + inpos[1];
+  int cls = (inpos[2] << 8) + inpos[3];
+  inpos += 4;
+  
+  unsigned char *outpos = outbuf+(inpos-inbuf);
+  unsigned char *outend = outbuf + BUFLEN;
+  
+  // printf("DNS: Request host='%s' type=%i class=%i\n", name, typ, cls);
+  
+  // calculate 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 (file)
index 0000000..97dd321
--- /dev/null
+++ b/dns.h
@@ -0,0 +1,28 @@
+#ifndef _DNS_H_
+#define _DNS_H_ 1
+
+#include <stdint.h>
+
+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 (file)
index 0000000..5a6cb9f
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,458 @@
+#include <algorithm>
+
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#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<string> 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 <host> -n <ns> [-m <mbox>] [-t <threads>] [-p <port>]\n"
+                              "\n"
+                              "Options:\n"
+                              "-s <seed>       Seed node to collect peers from (replaces default)\n"
+                              "-h <host>       Hostname of the DNS seed\n"
+                              "-n <ns>         Hostname of the nameserver\n"
+                              "-m <mbox>       E-Mail address reported in SOA records\n"
+                              "-t <threads>    Number of crawlers to run in parallel (default 24)\n"
+                              "-d <threads>    Number of DNS server threads (default 24)\n"
+                              "-p <port>       UDP port to listen on (default 53)\n"
+                              "-o <ip:port>    Tor proxy IP/Port\n"
+                              "--p2port <port> P2P port to connect to\n"
+                              "--magic <hex>   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<CAddress> 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<addr_t> 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<CNetAddr> ips;
+      db.GetIPs(ips, 1000, nets);
+      dbQueries++;
+      cache.clear();
+      nIPv4 = 0;
+      nIPv6 = 0;
+      cache.reserve(ips.size());
+      for (set<CNetAddr>::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 (i<max) {
+    int j = i + (rand() % (size - i));
+    do {
+        bool ok = (ipv4 && thread->cache[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<CDnsThread*> 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<CAddrReport> 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<CAddrReport>::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; i<dnsThread.size(); i++) {
+      requests += dnsThread[i]->dns_opt.nRequests;
+      queries += dnsThread[i]->dbQueries;
+    }
+    printf("%s %i/%i available (%i tried in %is, %i new, %i active), %i banned; %llu DNS requests, %llu db queries", c, stats.nGood, stats.nAvail, stats.nTracked, stats.nAge, stats.nNew, stats.nAvail - stats.nTracked - stats.nNew, stats.nBanned, (unsigned long long)requests, (unsigned long long)queries);
+    Sleep(1000);
+  } while(1);
+}
+
+static vector<string> vSeeds;
+unsigned short nP2Port;
+
+extern "C" void* ThreadSeeder(void*) {
+  vector<string> vDnsSeeds;
+  vector<string>::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<CNetAddr> ips;
+      LookupHost(itr->c_str(), ips);
+      for (vector<CNetAddr>::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<opts.nThreads; i++) {
+    pthread_t thread;
+    pthread_create(&thread, NULL, ThreadCrawler, NULL);
+  }
+  printf("done\n");
+  pthread_create(&threadDump, NULL, ThreadDumper, NULL);
+  if (fDNS) {
+    printf("Starting %i DNS threads for %s on %s (port %i)...", opts.nDnsThreads, opts.host, opts.ns, opts.nPort);
+    dnsThread.clear();
+    for (int i=0; i<opts.nDnsThreads; i++) {
+      dnsThread.push_back(new CDnsThread(&opts, i));
+      pthread_create(&threadDns, NULL, ThreadDNS, dnsThread[i]);
+      printf(".");
+      Sleep(20);
+    }
+    printf("done\n");
+  }
+  pthread_create(&threadStats, NULL, ThreadStats, NULL);
+  void* res;
+  pthread_join(threadDump, &res);
+  return 0;
+}
diff --git a/netbase.cpp b/netbase.cpp
new file mode 100644 (file)
index 0000000..2630f87
--- /dev/null
@@ -0,0 +1,1165 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2012 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "netbase.h"
+#include "util.h"
+
+#ifndef WIN32
+#include <sys/fcntl.h>
+#endif
+
+#include "strlcpy.h"
+#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
+
+#define printf my_printf
+
+using namespace std;
+
+// Settings
+typedef std::pair<CService, int> 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<CNetAddr>& 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<CNetAddr>& 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<CNetAddr>& vIP, unsigned int nMaxSolutions)
+{
+    return LookupHost(pszName, vIP, nMaxSolutions, false);
+}
+
+bool Lookup(const char *pszName, std::vector<CService>& 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<CNetAddr> 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<CService> 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<char>(std::min((int)strDest.size(), 255));
+    strSocks5 += strDest;
+    strSocks5 += static_cast<char>((port >> 8) & 0xFF);
+    strSocks5 += static_cast<char>((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; i<NET_MAX; i++) {
+        if (proxyInfo[i].second && (addr == (CNetAddr)proxyInfo[i].first))
+            return true;
+    }
+    return false;
+}
+
+bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout)
+{
+    const proxyType &proxy = proxyInfo[addrDest.GetNetwork()];
+
+    // no proxy needed
+    if (!proxy.second)
+        return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout);
+
+    SOCKET hSocket = INVALID_SOCKET;
+
+    // first connect to proxy server
+    if (!ConnectSocketDirectly(proxy.first, hSocket, nTimeout))
+        return false;
+
+    // do socks negotiation
+    switch (proxy.second) {
+    case 4:
+        if (!Socks4(addrDest, hSocket))
+            return false;
+        break;
+    case 5:
+        if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket))
+            return false;
+        break;
+    default:
+        return false;
+    }
+
+    hSocketRet = hSocket;
+    return true;
+}
+
+bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout)
+{
+    string strDest;
+    int port = portDefault;
+    SplitHostPort(string(pszDest), port, strDest);
+
+    SOCKET hSocket = INVALID_SOCKET;
+    CService addrResolved(CNetAddr(strDest, fNameLookup && !nameproxyInfo.second), port);
+    if (addrResolved.IsValid()) {
+        addr = addrResolved;
+        return ConnectSocket(addr, hSocketRet, nTimeout);
+    }
+    addr = CService("0.0.0.0:0");
+    if (!nameproxyInfo.second)
+        return false;
+    if (!ConnectSocketDirectly(nameproxyInfo.first, hSocket, nTimeout))
+        return false;
+
+    switch(nameproxyInfo.second)
+    {
+        default:
+        case 4: return false;
+        case 5:
+            if (!Socks5(strDest, port, hSocket))
+                return false;
+            break;
+    }
+
+    hSocketRet = hSocket;
+    return true;
+}
+
+void CNetAddr::Init()
+{
+    memset(ip, 0, 16);
+}
+
+void CNetAddr::SetIP(const CNetAddr& ipIn)
+{
+    memcpy(ip, ipIn.ip, sizeof(ip));
+}
+
+static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
+static const unsigned char pchGarliCat[] = {0xFD,0x60,0xDB,0x4D,0xDD,0xB5};
+
+bool CNetAddr::SetSpecial(const std::string &strName)
+{
+    if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") {
+        std::vector<unsigned char> 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<unsigned char> 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<CNetAddr> vIP;
+    if (LookupHost(pszIp, vIP, 1, fAllowLookup))
+        *this = vIP[0];
+}
+
+CNetAddr::CNetAddr(const std::string &strIp, bool fAllowLookup)
+{
+    Init();
+    std::vector<CNetAddr> 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<unsigned char> CNetAddr::GetGroup() const
+{
+    std::vector<unsigned char> 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<unsigned char> CService::GetKey() const
+{
+     std::vector<unsigned char> 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 (file)
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 <string>
+#include <vector>
+
+#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<unsigned char> 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<unsigned char> 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<CService*>(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<CNetAddr>& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true);
+bool LookupHostNumeric(const char *pszName, std::vector<CNetAddr>& 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<CService>& 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 (file)
index 0000000..bb43761
--- /dev/null
@@ -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 <vector>
+#include <stdexcept>
+
+#include "protocol.h"
+#include "util.h"
+#include "netbase.h"
+
+
+#ifndef WIN32
+# include <arpa/inet.h>
+#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 (file)
index 0000000..7cc680f
--- /dev/null
@@ -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 <string>
+#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<CAddress*>(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 (file)
index 0000000..d3f6b7d
--- /dev/null
@@ -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 <string>
+#include <vector>
+#include <map>
+#include <set>
+#include <cassert>
+#include <climits>
+#include <cstring>
+#include <cstdio>
+
+#include <boost/type_traits/is_fundamental.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
+#include <boost/tuple/tuple_io.hpp>
+
+#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 <windows.h>
+// 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 <windows.h>
+#define mlock(p, n) VirtualLock((p), (n));
+#define munlock(p, n) VirtualUnlock((p), (n));
+#else
+#include <sys/mman.h>
+#include <limits.h>
+/* This comes from limits.h if it's not defined there set a sane default */
+#ifndef PAGESIZE
+#include <unistd.h>
+#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<typename T>
+inline T& REF(const T& val)
+{
+    return const_cast<T&>(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<typename Stream>                   \
+    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<typename Stream>                   \
+    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<typename Stream> inline void Serialize(Stream& s, char a,           int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, signed char a,    int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, unsigned char a,  int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, signed short a,   int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, signed int a,     int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, unsigned int a,   int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, signed long a,    int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, unsigned long a,  int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, int64 a,          int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, uint64 a,         int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, float a,          int, int=0) { WRITEDATA(s, a); }
+template<typename Stream> inline void Serialize(Stream& s, double a,         int, int=0) { WRITEDATA(s, a); }
+
+template<typename Stream> inline void Unserialize(Stream& s, char& a,           int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, signed char& a,    int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, unsigned char& a,  int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, signed short& a,   int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, signed int& a,     int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, unsigned int& a,   int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, signed long& a,    int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, unsigned long& a,  int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, int64& a,          int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, uint64& a,         int, int=0) { READDATA(s, a); }
+template<typename Stream> inline void Unserialize(Stream& s, float& a,          int, int=0) { READDATA(s, a); }
+template<typename Stream> 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<typename Stream> inline void Serialize(Stream& s, bool a, int, int=0)    { char f=a; WRITEDATA(s, f); }
+template<typename Stream> 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<typename Stream>
+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<typename Stream>
+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<typename Stream>
+    void Serialize(Stream& s, int, int=0) const
+    {
+        s.write(pbegin, pend - pbegin);
+    }
+
+    template<typename Stream>
+    void Unserialize(Stream& s, int, int=0)
+    {
+        s.read(pbegin, pend - pbegin);
+    }
+};
+
+
+
+//
+// string stored as a fixed length field
+//
+template<std::size_t LEN>
+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<typename Stream>
+    void Serialize(Stream& s, int, int=0) const
+    {
+        char pszBuf[LEN];
+        strncpy(pszBuf, pcstr->c_str(), LEN);
+        s.write(pszBuf, LEN);
+    }
+
+    template<typename Stream>
+    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<typename C> unsigned int GetSerializeSize(const std::basic_string<C>& str, int, int=0);
+template<typename Stream, typename C> void Serialize(Stream& os, const std::basic_string<C>& str, int, int=0);
+template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_string<C>& str, int, int=0);
+
+// vector
+template<typename T, typename A> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&);
+template<typename T, typename A> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&);
+template<typename T, typename A> inline unsigned int GetSerializeSize(const std::vector<T, A>& v, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&);
+template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&);
+template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&);
+template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&);
+template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& 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<typename Stream> void Serialize(Stream& os, const CScript& v, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream> void Unserialize(Stream& is, CScript& v, int nType, int nVersion=PROTOCOL_VERSION);
+
+// pair
+template<typename K, typename T> unsigned int GetSerializeSize(const std::pair<K, T>& item, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename K, typename T> void Serialize(Stream& os, const std::pair<K, T>& item, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename K, typename T> void Unserialize(Stream& is, std::pair<K, T>& item, int nType, int nVersion=PROTOCOL_VERSION);
+
+// 3 tuple
+template<typename T0, typename T1, typename T2> unsigned int GetSerializeSize(const boost::tuple<T0, T1, T2>& item, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename T0, typename T1, typename T2> void Serialize(Stream& os, const boost::tuple<T0, T1, T2>& item, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename T0, typename T1, typename T2> void Unserialize(Stream& is, boost::tuple<T0, T1, T2>& item, int nType, int nVersion=PROTOCOL_VERSION);
+
+// 4 tuple
+template<typename T0, typename T1, typename T2, typename T3> unsigned int GetSerializeSize(const boost::tuple<T0, T1, T2, T3>& item, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename T0, typename T1, typename T2, typename T3> void Serialize(Stream& os, const boost::tuple<T0, T1, T2, T3>& item, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename T0, typename T1, typename T2, typename T3> void Unserialize(Stream& is, boost::tuple<T0, T1, T2, T3>& item, int nType, int nVersion=PROTOCOL_VERSION);
+
+// map
+template<typename K, typename T, typename Pred, typename A> unsigned int GetSerializeSize(const std::map<K, T, Pred, A>& m, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename K, typename T, typename Pred, typename A> void Serialize(Stream& os, const std::map<K, T, Pred, A>& m, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename K, typename T, typename Pred, typename A> void Unserialize(Stream& is, std::map<K, T, Pred, A>& m, int nType, int nVersion=PROTOCOL_VERSION);
+
+// set
+template<typename K, typename Pred, typename A> unsigned int GetSerializeSize(const std::set<K, Pred, A>& m, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename K, typename Pred, typename A> void Serialize(Stream& os, const std::set<K, Pred, A>& m, int nType, int nVersion=PROTOCOL_VERSION);
+template<typename Stream, typename K, typename Pred, typename A> void Unserialize(Stream& is, std::set<K, Pred, A>& 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<typename T>
+inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion=PROTOCOL_VERSION)
+{
+    return a.GetSerializeSize((int)nType, nVersion);
+}
+
+template<typename Stream, typename T>
+inline void Serialize(Stream& os, const T& a, long nType, int nVersion=PROTOCOL_VERSION)
+{
+    a.Serialize(os, (int)nType, nVersion);
+}
+
+template<typename Stream, typename T>
+inline void Unserialize(Stream& is, T& a, long nType, int nVersion=PROTOCOL_VERSION)
+{
+    a.Unserialize(is, (int)nType, nVersion);
+}
+
+
+
+
+
+//
+// string
+//
+template<typename C>
+unsigned int GetSerializeSize(const std::basic_string<C>& str, int, int)
+{
+    return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]);
+}
+
+template<typename Stream, typename C>
+void Serialize(Stream& os, const std::basic_string<C>& str, int, int)
+{
+    WriteCompactSize(os, str.size());
+    if (!str.empty())
+        os.write((char*)&str[0], str.size() * sizeof(str[0]));
+}
+
+template<typename Stream, typename C>
+void Unserialize(Stream& is, std::basic_string<C>& 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<typename T, typename A>
+unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&)
+{
+    return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T));
+}
+
+template<typename T, typename A>
+unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&)
+{
+    unsigned int nSize = GetSizeOfCompactSize(v.size());
+    for (typename std::vector<T, A>::const_iterator vi = v.begin(); vi != v.end(); ++vi)
+        nSize += GetSerializeSize((*vi), nType, nVersion);
+    return nSize;
+}
+
+template<typename T, typename A>
+inline unsigned int GetSerializeSize(const std::vector<T, A>& v, int nType, int nVersion)
+{
+    return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental<T>());
+}
+
+
+template<typename Stream, typename T, typename A>
+void Serialize_impl(Stream& os, const std::vector<T, A>& 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<typename Stream, typename T, typename A>
+void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&)
+{
+    WriteCompactSize(os, v.size());
+    for (typename std::vector<T, A>::const_iterator vi = v.begin(); vi != v.end(); ++vi)
+        ::Serialize(os, (*vi), nType, nVersion);
+}
+
+template<typename Stream, typename T, typename A>
+inline void Serialize(Stream& os, const std::vector<T, A>& v, int nType, int nVersion)
+{
+    Serialize_impl(os, v, nType, nVersion, boost::is_fundamental<T>());
+}
+
+
+template<typename Stream, typename T, typename A>
+void Unserialize_impl(Stream& is, std::vector<T, A>& 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<typename Stream, typename T, typename A>
+void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&)
+{
+    //unsigned int nSize = ReadCompactSize(is);
+    //v.resize(nSize);
+    //for (std::vector<T, A>::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<typename Stream, typename T, typename A>
+inline void Unserialize(Stream& is, std::vector<T, A>& v, int nType, int nVersion)
+{
+    Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental<T>());
+}
+
+
+
+//
+// others derived from vector
+//
+inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion)
+{
+    return GetSerializeSize((const std::vector<unsigned char>&)v, nType, nVersion);
+}
+
+template<typename Stream>
+void Serialize(Stream& os, const CScript& v, int nType, int nVersion)
+{
+    Serialize(os, (const std::vector<unsigned char>&)v, nType, nVersion);
+}
+
+template<typename Stream>
+void Unserialize(Stream& is, CScript& v, int nType, int nVersion)
+{
+    Unserialize(is, (std::vector<unsigned char>&)v, nType, nVersion);
+}
+
+
+
+//
+// pair
+//
+template<typename K, typename T>
+unsigned int GetSerializeSize(const std::pair<K, T>& item, int nType, int nVersion)
+{
+    return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion);
+}
+
+template<typename Stream, typename K, typename T>
+void Serialize(Stream& os, const std::pair<K, T>& item, int nType, int nVersion)
+{
+    Serialize(os, item.first, nType, nVersion);
+    Serialize(os, item.second, nType, nVersion);
+}
+
+template<typename Stream, typename K, typename T>
+void Unserialize(Stream& is, std::pair<K, T>& item, int nType, int nVersion)
+{
+    Unserialize(is, item.first, nType, nVersion);
+    Unserialize(is, item.second, nType, nVersion);
+}
+
+
+
+//
+// 3 tuple
+//
+template<typename T0, typename T1, typename T2>
+unsigned int GetSerializeSize(const boost::tuple<T0, T1, T2>& 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<typename Stream, typename T0, typename T1, typename T2>
+void Serialize(Stream& os, const boost::tuple<T0, T1, T2>& 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<typename Stream, typename T0, typename T1, typename T2>
+void Unserialize(Stream& is, boost::tuple<T0, T1, T2>& 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<typename T0, typename T1, typename T2, typename T3>
+unsigned int GetSerializeSize(const boost::tuple<T0, T1, T2, T3>& 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<typename Stream, typename T0, typename T1, typename T2, typename T3>
+void Serialize(Stream& os, const boost::tuple<T0, T1, T2, T3>& 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<typename Stream, typename T0, typename T1, typename T2, typename T3>
+void Unserialize(Stream& is, boost::tuple<T0, T1, T2, T3>& 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<typename K, typename T, typename Pred, typename A>
+unsigned int GetSerializeSize(const std::map<K, T, Pred, A>& m, int nType, int nVersion)
+{
+    unsigned int nSize = GetSizeOfCompactSize(m.size());
+    for (typename std::map<K, T, Pred, A>::const_iterator mi = m.begin(); mi != m.end(); ++mi)
+        nSize += GetSerializeSize((*mi), nType, nVersion);
+    return nSize;
+}
+
+template<typename Stream, typename K, typename T, typename Pred, typename A>
+void Serialize(Stream& os, const std::map<K, T, Pred, A>& m, int nType, int nVersion)
+{
+    WriteCompactSize(os, m.size());
+    for (typename std::map<K, T, Pred, A>::const_iterator mi = m.begin(); mi != m.end(); ++mi)
+        Serialize(os, (*mi), nType, nVersion);
+}
+
+template<typename Stream, typename K, typename T, typename Pred, typename A>
+void Unserialize(Stream& is, std::map<K, T, Pred, A>& m, int nType, int nVersion)
+{
+    m.clear();
+    unsigned int nSize = ReadCompactSize(is);
+    typename std::map<K, T, Pred, A>::iterator mi = m.begin();
+    for (unsigned int i = 0; i < nSize; i++)
+    {
+        std::pair<K, T> item;
+        Unserialize(is, item, nType, nVersion);
+        mi = m.insert(mi, item);
+    }
+}
+
+
+
+//
+// set
+//
+template<typename K, typename Pred, typename A>
+unsigned int GetSerializeSize(const std::set<K, Pred, A>& m, int nType, int nVersion)
+{
+    unsigned int nSize = GetSizeOfCompactSize(m.size());
+    for (typename std::set<K, Pred, A>::const_iterator it = m.begin(); it != m.end(); ++it)
+        nSize += GetSerializeSize((*it), nType, nVersion);
+    return nSize;
+}
+
+template<typename Stream, typename K, typename Pred, typename A>
+void Serialize(Stream& os, const std::set<K, Pred, A>& m, int nType, int nVersion)
+{
+    WriteCompactSize(os, m.size());
+    for (typename std::set<K, Pred, A>::const_iterator it = m.begin(); it != m.end(); ++it)
+        Serialize(os, (*it), nType, nVersion);
+}
+
+template<typename Stream, typename K, typename Pred, typename A>
+void Unserialize(Stream& is, std::set<K, Pred, A>& m, int nType, int nVersion)
+{
+    m.clear();
+    unsigned int nSize = ReadCompactSize(is);
+    typename std::set<K, Pred, A>::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<typename Stream, typename T>
+inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action)
+{
+    return ::GetSerializeSize(obj, nType, nVersion);
+}
+
+template<typename Stream, typename T>
+inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action)
+{
+    ::Serialize(s, obj, nType, nVersion);
+    return 0;
+}
+
+template<typename Stream, typename T>
+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<typename T>
+struct secure_allocator : public std::allocator<T>
+{
+    // MSVC8 default copy constructor is broken
+    typedef std::allocator<T> 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 <typename U>
+    secure_allocator(const secure_allocator<U>& a) throw() : base(a) {}
+    ~secure_allocator() throw() {}
+    template<typename _Other> struct rebind
+    { typedef secure_allocator<_Other> other; };
+
+    T* allocate(std::size_t n, const void *hint = 0)
+    {
+        T *p;
+        p = std::allocator<T>::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<T>::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<char, secure_allocator<char> > 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<char>& vchIn, int nTypeIn=SER_NETWORK, int nVersionIn=PROTOCOL_VERSION) : vch(vchIn.begin(), vchIn.end())
+    {
+        Init(nTypeIn, nVersionIn);
+    }
+
+    CDataStream(const std::vector<unsigned char>& 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<char>::const_iterator first, std::vector<char>::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<typename Stream>
+    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<typename T>
+    unsigned int GetSerializeSize(const T& obj)
+    {
+        // Tells the size of the object if serialized to this stream
+        return ::GetSerializeSize(obj, nType, nVersion);
+    }
+
+    template<typename T>
+    CDataStream& operator<<(const T& obj)
+    {
+        // Serialize to this stream
+        ::Serialize(*this, obj, nType, nVersion);
+        return (*this);
+    }
+
+    template<typename T>
+    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 <iostream>
+int main(int argc, char *argv[])
+{
+    vector<unsigned char> 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<typename T>
+    unsigned int GetSerializeSize(const T& obj)
+    {
+        // Tells the size of the object if serialized to this stream
+        return ::GetSerializeSize(obj, nType, nVersion);
+    }
+
+    template<typename T>
+    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<typename T>
+    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 (file)
index 0000000..2cc786e
--- /dev/null
+++ b/strlcpy.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+/*
+ * 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 (file)
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 (file)
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 <limits.h>
+#include <string>
+#include <vector>
+
+#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<std::string> vArg);
+
+
+
+// We have to keep a separate base class without constructors
+// so the compiler will let us use it in a union
+template<unsigned int BITS>
+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<typename Stream>
+    void Serialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) const
+    {
+        s.write((char*)pn, sizeof(pn));
+    }
+
+    template<typename Stream>
+    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<std::string> 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<unsigned char>& 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<unsigned char>& 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<std::string> 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 (file)
index 0000000..3a87429
--- /dev/null
+++ b/util.cpp
@@ -0,0 +1,218 @@
+#include <stdio.h>
+#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<pchEnd)
+    {
+        int enc = *(pch++);
+        switch (mode)
+        {
+            case 0: // we have no bits
+                strRet += pbase32[enc >> 3];
+                left = (enc & 7) << 2;
+                mode = 1;
+                break;
+
+            case 1: // we have three bits
+                strRet += pbase32[left | (enc >> 6)];
+                strRet += pbase32[(enc >> 1) & 31];
+                left = (enc & 1) << 4;
+                mode = 2;
+                break;
+
+            case 2: // we have one bit
+                strRet += pbase32[left | (enc >> 4)];
+                left = (enc & 15) << 1;
+                mode = 3;
+                break;
+
+            case 3: // we have four bits
+                strRet += pbase32[left | (enc >> 7)];
+                strRet += pbase32[(enc >> 2) & 31];
+                left = (enc & 3) << 3;
+                mode = 4;
+                break;
+
+            case 4: // we have two bits
+                strRet += pbase32[left | (enc >> 5)];
+                strRet += pbase32[enc & 31];
+                mode = 0;
+        }
+    }
+
+    static const int nPadding[5] = {0, 6, 4, 3, 1};
+    if (mode)
+    {
+        strRet += pbase32[left];
+        for (int n=0; n<nPadding[mode]; n++)
+             strRet += '=';
+    }
+
+    return strRet;
+}
+
+string EncodeBase32(const string& str)
+{
+    return EncodeBase32((const unsigned char*)str.c_str(), str.size());
+}
+
+vector<unsigned char> 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<unsigned char> 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<unsigned char> 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 (file)
index 0000000..fbcbd1f
--- /dev/null
+++ b/util.h
@@ -0,0 +1,106 @@
+#ifndef _UTIL_H_
+#define _UTIL_H_ 1
+
+#include <pthread.h>
+#include <errno.h>
+#include <openssl/sha.h>
+#include <stdarg.h>
+
+#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<typename T1> 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<unsigned char> 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