text: // Copyright (c) 2009-2010 Satoshi Nakamoto
  // Copyright (c) 2009-2014 The Bitcoin developers
  // Copyright (c) 2014-2015 The Dash developers
  // Copyright (c) 2015-2017 The PIVX developers
  // Copyright (c) 2017-2018 The Dotme developers
  // Distributed under the MIT/X11 software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #if defined(HAVE_CONFIG_H)
  #include "config/dotme-config.h"
  #endif
  
  #include "net.h"
  
  #include "addrman.h"
  #include "chainparams.h"
  #include "clientversion.h"
  #include "miner.h"
  #include "obfuscation.h"
  #include "primitives/transaction.h"
  #include "ui_interface.h"
  #include "wallet.h"
  
  #ifdef WIN32
  #include <string.h>
  #else
  #include <fcntl.h>
  #endif
  
  #ifdef USE_UPNP
  #include <miniupnpc/miniupnpc.h>
  #include <miniupnpc/miniwget.h>
  #include <miniupnpc/upnpcommands.h>
  #include <miniupnpc/upnperrors.h>
  #endif
  
  #include <boost/filesystem.hpp>
  #include <boost/thread.hpp>
  
  // Dump addresses to peers.dat every 15 minutes (900s)
  #define DUMP_ADDRESSES_INTERVAL 900
  
  #if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL)
  #define MSG_NOSIGNAL 0
  #endif
  
  // Fix for ancient MinGW versions, that don't have defined these in ws2tcpip.h.
  // Todo: Can be removed when our pull-tester is upgraded to a modern MinGW version.
  #ifdef WIN32
  #ifndef PROTECTION_LEVEL_UNRESTRICTED
  #define PROTECTION_LEVEL_UNRESTRICTED 10
  #endif
  #ifndef IPV6_PROTECTION_LEVEL
  #define IPV6_PROTECTION_LEVEL 23
  #endif
  #endif
  
  using namespace boost;
  using namespace std;
  
  namespace
  {
  const int MAX_OUTBOUND_CONNECTIONS = 16;
  
  struct ListenSocket {
      SOCKET socket;
      bool whitelisted;
  
      ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {}
  };
  }
  
  //
  // Global state variables
  //
  bool fDiscover = true;
  bool fListen = true;
  uint64_t nLocalServices = NODE_NETWORK;
  CCriticalSection cs_mapLocalHost;
  map<CNetAddr, LocalServiceInfo> mapLocalHost;
  //static bool vfReachable[NET_MAX] = {};  // Network related fix
  static bool vfLimited[NET_MAX] = {};
  static CNode* pnodeLocalHost = NULL;
  uint64_t nLocalHostNonce = 0;
  static std::vector<ListenSocket> vhListenSocket;
  CAddrMan addrman;
  int nMaxConnections = 125;
  bool fAddressesInitialized = false;
  
  vector<CNode*> vNodes;
  CCriticalSection cs_vNodes;
  map<CInv, CDataStream> mapRelay;
  deque<pair<int64_t, CInv> > vRelayExpiration;
  CCriticalSection cs_mapRelay;
  limitedmap<CInv, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
  
  static deque<string> vOneShots;
  CCriticalSection cs_vOneShots;
  
  set<CNetAddr> setservAddNodeAddresses;
  CCriticalSection cs_setservAddNodeAddresses;
  
  vector<std::string> vAddedNodes;
  CCriticalSection cs_vAddedNodes;
  
  NodeId nLastNodeId = 0;
  CCriticalSection cs_nLastNodeId;
  
  static CSemaphore* semOutbound = NULL;
  boost::condition_variable messageHandlerCondition;
  
  // Signals for message handling
  static CNodeSignals g_signals;
  CNodeSignals& GetNodeSignals() { return g_signals; }
  
  void AddOneShot(string strDest)
  {
      LOCK(cs_vOneShots);
      vOneShots.push_back(strDest);
  }
  
  unsigned short GetListenPort()
  {
      return (unsigned short)(GetArg("-port", Params().GetDefaultPort()));
  }
  
  // find 'best' local address for a particular peer
  bool GetLocal(CService& addr, const CNetAddr* paddrPeer)
  {
      if (!fListen)
          return false;
  
      int nBestScore = -1;
      int nBestReachability = -1;
      {
          LOCK(cs_mapLocalHost);
          for (map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) {
              int nScore = (*it).second.nScore;
              int nReachability = (*it).first.GetReachabilityFrom(paddrPeer);
              if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) {
                  addr = CService((*it).first, (*it).second.nPort);
                  nBestReachability = nReachability;
                  nBestScore = nScore;
              }
          }
      }
      return nBestScore >= 0;
  }
  
  // get best local address for a particular peer as a CAddress
  // Otherwise, return the unroutable 0.0.0.0 but filled in with
  // the normal parameters, since the IP may be changed to a useful
  // one by discovery.
  CAddress GetLocalAddress(const CNetAddr* paddrPeer)
  {
      CAddress ret(CService("0.0.0.0", GetListenPort()), 0);
      CService addr;
      if (GetLocal(addr, paddrPeer)) {
          ret = CAddress(addr);
      }
      ret.nServices = nLocalServices;
      ret.nTime = GetAdjustedTime();
      return ret;
  }
  
  bool RecvLine(SOCKET hSocket, string& strLine)
  {
      strLine = "";
      while (true) {
          char c;
          int nBytes = recv(hSocket, &c, 1, 0);
          if (nBytes > 0) {
              if (c == '\n')
                  continue;
              if (c == '\r')
                  return true;
              strLine += c;
              if (strLine.size() >= 9000)
                  return true;
          } else if (nBytes <= 0) {
              boost::this_thread::interruption_point();
              if (nBytes < 0) {
                  int nErr = WSAGetLastError();
                  if (nErr == WSAEMSGSIZE)
                      continue;
                  if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS) {
                      MilliSleep(10);
                      continue;
                  }
              }
              if (!strLine.empty())
                  return true;
              if (nBytes == 0) {
                  // socket closed
                  LogPrint("net", "socket closed\n");
                  return false;
              } else {
                  // socket error
                  int nErr = WSAGetLastError();
                  LogPrint("net", "recv failed: %s\n", NetworkErrorString(nErr));
                  return false;
              }
          }
      }
  }
  
  int GetnScore(const CService& addr)
  {
      LOCK(cs_mapLocalHost);
      if (mapLocalHost.count(addr) == LOCAL_NONE)
          return 0;
      return mapLocalHost[addr].nScore;
  }
  
  // Is our peer's addrLocal potentially useful as an external IP source?
  bool IsPeerAddrLocalGood(CNode* pnode)
  {
      return fDiscover && pnode->addr.IsRoutable() && pnode->addrLocal.IsRoutable() &&
             !IsLimited(pnode->addrLocal.GetNetwork());
  }
  
  // pushes our own address to a peer
  void AdvertiseLocal(CNode* pnode)
  {
      if (fListen && pnode->fSuccessfullyConnected) {
          CAddress addrLocal = GetLocalAddress(&pnode->addr);
          // If discovery is enabled, sometimes give our peer the address it
          // tells us that it sees us as in case it has a better idea of our
          // address than we do.
          if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
                                                GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8 : 2) == 0)) {
              addrLocal.SetIP(pnode->addrLocal);
          }
          if (addrLocal.IsRoutable()) {
              pnode->PushAddress(addrLocal);
          }
      }
  }
  
  // Network related fix
  
  /* void SetReachable(enum Network net, bool fFlag)
  {
      LOCK(cs_mapLocalHost);
      vfReachable[net] = fFlag;
      if (net == NET_IPV6 && fFlag)
          vfReachable[NET_IPV4] = true;
  } */
  
  // learn a new local address
  bool AddLocal(const CService& addr, int nScore)
  {
      if (!addr.IsRoutable())
          return false;
  
      if (!fDiscover && nScore < LOCAL_MANUAL)
          return false;
  
      if (IsLimited(addr))
          return false;
  
      LogPrintf("AddLocal(%s,%i)\n", addr.ToString(), nScore);
  
      {
          LOCK(cs_mapLocalHost);
          bool fAlready = mapLocalHost.count(addr) > 0;
          LocalServiceInfo& info = mapLocalHost[addr];
          if (!fAlready || nScore >= info.nScore) {
              info.nScore = nScore + (fAlready ? 1 : 0);
              info.nPort = addr.GetPort();
          }
         // SetReachable(addr.GetNetwork());  // Network related fix
      }
  
      return true;
  }
  
  bool AddLocal(const CNetAddr& addr, int nScore)
  {
      return AddLocal(CService(addr, GetListenPort()), nScore);
  }
  
  /** Make a particular network entirely off-limits (no automatic connects to it) */
  void SetLimited(enum Network net, bool fLimited)
  {
      if (net == NET_UNROUTABLE)
          return;
      LOCK(cs_mapLocalHost);
      vfLimited[net] = fLimited;
  }
  
  bool IsLimited(enum Network net)
  {
      LOCK(cs_mapLocalHost);
      return vfLimited[net];
  }
  
  bool IsLimited(const CNetAddr& addr)
  {
      return IsLimited(addr.GetNetwork());
  }
  
  /** vote for a local address */
  bool SeenLocal(const CService& addr)
  {
      {
          LOCK(cs_mapLocalHost);
          if (mapLocalHost.count(addr) == 0)
              return false;
          mapLocalHost[addr].nScore++;
      }
      return true;
  }
  
  
  /** check whether a given address is potentially local */
  bool IsLocal(const CService& addr)
  {
      LOCK(cs_mapLocalHost);
      return mapLocalHost.count(addr) > 0;
  }
  
  /** check whether a given network is one we can probably connect to */
  bool IsReachable(enum Network net)
  {
      LOCK(cs_mapLocalHost);
     // return vfReachable[net] && !vfLimited[net];
  	return !vfLimited[net];   // Network related fix
  }
  
  /** check whether a given address is in a network we can probably connect to */
  bool IsReachable(const CNetAddr& addr)
  {
      enum Network net = addr.GetNetwork();
      return IsReachable(net);
  }
  
  void AddressCurrentlyConnected(const CService& addr)
  {
      addrman.Connected(addr);
  }
  
  
  uint64_t CNode::nTotalBytesRecv = 0;
  uint64_t CNode::nTotalBytesSent = 0;
  CCriticalSection CNode::cs_totalBytesRecv;
  CCriticalSection CNode::cs_totalBytesSent;
  
  CNode* FindNode(const CNetAddr& ip)
  {
      LOCK(cs_vNodes);
      BOOST_FOREACH (CNode* pnode, vNodes)
          if ((CNetAddr)pnode->addr == ip)
              return (pnode);
      return NULL;
  }
  
  CNode* FindNode(const std::string& addrName)
  {
      LOCK(cs_vNodes);
      BOOST_FOREACH (CNode* pnode, vNodes)
          if (pnode->addrName == addrName)
              return (pnode);
      return NULL;
  }
  
  CNode* FindNode(const CService& addr)
  {
      LOCK(cs_vNodes);
      BOOST_FOREACH (CNode* pnode, vNodes) {
          if (Params().NetworkID() == CBaseChainParams::REGTEST) {
              //if using regtest, just check the IP
              if ((CNetAddr)pnode->addr == (CNetAddr)addr)
                  return (pnode);
          } else {
              if (pnode->addr == addr)
                  return (pnode);
          }
      }
      return NULL;
  }
  
  CNode* ConnectNode(CAddress addrConnect, const char* pszDest, bool obfuScationMaster)
  {
      if (pszDest == NULL) {
          // we clean masternode connections in CMasternodeMan::ProcessMasternodeConnections()
          // so should be safe to skip this and connect to local Hot MN on CActiveMasternode::ManageStatus()
          if (IsLocal(addrConnect) && !obfuScationMaster)
              return NULL;
  
          // Look for an existing connection
          CNode* pnode = FindNode((CService)addrConnect);
          if (pnode) {
              pnode->fObfuScationMaster = obfuScationMaster;
  
              pnode->AddRef();
              return pnode;
          }
      }
  
      /// debug print
      LogPrint("net", "trying connection %s lastseen=%.1fhrs\n",
          pszDest ? pszDest : addrConnect.ToString(),
          pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime) / 3600.0);
  
      // Connect
      SOCKET hSocket;
      bool proxyConnectionFailed = false;
      if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) :
                    ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) {
          if (!IsSelectableSocket(hSocket)) {
              LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
              CloseSocket(hSocket);
              return NULL;
          }
  
          addrman.Attempt(addrConnect);
  
          // Add node
          CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false);
          pnode->AddRef();
  
          {
              LOCK(cs_vNodes);
              vNodes.push_back(pnode);
          }
  
          pnode->nTimeConnected = GetTime();
          if (obfuScationMaster) pnode->fObfuScationMaster = true;
  
          return pnode;
      } else if (!proxyConnectionFailed) {
          // If connecting to the node failed, and failure is not caused by a problem connecting to
          // the proxy, mark this as an attempt.
          addrman.Attempt(addrConnect);
      }
  
      return NULL;
  }
  
  void CNode::CloseSocketDisconnect()
  {
      fDisconnect = true;
      if (hSocket != INVALID_SOCKET) {
          LogPrint("net", "disconnecting peer=%d\n", id);
          CloseSocket(hSocket);
      }
  
      // in case this fails, we'll empty the recv buffer when the CNode is deleted
      TRY_LOCK(cs_vRecvMsg, lockRecv);
      if (lockRecv)
          vRecvMsg.clear();
  }
  
  void CNode::PushVersion()
  {
      int nBestHeight = g_signals.GetHeight().get_value_or(0);
  
      /// when NTP implemented, change to just nTime = GetAdjustedTime()
      int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime());
      CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0", 0)));
      CAddress addrMe = GetLocalAddress(&addr);
      GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
      if (fLogIPs)
          LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id);
      else
          LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id);
      PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
          nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight, true);
  }
  
  
  std::map<CNetAddr, int64_t> CNode::setBanned;
  CCriticalSection CNode::cs_setBanned;
  
  void CNode::ClearBanned()
  {
      setBanned.clear();
  }
  
  bool CNode::IsBanned(CNetAddr ip)
  {
      bool fResult = false;
      {
          LOCK(cs_setBanned);
          std::map<CNetAddr, int64_t>::iterator i = setBanned.find(ip);
          if (i != setBanned.end()) {
              int64_t t = (*i).second;
              if (GetTime() < t)
                  fResult = true;
          }
      }
      return fResult;
  }
  
  bool CNode::Ban(const CNetAddr& addr)
  {
      int64_t banTime = GetTime() + GetArg("-bantime", 60 * 60 * 24); // Default 24-hour ban
      {
          LOCK(cs_setBanned);
          if (setBanned[addr] < banTime)
              setBanned[addr] = banTime;
      }
      return true;
  }
  
  
  std::vector<CSubNet> CNode::vWhitelistedRange;
  CCriticalSection CNode::cs_vWhitelistedRange;
  
  bool CNode::IsWhitelistedRange(const CNetAddr& addr)
  {
      LOCK(cs_vWhitelistedRange);
      BOOST_FOREACH (const CSubNet& subnet, vWhitelistedRange) {
          if (subnet.Match(addr))
              return true;
      }
      return false;
  }
  
  void CNode::AddWhitelistedRange(const CSubNet& subnet)
  {
      LOCK(cs_vWhitelistedRange);
      vWhitelistedRange.push_back(subnet);
  }
  
  #undef X
  #define X(name) stats.name = name
  void CNode::copyStats(CNodeStats& stats)
  {
      stats.nodeid = this->GetId();
      X(nServices);
      X(nLastSend);
      X(nLastRecv);
      X(nTimeConnected);
      X(addrName);
      X(nVersion);
      X(cleanSubVer);
      X(fInbound);
      X(nStartingHeight);
      X(nSendBytes);
      X(nRecvBytes);
      X(fWhitelisted);
  
      // It is common for nodes with good ping times to suddenly become lagged,
      // due to a new block arriving or other large transfer.
      // Merely reporting pingtime might fool the caller into thinking the node was still responsive,
      // since pingtime does not update until the ping is complete, which might take a while.
      // So, if a ping is taking an unusually long time in flight,
      // the caller can immediately detect that this is happening.
      int64_t nPingUsecWait = 0;
      if ((0 != nPingNonceSent) && (0 != nPingUsecStart)) {
          nPingUsecWait = GetTimeMicros() - nPingUsecStart;
      }
  
      // Raw ping time is in microseconds, but show it to user as whole seconds (Dotme users should be well used to small numbers with many decimal places by now :)
      stats.dPingTime = (((double)nPingUsecTime) / 1e6);
      stats.dPingWait = (((double)nPingUsecWait) / 1e6);
  
      // Leave string empty if addrLocal invalid (not filled in yet)
      stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : "";
  }
  #undef X
  
  // requires LOCK(cs_vRecvMsg)
  bool CNode::ReceiveMsgBytes(const char* pch, unsigned int nBytes)
  {
      while (nBytes > 0) {
          // get current incomplete message, or create a new one
          if (vRecvMsg.empty() ||
              vRecvMsg.back().complete())
              vRecvMsg.push_back(CNetMessage(SER_NETWORK, nRecvVersion));
  
          CNetMessage& msg = vRecvMsg.back();
  
          // absorb network data
          int handled;
          if (!msg.in_data)
              handled = msg.readHeader(pch, nBytes);
          else
              handled = msg.readData(pch, nBytes);
  
          if (handled < 0)
              return false;
  
          if (msg.in_data && msg.hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
              LogPrint("net", "Oversized message from peer=%i, disconnecting", GetId());
              return false;
          }
  
          pch += handled;
          nBytes -= handled;
  
          if (msg.complete()) {
              msg.nTime = GetTimeMicros();
              messageHandlerCondition.notify_one();
          }
      }
  
      return true;
  }
  
  int CNetMessage::readHeader(const char* pch, unsigned int nBytes)
  {
      // copy data to temporary parsing buffer
      unsigned int nRemaining = 24 - nHdrPos;
      unsigned int nCopy = std::min(nRemaining, nBytes);
  
      memcpy(&hdrbuf[nHdrPos], pch, nCopy);
      nHdrPos += nCopy;
  
      // if header incomplete, exit
      if (nHdrPos < 24)
          return nCopy;
  
      // deserialize to CMessageHeader
      try {
          hdrbuf >> hdr;
      } catch (const std::exception&) {
          return -1;
      }
  
      // reject messages larger than MAX_SIZE
      if (hdr.nMessageSize > MAX_SIZE)
          return -1;
  
      // switch state to reading message data
      in_data = true;
  
      return nCopy;
  }
  
  int CNetMessage::readData(const char* pch, unsigned int nBytes)
  {
      unsigned int nRemaining = hdr.nMessageSize - nDataPos;
      unsigned int nCopy = std::min(nRemaining, nBytes);
  
      if (vRecv.size() < nDataPos + nCopy) {
          // Allocate up to 256 KiB ahead, but never more than the total message size.
          vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024));
      }
  
      memcpy(&vRecv[nDataPos], pch, nCopy);
      nDataPos += nCopy;
  
      return nCopy;
  }
  
  
  // requires LOCK(cs_vSend)
  void SocketSendData(CNode* pnode)
  {
      std::deque<CSerializeData>::iterator it = pnode->vSendMsg.begin();
  
      while (it != pnode->vSendMsg.end()) {
          const CSerializeData& data = *it;
          assert(data.size() > pnode->nSendOffset);
          int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
          if (nBytes > 0) {
              pnode->nLastSend = GetTime();
              pnode->nSendBytes += nBytes;
              pnode->nSendOffset += nBytes;
              pnode->RecordBytesSent(nBytes);
              if (pnode->nSendOffset == data.size()) {
                  pnode->nSendOffset = 0;
                  pnode->nSendSize -= data.size();
                  it++;
              } else {
                  // could not send full message; stop sending more
                  break;
              }
          } else {
              if (nBytes < 0) {
                  // error
                  int nErr = WSAGetLastError();
                  if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) {
                      LogPrintf("socket send error %s\n", NetworkErrorString(nErr));
                      pnode->CloseSocketDisconnect();
                  }
              }
              // couldn't send anything at all
              break;
          }
      }
  
      if (it == pnode->vSendMsg.end()) {
          assert(pnode->nSendOffset == 0);
          assert(pnode->nSendSize == 0);
      }
      pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it);
  }
  
  static list<CNode*> vNodesDisconnected;
  
  void ThreadSocketHandler()
  {
      unsigned int nPrevNodeCount = 0;
      while (true) {
          //
          // Disconnect nodes
          //
          {
              LOCK(cs_vNodes);
              // Disconnect unused nodes
              vector<CNode*> vNodesCopy = vNodes;
              BOOST_FOREACH (CNode* pnode, vNodesCopy) {
                  if (pnode->fDisconnect ||
                      (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.empty())) {
                      // remove from vNodes
                      vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
  
                      // release outbound grant (if any)
                      pnode->grantOutbound.Release();
  
                      // close socket and cleanup
                      pnode->CloseSocketDisconnect();
  
                      // hold in disconnected pool until all refs are released
                      if (pnode->fNetworkNode || pnode->fInbound)
                          pnode->Release();
                      vNodesDisconnected.push_back(pnode);
                  }
              }
          }
          {
              // Delete disconnected nodes
              list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
              BOOST_FOREACH (CNode* pnode, vNodesDisconnectedCopy) {
                  // wait until threads are done using it
                  if (pnode->GetRefCount() <= 0) {
                      bool fDelete = false;
                      {
                          TRY_LOCK(pnode->cs_vSend, lockSend);
                          if (lockSend) {
                              TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                              if (lockRecv) {
                                  TRY_LOCK(pnode->cs_inventory, lockInv);
                                  if (lockInv)
                                      fDelete = true;
                              }
                          }
                      }
                      if (fDelete) {
                          vNodesDisconnected.remove(pnode);
                          delete pnode;
                      }
                  }
              }
          }
          if (vNodes.size() != nPrevNodeCount) {
              nPrevNodeCount = vNodes.size();
              uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount);
          }
  
          //
          // Find which sockets have data to receive
          //
          struct timeval timeout;
          timeout.tv_sec = 0;
          timeout.tv_usec = 3000; // frequency to poll pnode->vSend
  
          fd_set fdsetRecv;
          fd_set fdsetSend;
          fd_set fdsetError;
          FD_ZERO(&fdsetRecv);
          FD_ZERO(&fdsetSend);
          FD_ZERO(&fdsetError);
          SOCKET hSocketMax = 0;
          bool have_fds = false;
  
          BOOST_FOREACH (const ListenSocket& hListenSocket, vhListenSocket) {
              FD_SET(hListenSocket.socket, &fdsetRecv);
              hSocketMax = max(hSocketMax, hListenSocket.socket);
              have_fds = true;
          }
  
          {
              LOCK(cs_vNodes);
              BOOST_FOREACH (CNode* pnode, vNodes) {
                  if (pnode->hSocket == INVALID_SOCKET)
                      continue;
                  FD_SET(pnode->hSocket, &fdsetError);
                  hSocketMax = max(hSocketMax, pnode->hSocket);
                  have_fds = true;
  
                  // Implement the following logic:
                  // * If there is data to send, select() for sending data. As this only
                  //   happens when optimistic write failed, we choose to first drain the
                  //   write buffer in this case before receiving more. This avoids
                  //   needlessly queueing received data, if the remote peer is not themselves
                  //   receiving data. This means properly utilizing TCP flow control signalling.
                  // * Otherwise, if there is no (complete) message in the receive buffer,
                  //   or there is space left in the buffer, select() for receiving data.
                  // * (if neither of the above applies, there is certainly one message
                  //   in the receiver buffer ready to be processed).
                  // Together, that means that at least one of the following is always possible,
                  // so we don't deadlock:
                  // * We send some data.
                  // * We wait for data to be received (and disconnect after timeout).
                  // * We process a message in the buffer (message handler thread).
                  {
                      TRY_LOCK(pnode->cs_vSend, lockSend);
                      if (lockSend && !pnode->vSendMsg.empty()) {
                          FD_SET(pnode->hSocket, &fdsetSend);
                          continue;
                      }
                  }
                  {
                      TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                      if (lockRecv && (pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() ||
                                          pnode->GetTotalRecvSize() <= ReceiveFloodSize()))
                          FD_SET(pnode->hSocket, &fdsetRecv);
                  }
              }
          }
  
          int nSelect = select(have_fds ? hSocketMax + 1 : 0,
              &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
          boost::this_thread::interruption_point();
  
          if (nSelect == SOCKET_ERROR) {
              if (have_fds) {
                  int nErr = WSAGetLastError();
                  LogPrintf("socket select error %s\n", NetworkErrorString(nErr));
                  for (unsigned int i = 0; i <= hSocketMax; i++)
                      FD_SET(i, &fdsetRecv);
              }
              FD_ZERO(&fdsetSend);
              FD_ZERO(&fdsetError);
              MilliSleep(timeout.tv_usec / 1000);
          }
  
          //
          // Accept new connections
          //
          BOOST_FOREACH (const ListenSocket& hListenSocket, vhListenSocket) {
              if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) {
                  struct sockaddr_storage sockaddr;
                  socklen_t len = sizeof(sockaddr);
                  SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
                  CAddress addr;
                  int nInbound = 0;
  
                  if (hSocket != INVALID_SOCKET)
                      if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
                          LogPrintf("Warning: Unknown socket family\n");
  
                  bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
                  {
                      LOCK(cs_vNodes);
                      BOOST_FOREACH (CNode* pnode, vNodes)
                          if (pnode->fInbound)
                              nInbound++;
                  }
  
                  if (hSocket == INVALID_SOCKET) {
                      int nErr = WSAGetLastError();
                      if (nErr != WSAEWOULDBLOCK)
                          LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
                  } else if (!IsSelectableSocket(hSocket)) {
                      LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString());
                      CloseSocket(hSocket);
                  } else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS) {
                      LogPrint("net", "connection from %s dropped (full)\n", addr.ToString());
                      CloseSocket(hSocket);
                  } else if (CNode::IsBanned(addr) && !whitelisted) {
                      LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
                      CloseSocket(hSocket);
                  } else {
                      CNode* pnode = new CNode(hSocket, addr, "", true);
                      pnode->AddRef();
                      pnode->fWhitelisted = whitelisted;
  
                      {
                          LOCK(cs_vNodes);
                          vNodes.push_back(pnode);
                      }
                  }
              }
          }
  
          //
          // Service each socket
          //
          vector<CNode*> vNodesCopy;
          {
              LOCK(cs_vNodes);
              vNodesCopy = vNodes;
              BOOST_FOREACH (CNode* pnode, vNodesCopy)
                  pnode->AddRef();
          }
          BOOST_FOREACH (CNode* pnode, vNodesCopy) {
              boost::this_thread::interruption_point();
  
              //
              // Receive
              //
              if (pnode->hSocket == INVALID_SOCKET)
                  continue;
              if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) {
                  TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                  if (lockRecv) {
                      {
                          // typical socket buffer is 8K-64K
                          char pchBuf[0x10000];
                          int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
                          if (nBytes > 0) {
                              if (!pnode->ReceiveMsgBytes(pchBuf, nBytes))
                                  pnode->CloseSocketDisconnect();
                              pnode->nLastRecv = GetTime();
                              pnode->nRecvBytes += nBytes;
                              pnode->RecordBytesRecv(nBytes);
                          } else if (nBytes == 0) {
                              // socket closed gracefully
                              if (!pnode->fDisconnect)
                                  LogPrint("net", "socket closed\n");
                              pnode->CloseSocketDisconnect();
                          } else if (nBytes < 0) {
                              // error
                              int nErr = WSAGetLastError();
                              if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) {
                                  if (!pnode->fDisconnect)
                                      LogPrintf("socket recv error %s\n", NetworkErrorString(nErr));
                                  pnode->CloseSocketDisconnect();
                              }
                          }
                      }
                  }
              }
  
              //
              // Send
              //
              if (pnode->hSocket == INVALID_SOCKET)
                  continue;
              if (FD_ISSET(pnode->hSocket, &fdsetSend)) {
                  TRY_LOCK(pnode->cs_vSend, lockSend);
                  if (lockSend)
                      SocketSendData(pnode);
              }
  
              //
              // Inactivity checking
              //
              int64_t nTime = GetTime();
              if (nTime - pnode->nTimeConnected > 60) {
                  if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) {
                      LogPrint("net", "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id);
                      pnode->fDisconnect = true;
                  } else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) {
                      LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend);
                      pnode->fDisconnect = true;
                  } else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90 * 60)) {
                      LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv);
                      pnode->fDisconnect = true;
                  } else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros()) {
                      LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart));
                      pnode->fDisconnect = true;
                  }
              }
          }
          {
              LOCK(cs_vNodes);
              BOOST_FOREACH (CNode* pnode, vNodesCopy)
                  pnode->Release();
          }
      }
  }
  
  
  #ifdef USE_UPNP
  void ThreadMapPort()
  {
      std::string port = strprintf("%u", GetListenPort());
      const char* multicastif = 0;
      const char* minissdpdpath = 0;
      struct UPNPDev* devlist = 0;
      char lanaddr[64];
  
  #ifndef UPNPDISCOVER_SUCCESS
      /* miniupnpc 1.5 */
      devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0);
  #elif MINIUPNPC_API_VERSION < 14
      /* miniupnpc 1.6 */
      int error = 0;
      devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
  #else
      /* miniupnpc 1.9.20150730 */
      int error = 0;
      devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
  #endif
  
      struct UPNPUrls urls;
      struct IGDdatas data;
      int r;
  
      r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
      if (r == 1) {
          if (fDiscover) {
              char externalIPAddress[40];
              r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
              if (r != UPNPCOMMAND_SUCCESS)
                  LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
              else {
                  if (externalIPAddress[0]) {
                      LogPrintf("UPnP: ExternalIPAddress = %s\n", externalIPAddress);
                      AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP);
                  } else
                      LogPrintf("UPnP: GetExternalIPAddress failed.\n");
              }
          }
  
          string strDesc = "Dotme " + FormatFullVersion();
  
          try {
              while (true) {
  #ifndef UPNPDISCOVER_SUCCESS
                  /* miniupnpc 1.5 */
                  r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
                      port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0);
  #else
                  /* miniupnpc 1.6 */
                  r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
                      port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
  #endif
  
                  if (r != UPNPCOMMAND_SUCCESS)
                      LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
                          port, port, lanaddr, r, strupnperror(r));
                  else
                      LogPrintf("UPnP Port Mapping successful.\n");
                  ;
  
                  MilliSleep(20 * 60 * 1000); // Refresh every 20 minutes
              }
          } catch (boost::thread_interrupted) {
              r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
              LogPrintf("UPNP_DeletePortMapping() returned : %d\n", r);
              freeUPNPDevlist(devlist);
              devlist = 0;
              FreeUPNPUrls(&urls);
              throw;
          }
      } else {
          LogPrintf("No valid UPnP IGDs found\n");
          freeUPNPDevlist(devlist);
          devlist = 0;
          if (r != 0)
              FreeUPNPUrls(&urls);
      }
  }
  
  void MapPort(bool fUseUPnP)
  {
      static boost::thread* upnp_thread = NULL;
  
      if (fUseUPnP) {
          if (upnp_thread) {
              upnp_thread->interrupt();
              upnp_thread->join();
              delete upnp_thread;
          }
          upnp_thread = new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort));
      } else if (upnp_thread) {
          upnp_thread->interrupt();
          upnp_thread->join();
          delete upnp_thread;
          upnp_thread = NULL;
      }
  }
  
  #else
  void MapPort(bool)
  {
      // Intentionally left blank.
  }
  #endif
  
  
  void ThreadDNSAddressSeed()
  {
      // goal: only query DNS seeds if address need is acute
      if ((addrman.size() > 0) &&
          (!GetBoolArg("-forcednsseed", false))) {
          MilliSleep(11 * 1000);
  
          LOCK(cs_vNodes);
          if (vNodes.size() >= 2) {
              LogPrintf("P2P peers available. Skipped DNS seeding.\n");
              return;
          }
      }
  
      const vector<CDNSSeedData>& vSeeds = Params().DNSSeeds();
      int found = 0;
  
      LogPrintf("Loading addresses from DNS seeds (could take a while)\n");
  
      BOOST_FOREACH (const CDNSSeedData& seed, vSeeds) {
          if (HaveNameProxy()) {
              AddOneShot(seed.host);
          } else {
              vector<CNetAddr> vIPs;
              vector<CAddress> vAdd;
              if (LookupHost(seed.host.c_str(), vIPs)) {
                  BOOST_FOREACH (CNetAddr& ip, vIPs) {
                      int nOneDay = 24 * 3600;
                      CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()));
                      addr.nTime = GetTime() - 3 * nOneDay - GetRand(4 * nOneDay); // use a random age between 3 and 7 days old
                      vAdd.push_back(addr);
                      found++;
                  }
              }
              addrman.Add(vAdd, CNetAddr(seed.name, true));
          }
      }
  
      LogPrintf("%d addresses found from DNS seeds\n", found);
  }
  
  
  void DumpAddresses()
  {
      int64_t nStart = GetTimeMillis();
  
      CAddrDB adb;
      adb.Write(addrman);
  
      LogPrint("net", "Flushed %d addresses to peers.dat  %dms\n",
          addrman.size(), GetTimeMillis() - nStart);
  }
  
  void static ProcessOneShot()
  {
      string strDest;
      {
          LOCK(cs_vOneShots);
          if (vOneShots.empty())
              return;
          strDest = vOneShots.front();
          vOneShots.pop_front();
      }
      CAddress addr;
      CSemaphoreGrant grant(*semOutbound, true);
      if (grant) {
          if (!OpenNetworkConnection(addr, &grant, strDest.c_str(), true))
              AddOneShot(strDest);
      }
  }
  
  void ThreadOpenConnections()
  {
      // Connect to specific addresses
      if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
          for (int64_t nLoop = 0;; nLoop++) {
              ProcessOneShot();
              BOOST_FOREACH (string strAddr, mapMultiArgs["-connect"]) {
                  CAddress addr;
                  OpenNetworkConnection(addr, NULL, strAddr.c_str());
                  for (int i = 0; i < 10 && i < nLoop; i++) {
                      MilliSleep(500);
                  }
              }
              MilliSleep(500);
          }
      }
  
      // Initiate network connections
      int64_t nStart = GetTime();
      while (true) {
          ProcessOneShot();
  
          MilliSleep(500);
  
          CSemaphoreGrant grant(*semOutbound);
          boost::this_thread::interruption_point();
  
          // Add seed nodes if DNS seeds are all down (an infrastructure attack?).
          if (addrman.size() == 0 && (GetTime() - nStart > 60)) {
              static bool done = false;
              if (!done) {
                  LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n");
                  addrman.Add(Params().FixedSeeds(), CNetAddr("127.0.0.1"));
                  done = true;
              }
          }
  
          //
          // Choose an address to connect to based on most recently seen
          //
          CAddress addrConnect;
  
          // Only connect out to one peer per network group (/16 for IPv4).
          // Do this here so we don't have to critsect vNodes inside mapAddresses critsect.
          int nOutbound = 0;
          set<vector<unsigned char> > setConnected;
          {
              LOCK(cs_vNodes);
              BOOST_FOREACH (CNode* pnode, vNodes) {
                  if (!pnode->fInbound) {
                      setConnected.insert(pnode->addr.GetGroup());
                      nOutbound++;
                  }
              }
          }
  
          int64_t nANow = GetAdjustedTime();
  
          int nTries = 0;
          while (true) {
              CAddress addr = addrman.Select();
  
              // if we selected an invalid address, restart
              if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
                  break;
  
              // If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
              // stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
              // already-connected network ranges, ...) before trying new addrman addresses.
              nTries++;
              if (nTries > 100)
                  break;
  
              if (IsLimited(addr))
                  continue;
  
              // only consider very recently tried nodes after 30 failed attempts
              if (nANow - addr.nLastTry < 600 && nTries < 30)
                  continue;
  
              // do not allow non-default ports, unless after 50 invalid addresses selected already
              if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50)
                  continue;
  
              addrConnect = addr;
              break;
          }
  
          if (addrConnect.IsValid())
              OpenNetworkConnection(addrConnect, &grant);
      }
  }
  
  void ThreadOpenAddedConnections()
  {
      {
          LOCK(cs_vAddedNodes);
          vAddedNodes = mapMultiArgs["-addnode"];
      }
  
      if (HaveNameProxy()) {
          while (true) {
              list<string> lAddresses(0);
              {
                  LOCK(cs_vAddedNodes);
                  BOOST_FOREACH (string& strAddNode, vAddedNodes)
                      lAddresses.push_back(strAddNode);
              }
              BOOST_FOREACH (string& strAddNode, lAddresses) {
                  CAddress addr;
                  CSemaphoreGrant grant(*semOutbound);
                  OpenNetworkConnection(addr, &grant, strAddNode.c_str());
                  MilliSleep(500);
              }
              MilliSleep(120000); // Retry every 2 minutes
          }
      }
  
      for (unsigned int i = 0; true; i++) {
          list<string> lAddresses(0);
          {
              LOCK(cs_vAddedNodes);
              BOOST_FOREACH (string& strAddNode, vAddedNodes)
                  lAddresses.push_back(strAddNode);
          }
  
          list<vector<CService> > lservAddressesToAdd(0);
          BOOST_FOREACH (string& strAddNode, lAddresses) {
              vector<CService> vservNode(0);
              if (Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0)) {
                  lservAddressesToAdd.push_back(vservNode);
                  {
                      LOCK(cs_setservAddNodeAddresses);
                      BOOST_FOREACH (CService& serv, vservNode)
                          setservAddNodeAddresses.insert(serv);
                  }
              }
          }
          // Attempt to connect to each IP for each addnode entry until at least one is successful per addnode entry
          // (keeping in mind that addnode entries can have many IPs if fNameLookup)
          {
              LOCK(cs_vNodes);
              BOOST_FOREACH (CNode* pnode, vNodes)
                  for (list<vector<CService> >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++)
                      BOOST_FOREACH (CService& addrNode, *(it))
                          if (pnode->addr == addrNode) {
                              it = lservAddressesToAdd.erase(it);
                              it--;
                              break;
                          }
          }
          BOOST_FOREACH (vector<CService>& vserv, lservAddressesToAdd) {
              CSemaphoreGrant grant(*semOutbound);
              OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant);
              MilliSleep(500);
          }
          MilliSleep(120000); // Retry every 2 minutes
      }
  }
  
  // if successful, this moves the passed grant to the constructed node
  bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant* grantOutbound, const char* pszDest, bool fOneShot)
  {
      //
      // Initiate outbound network connection
      //
      boost::this_thread::interruption_point();
      if (!pszDest) {
          if (IsLocal(addrConnect) ||
              FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) ||
              FindNode(addrConnect.ToStringIPPort()))
              return false;
      } else if (FindNode(pszDest))
          return false;
  
      CNode* pnode = ConnectNode(addrConnect, pszDest);
      boost::this_thread::interruption_point();
  
      if (!pnode)
          return false;
      if (grantOutbound)
          grantOutbound->MoveTo(pnode->grantOutbound);
      pnode->fNetworkNode = true;
      if (fOneShot)
          pnode->fOneShot = true;
  
      return true;
  }
  
  
  void ThreadMessageHandler()
  {
      boost::mutex condition_mutex;
      boost::unique_lock<boost::mutex> lock(condition_mutex);
  
      SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
      while (true) {
          vector<CNode*> vNodesCopy;
          {
              LOCK(cs_vNodes);
              vNodesCopy = vNodes;
              BOOST_FOREACH (CNode* pnode, vNodesCopy) {
                  pnode->AddRef();
              }
          }
  
          // Poll the connected nodes for messages
          CNode* pnodeTrickle = NULL;
          if (!vNodesCopy.empty())
              pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())];
  
          bool fSleep = true;
  
          BOOST_FOREACH (CNode* pnode, vNodesCopy) {
              if (pnode->fDisconnect)
                  continue;
  
              // Receive messages
              {
                  TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
                  if (lockRecv) {
                      if (!g_signals.ProcessMessages(pnode))
                          pnode->CloseSocketDisconnect();
  
                      if (pnode->nSendSize < SendBufferSize()) {
                          if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) {
                              fSleep = false;
                          }
                      }
                  }
              }
              boost::this_thread::interruption_point();
  
              // Send messages
              {
                  TRY_LOCK(pnode->cs_vSend, lockSend);
                  if (lockSend)
                      g_signals.SendMessages(pnode, pnode == pnodeTrickle || pnode->fWhitelisted);
              }
              boost::this_thread::interruption_point();
          }
  
  
          {
              LOCK(cs_vNodes);
              BOOST_FOREACH (CNode* pnode, vNodesCopy)
                  pnode->Release();
          }
  
          if (fSleep)
              messageHandlerCondition.timed_wait(lock, boost::posix_time::microsec_clock::universal_time() + boost::posix_time::milliseconds(100));
      }
  }
  
  // ppcoin: stake minter thread
  void static ThreadStakeMinter()
  {
      boost::this_thread::interruption_point();
      LogPrintf("ThreadStakeMinter started\n");
      CWallet* pwallet = pwalletMain;
      try {
          BitcoinMiner(pwallet, true);
          boost::this_thread::interruption_point();
      } catch (std::exception& e) {
          LogPrintf("ThreadStakeMinter() exception \n");
      } catch (...) {
          LogPrintf("ThreadStakeMinter() error \n");
      }
      LogPrintf("ThreadStakeMinter exiting,\n");
  }
  
  bool BindListenPort(const CService& addrBind, string& strError, bool fWhitelisted)
  {
      strError = "";
      int nOne = 1;
  
      // Create socket for listening for incoming connections
      struct sockaddr_storage sockaddr;
      socklen_t len = sizeof(sockaddr);
      if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
          strError = strprintf("Error: Bind address family for %s not supported", addrBind.ToString());
          LogPrintf("%s\n", strError);
          return false;
      }
  
      SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
      if (hListenSocket == INVALID_SOCKET) {
          strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %s)", NetworkErrorString(WSAGetLastError()));
          LogPrintf("%s\n", strError);
          return false;
      }
      if (!IsSelectableSocket(hListenSocket)) {
          strError = "Error: Couldn't create a listenable socket for incoming connections";
          LogPrintf("%s\n", strError);
          return false;
      }
  
  
  #ifndef WIN32
  #ifdef SO_NOSIGPIPE
      // Different way of disabling SIGPIPE on BSD
      setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));
  #endif
      // Allow binding if the port is still in TIME_WAIT state after
      // the program was closed and restarted. Not an issue on windows!
      setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
  #endif
  
      // Set to non-blocking, incoming connections will also inherit this
      if (!SetSocketNonBlocking(hListenSocket, true)) {
          strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
          LogPrintf("%s\n", strError);
          return false;
      }
  
      // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
      // and enable it by default or not. Try to enable it, if possible.
      if (addrBind.IsIPv6()) {
  #ifdef IPV6_V6ONLY
  #ifdef WIN32
          setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int));
  #else
          setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int));
  #endif
  #endif
  #ifdef WIN32
          int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
          setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));
  #endif
      }
  
      if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR) {
          int nErr = WSAGetLastError();
          if (nErr == WSAEADDRINUSE)
              strError = strprintf(_("Unable to bind to %s on this computer. Dotme Core is probably already running."), addrBind.ToString());
          else
              strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
          LogPrintf("%s\n", strError);
          CloseSocket(hListenSocket);
          return false;
      }
      LogPrintf("Bound to %s\n", addrBind.ToString());
  
      // Listen for incoming connections
      if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) {
          strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
          LogPrintf("%s\n", strError);
          CloseSocket(hListenSocket);
          return false;
      }
  
      vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
  
      if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
          AddLocal(addrBind, LOCAL_BIND);
  
      return true;
  }
  
  void static Discover(boost::thread_group& threadGroup)
  {
      if (!fDiscover)
          return;
  
  #ifdef WIN32
      // Get local host IP
      char pszHostName[256] = "";
      if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) {
          vector<CNetAddr> vaddr;
          if (LookupHost(pszHostName, vaddr)) {
              BOOST_FOREACH (const CNetAddr& addr, vaddr) {
                  if (AddLocal(addr, LOCAL_IF))
                      LogPrintf("%s: %s - %s\n", __func__, pszHostName, addr.ToString());
              }
          }
      }
  #else
      // Get local host ip
      struct ifaddrs* myaddrs;
      if (getifaddrs(&myaddrs) == 0) {
          for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) {
              if (ifa->ifa_addr == NULL) continue;
              if ((ifa->ifa_flags & IFF_UP) == 0) continue;
              if (strcmp(ifa->ifa_name, "lo") == 0) continue;
              if (strcmp(ifa->ifa_name, "lo0") == 0) continue;
              if (ifa->ifa_addr->sa_family == AF_INET) {
                  struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr);
                  CNetAddr addr(s4->sin_addr);
                  if (AddLocal(addr, LOCAL_IF))
                      LogPrintf("%s: IPv4 %s: %s\n", __func__, ifa->ifa_name, addr.ToString());
              } else if (ifa->ifa_addr->sa_family == AF_INET6) {
                  struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr);
                  CNetAddr addr(s6->sin6_addr);
                  if (AddLocal(addr, LOCAL_IF))
                      LogPrintf("%s: IPv6 %s: %s\n", __func__, ifa->ifa_name, addr.ToString());
              }
          }
          freeifaddrs(myaddrs);
      }
  #endif
  }
  
  void StartNode(boost::thread_group& threadGroup)
  {
      uiInterface.InitMessage(_("Loading addresses..."));
      // Load addresses for peers.dat
      int64_t nStart = GetTimeMillis();
      {
          CAddrDB adb;
          if (!adb.Read(addrman))
              LogPrintf("Invalid or missing peers.dat; recreating\n");
      }
      LogPrintf("Loaded %i addresses from peers.dat  %dms\n",
          addrman.size(), GetTimeMillis() - nStart);
      fAddressesInitialized = true;
  
      if (semOutbound == NULL) {
          // initialize semaphore
          int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections);
          semOutbound = new CSemaphore(nMaxOutbound);
      }
  
      if (pnodeLocalHost == NULL)
          pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices));
  
      Discover(threadGroup);
  
      //
      // Start threads
      //
  
      if (!GetBoolArg("-dnsseed", true))
          LogPrintf("DNS seeding disabled\n");
      else
          threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "dnsseed", &ThreadDNSAddressSeed));
  
      // Map ports with UPnP
      MapPort(GetBoolArg("-upnp", DEFAULT_UPNP));
  
      // Send and receive from sockets, accept connections
      threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "net", &ThreadSocketHandler));
  
      // Initiate outbound connections from -addnode
      threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "addcon", &ThreadOpenAddedConnections));
  
      // Initiate outbound connections
      threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "opencon", &ThreadOpenConnections));
  
      // Process messages
      threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler));
  
      // Dump network addresses
      threadGroup.create_thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, DUMP_ADDRESSES_INTERVAL * 1000));
  
      // ppcoin:mint proof-of-stake blocks in the background
      if (GetBoolArg("-staking", true))
          threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "stakemint", &ThreadStakeMinter));
  }
  
  bool StopNode()
  {
      LogPrintf("StopNode()\n");
      MapPort(false);
      if (semOutbound)
          for (int i = 0; i < MAX_OUTBOUND_CONNECTIONS; i++)
              semOutbound->post();
  
      if (fAddressesInitialized) {
          DumpAddresses();
          fAddressesInitialized = false;
      }
  
      return true;
  }
  
  class CNetCleanup
  {
  public:
      CNetCleanup() {}
  
      ~CNetCleanup()
      {
          // Close sockets
          BOOST_FOREACH (CNode* pnode, vNodes)
              if (pnode->hSocket != INVALID_SOCKET)
                  CloseSocket(pnode->hSocket);
          BOOST_FOREACH (ListenSocket& hListenSocket, vhListenSocket)
              if (hListenSocket.socket != INVALID_SOCKET)
                  if (!CloseSocket(hListenSocket.socket))
                      LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
  
          // clean up some globals (to help leak detection)
          BOOST_FOREACH (CNode* pnode, vNodes)
              delete pnode;
          BOOST_FOREACH (CNode* pnode, vNodesDisconnected)
              delete pnode;
          vNodes.clear();
          vNodesDisconnected.clear();
          vhListenSocket.clear();
          delete semOutbound;
          semOutbound = NULL;
          delete pnodeLocalHost;
          pnodeLocalHost = NULL;
  
  #ifdef WIN32
          // Shutdown Windows Sockets
          WSACleanup();
  #endif
      }
  } instance_of_cnetcleanup;
  
  void CExplicitNetCleanup::callCleanup()
  {
      // Explicit call to destructor of CNetCleanup because it's not implicitly called
      // when the wallet is restarted from within the wallet itself.
      CNetCleanup* tmp = new CNetCleanup();
      delete tmp; // Stroustrup's gonna kill me for that
  }
  
  void RelayTransaction(const CTransaction& tx)
  {
      CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
      ss.reserve(10000);
      ss << tx;
      RelayTransaction(tx, ss);
  }
  
  void RelayTransaction(const CTransaction& tx, const CDataStream& ss)
  {
      CInv inv(MSG_TX, tx.GetHash());
      {
          LOCK(cs_mapRelay);
          // Expire old relay messages
          while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) {
              mapRelay.erase(vRelayExpiration.front().second);
              vRelayExpiration.pop_front();
          }
  
          // Save original serialized message so newer versions are preserved
          mapRelay.insert(std::make_pair(inv, ss));
          vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv));
      }
      LOCK(cs_vNodes);
      BOOST_FOREACH (CNode* pnode, vNodes) {
          if (!pnode->fRelayTxes)
              continue;
          LOCK(pnode->cs_filter);
          if (pnode->pfilter) {
              if (pnode->pfilter->IsRelevantAndUpdate(tx))
                  pnode->PushInventory(inv);
          } else
              pnode->PushInventory(inv);
      }
  }
  
  void RelayTransactionLockReq(const CTransaction& tx, bool relayToAll)
  {
      CInv inv(MSG_TXLOCK_REQUEST, tx.GetHash());
  
      //broadcast the new lock
      LOCK(cs_vNodes);
      BOOST_FOREACH (CNode* pnode, vNodes) {
          if (!relayToAll && !pnode->fRelayTxes)
              continue;
  
          pnode->PushMessage("ix", tx);
      }
  }
  
  void RelayInv(CInv& inv)
  {
      LOCK(cs_vNodes);
      BOOST_FOREACH (CNode* pnode, vNodes)
          if (pnode->nVersion >= ActiveProtocol())
              pnode->PushInventory(inv);
  }
  
  void CNode::RecordBytesRecv(uint64_t bytes)
  {
      LOCK(cs_totalBytesRecv);
      nTotalBytesRecv += bytes;
  }
  
  void CNode::RecordBytesSent(uint64_t bytes)
  {
      LOCK(cs_totalBytesSent);
      nTotalBytesSent += bytes;
  }
  
  uint64_t CNode::GetTotalBytesRecv()
  {
      LOCK(cs_totalBytesRecv);
      return nTotalBytesRecv;
  }
  
  uint64_t CNode::GetTotalBytesSent()
  {
      LOCK(cs_totalBytesSent);
      return nTotalBytesSent;
  }
  
  void CNode::Fuzz(int nChance)
  {
      if (!fSuccessfullyConnected) return; // Don't fuzz initial handshake
      if (GetRand(nChance) != 0) return;   // Fuzz 1 of every nChance messages
  
      switch (GetRand(3)) {
      case 0:
          // xor a random byte with a random value:
          if (!ssSend.empty()) {
              CDataStream::size_type pos = GetRand(ssSend.size());
              ssSend[pos] ^= (unsigned char)(GetRand(256));
          }
          break;
      case 1:
          // delete a random byte:
          if (!ssSend.empty()) {
              CDataStream::size_type pos = GetRand(ssSend.size());
              ssSend.erase(ssSend.begin() + pos);
          }
          break;
      case 2:
          // insert a random byte at a random position
          {
              CDataStream::size_type pos = GetRand(ssSend.size());
              char ch = (char)GetRand(256);
              ssSend.insert(ssSend.begin() + pos, ch);
          }
          break;
      }
      // Chance of more than one change half the time:
      // (more changes exponentially less likely):
      Fuzz(2);
  }
  
  //
  // CAddrDB
  //
  
  CAddrDB::CAddrDB()
  {
      pathAddr = GetDataDir() / "peers.dat";
  }
  
  bool CAddrDB::Write(const CAddrMan& addr)
  {
      // Generate random temporary filename
      unsigned short randv = 0;
      GetRandBytes((unsigned char*)&randv, sizeof(randv));
      std::string tmpfn = strprintf("peers.dat.%04x", randv);
  
      // serialize addresses, checksum data up to that point, then append csum
      CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
      ssPeers << FLATDATA(Params().MessageStart());
      ssPeers << addr;
      uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
      ssPeers << hash;
  
      // open output file, and associate with CAutoFile
      boost::filesystem::path pathAddr = GetDataDir() / "peers.dat";
      FILE* file = fopen(pathAddr.string().c_str(), "wb");
      CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
      if (fileout.IsNull())
          return error("%s : Failed to open file %s", __func__, pathAddr.string());
  
      // Write and commit header, data
      try {
          fileout << ssPeers;
      } catch (std::exception& e) {
          return error("%s : Serialize or I/O error - %s", __func__, e.what());
      }
      FileCommit(fileout.Get());
      fileout.fclose();
  
      return true;
  }
  
  bool CAddrDB::Read(CAddrMan& addr)
  {
      // open input file, and associate with CAutoFile
      FILE* file = fopen(pathAddr.string().c_str(), "rb");
      CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
      if (filein.IsNull())
          return error("%s : Failed to open file %s", __func__, pathAddr.string());
  
      // use file size to size memory buffer
      int fileSize = boost::filesystem::file_size(pathAddr);
      int dataSize = fileSize - sizeof(uint256);
      // Don't try to resize to a negative number if file is small
      if (dataSize < 0)
          dataSize = 0;
      vector<unsigned char> vchData;
      vchData.resize(dataSize);
      uint256 hashIn;
  
      // read data and checksum from file
      try {
          filein.read((char*)&vchData[0], dataSize);
          filein >> hashIn;
      } catch (std::exception& e) {
          return error("%s : Deserialize or I/O error - %s", __func__, e.what());
      }
      filein.fclose();
  
      CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
  
      // verify stored checksum matches input data
      uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
      if (hashIn != hashTmp)
          return error("%s : Checksum mismatch, data corrupted", __func__);
  
      unsigned char pchMsgTmp[4];
      try {
          // de-serialize file header (network specific magic number) and ..
          ssPeers >> FLATDATA(pchMsgTmp);
  
          // ... verify the network matches ours
          if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
              return error("%s : Invalid network magic number", __func__);
  
          // de-serialize address data into one CAddrMan object
          ssPeers >> addr;
      } catch (std::exception& e) {
          return error("%s : Deserialize or I/O error - %s", __func__, e.what());
      }
  
      return true;
  }
  
  unsigned int ReceiveFloodSize() { return 1000 * GetArg("-maxreceivebuffer", 5 * 1000); }
  unsigned int SendBufferSize() { return 1000 * GetArg("-maxsendbuffer", 1 * 1000); }
  
  CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), setAddrKnown(5000)
  {
      nServices = 0;
      hSocket = hSocketIn;
      nRecvVersion = INIT_PROTO_VERSION;
      nLastSend = 0;
      nLastRecv = 0;
      nSendBytes = 0;
      nRecvBytes = 0;
      nTimeConnected = GetTime();
      addr = addrIn;
      addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
      nVersion = 0;
      strSubVer = "";
      fWhitelisted = false;
      fOneShot = false;
      fClient = false; // set by version message
      fInbound = fInboundIn;
      fNetworkNode = false;
      fSuccessfullyConnected = false;
      fDisconnect = false;
      nRefCount = 0;
      nSendSize = 0;
      nSendOffset = 0;
      hashContinue = 0;
      nStartingHeight = -1;
      fGetAddr = false;
      fRelayTxes = false;
      setInventoryKnown.max_size(SendBufferSize() / 1000);
      pfilter = new CBloomFilter();
      nPingNonceSent = 0;
      nPingUsecStart = 0;
      nPingUsecTime = 0;
      fPingQueued = false;
      fObfuScationMaster = false;
  
      {
          LOCK(cs_nLastNodeId);
          id = nLastNodeId++;
      }
  
      if (fLogIPs)
          LogPrint("net", "Added connection to %s peer=%d\n", addrName, id);
      else
          LogPrint("net", "Added connection peer=%d\n", id);
  
      // Be shy and don't send version until we hear
      if (hSocket != INVALID_SOCKET && !fInbound)
          PushVersion();
  
      GetNodeSignals().InitializeNode(GetId(), this);
  }
  
  CNode::~CNode()
  {
      CloseSocket(hSocket);
  
      if (pfilter)
          delete pfilter;
  
      GetNodeSignals().FinalizeNode(GetId());
  }
  
  void CNode::AskFor(const CInv& inv)
  {
      if (mapAskFor.size() > MAPASKFOR_MAX_SZ)
          return;
      // We're using mapAskFor as a priority queue,
      // the key is the earliest time the request can be sent
      int64_t nRequestTime;
      limitedmap<CInv, int64_t>::const_iterator it = mapAlreadyAskedFor.find(inv);
      if (it != mapAlreadyAskedFor.end())
          nRequestTime = it->second;
      else
          nRequestTime = 0;
      LogPrint("net", "askfor %s  %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime / 1000000), id);
  
      // Make sure not to reuse time indexes to keep things in the same order
      int64_t nNow = GetTimeMicros() - 1000000;
      static int64_t nLastTime;
      ++nLastTime;
      nNow = std::max(nNow, nLastTime);
      nLastTime = nNow;
  
      // Each retry is 2 minutes after the last
      nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow);
      if (it != mapAlreadyAskedFor.end())
          mapAlreadyAskedFor.update(it, nRequestTime);
      else
          mapAlreadyAskedFor.insert(std::make_pair(inv, nRequestTime));
      mapAskFor.insert(std::make_pair(nRequestTime, inv));
  }
  
  void CNode::BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend)
  {
      ENTER_CRITICAL_SECTION(cs_vSend);
      assert(ssSend.size() == 0);
      ssSend << CMessageHeader(pszCommand, 0);
      LogPrint("net", "sending: %s ", SanitizeString(pszCommand));
  }
  
  void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend)
  {
      ssSend.clear();
  
      LEAVE_CRITICAL_SECTION(cs_vSend);
  
      LogPrint("net", "(aborted)\n");
  }
  
  void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend)
  {
      // The -*messagestest options are intentionally not documented in the help message,
      // since they are only used during development to debug the networking code and are
      // not intended for end-users.
      if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0) {
          LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n");
          AbortMessage();
          return;
      }
      if (mapArgs.count("-fuzzmessagestest"))
          Fuzz(GetArg("-fuzzmessagestest", 10));
  
      if (ssSend.size() == 0)
          return;
  
      // Set the size
      unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE;
      memcpy((char*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], &nSize, sizeof(nSize));
  
      // Set the checksum
      uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end());
      unsigned int nChecksum = 0;
      memcpy(&nChecksum, &hash, sizeof(nChecksum));
      assert(ssSend.size() >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum));
      memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum));
  
      LogPrint("net", "(%d bytes) peer=%d\n", nSize, id);
  
      std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData());
      ssSend.GetAndClear(*it);
      nSendSize += (*it).size();
  
      // If write queue empty, attempt "optimistic write"
      if (it == vSendMsg.begin())
          SocketSendData(this);
  
      LEAVE_CRITICAL_SECTION(cs_vSend);
  }
  