text: // Copyright (c) 2009-2010 Satoshi Nakamoto
  // Copyright (c) 2009-2012 The Bitcoin developers
  // Copyright (c) 2011-2012 Litecoin Developers
  // Distributed under the MIT/X11 software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "irc.h"
  #include "db.h"
  #include "net.h"
  #include "init.h"
  #include "strlcpy.h"
  #include "addrman.h"
  #include "ui_interface.h"
  
  #ifdef WIN32
  #include <string.h>
  #endif
  
  
  using namespace std;
  using namespace boost;
  
  static const int MAX_OUTBOUND_CONNECTIONS = 8;
  
  void ThreadMessageHandler2(void* parg);
  void ThreadSocketHandler2(void* parg);
  void ThreadOpenConnections2(void* parg);
  void ThreadOpenAddedConnections2(void* parg);
  
  void ThreadDNSAddressSeed2(void* parg);
  bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
  
  
  struct LocalServiceInfo {
      int nScore;
      int nPort;
  };
  
  //
  // Global state variables
  //
  bool fClient = false;
  bool fDiscover = true;
  
  uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK);
  static CCriticalSection cs_mapLocalHost;
  static map<CNetAddr, LocalServiceInfo> mapLocalHost;
  static bool vfReachable[NET_MAX] = {};
  static bool vfLimited[NET_MAX] = {};
  static CNode* pnodeLocalHost = NULL;
  uint64 nLocalHostNonce = 0;
  boost::array<int, THREAD_MAX> vnThreadsRunning;
  static std::vector<SOCKET> vhListenSocket;
  CAddrMan addrman;
  
  vector<CNode*> vNodes;
  CCriticalSection cs_vNodes;
  map<CInv, CDataStream> mapRelay;
  deque<pair<int64, CInv> > vRelayExpiration;
  CCriticalSection cs_mapRelay;
  map<CInv, int64> mapAlreadyAskedFor;
  
  static deque<string> vOneShots;
  CCriticalSection cs_vOneShots;
  
  set<CNetAddr> setservAddNodeAddresses;
  CCriticalSection cs_setservAddNodeAddresses;
  
  static CSemaphore *semOutbound = NULL;
  
  void AddOneShot(string strDest)
  {
      LOCK(cs_vOneShots);
      vOneShots.push_back(strDest);
  }
  
  unsigned short GetListenPort()
  {
      return (unsigned short)(GetArg("-port", GetDefaultPort()));
  }
  
  void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd)
  {
      // Filter out duplicate requests
      if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd)
          return;
      pindexLastGetBlocksBegin = pindexBegin;
      hashLastGetBlocksEnd = hashEnd;
  
      PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd);
  }
  
  // find 'best' local address for a particular peer
  bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
  {
      if (fNoListen)
          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
  CAddress GetLocalAddress(const CNetAddr *paddrPeer)
  {
      CAddress ret(CService("0.0.0.0",0),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 = "";
      loop
      {
          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)
          {
              if (fShutdown)
                  return false;
              if (nBytes < 0)
              {
                  int nErr = WSAGetLastError();
                  if (nErr == WSAEMSGSIZE)
                      continue;
                  if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS)
                  {
                      Sleep(10);
                      continue;
                  }
              }
              if (!strLine.empty())
                  return true;
              if (nBytes == 0)
              {
                  // socket closed
                  printf("socket closed\n");
                  return false;
              }
              else
              {
                  // socket error
                  int nErr = WSAGetLastError();
                  printf("recv failed: %d\n", nErr);
                  return false;
              }
          }
      }
  }
  
  // used when scores of local addresses may have changed
  // pushes better local address to peers
  void static AdvertizeLocal()
  {
      LOCK(cs_vNodes);
      BOOST_FOREACH(CNode* pnode, vNodes)
      {
          if (pnode->fSuccessfullyConnected)
          {
              CAddress addrLocal = GetLocalAddress(&pnode->addr);
              if (addrLocal.IsRoutable() && (CService)addrLocal != (CService)pnode->addrLocal)
              {
                  pnode->PushAddress(addrLocal);
                  pnode->addrLocal = addrLocal;
              }
          }
      }
  }
  
  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;
  
      printf("AddLocal(%s,%i)\n", addr.ToString().c_str(), nScore);
  
      {
          LOCK(cs_mapLocalHost);
          bool fAlready = mapLocalHost.count(addr) > 0;
          LocalServiceInfo &info = mapLocalHost[addr];
          if (!fAlready || nScore >= info.nScore) {
              info.nScore = nScore;
              info.nPort = addr.GetPort() + (fAlready ? 1 : 0);
          }
          SetReachable(addr.GetNetwork());
      }
  
      AdvertizeLocal();
  
      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++;
      }
  
      AdvertizeLocal();
  
      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 address is in a network we can probably connect to */
  bool IsReachable(const CNetAddr& addr)
  {
      LOCK(cs_mapLocalHost);
      enum Network net = addr.GetNetwork();
      return vfReachable[net] && !vfLimited[net];
  }
  
  bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet)
  {
      SOCKET hSocket;
      if (!ConnectSocket(addrConnect, hSocket))
          return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString().c_str());
  
      send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL);
  
      string strLine;
      while (RecvLine(hSocket, strLine))
      {
          if (strLine.empty()) // HTTP response is separated from headers by blank line
          {
              loop
              {
                  if (!RecvLine(hSocket, strLine))
                  {
                      closesocket(hSocket);
                      return false;
                  }
                  if (pszKeyword == NULL)
                      break;
                  if (strLine.find(pszKeyword) != string::npos)
                  {
                      strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword));
                      break;
                  }
              }
              closesocket(hSocket);
              if (strLine.find("<") != string::npos)
                  strLine = strLine.substr(0, strLine.find("<"));
              strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r"));
              while (strLine.size() > 0 && isspace(strLine[strLine.size()-1]))
                  strLine.resize(strLine.size()-1);
              CService addr(strLine,0,true);
              printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str());
              if (!addr.IsValid() || !addr.IsRoutable())
                  return false;
              ipRet.SetIP(addr);
              return true;
          }
      }
      closesocket(hSocket);
      return error("GetMyExternalIP() : connection closed");
  }
  
  // We now get our external IP from the IRC server first and only use this as a backup
  bool GetMyExternalIP(CNetAddr& ipRet)
  {
      CService addrConnect;
      const char* pszGet;
      const char* pszKeyword;
  
      for (int nLookup = 0; nLookup <= 1; nLookup++)
      for (int nHost = 1; nHost <= 2; nHost++)
      {
          // We should be phasing out our use of sites like these.  If we need
          // replacements, we should ask for volunteers to put this simple
          // php file on their webserver that prints the client IP:
          //  <?php echo $_SERVER["REMOTE_ADDR"]; ?>
          if (nHost == 1)
          {
              addrConnect = CService("91.198.22.70",80); // checkip.dyndns.org
  
              if (nLookup == 1)
              {
                  CService addrIP("checkip.dyndns.org", 80, true);
                  if (addrIP.IsValid())
                      addrConnect = addrIP;
              }
  
              pszGet = "GET / HTTP/1.1\r\n"
                       "Host: checkip.dyndns.org\r\n"
                       "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n"
                       "Connection: close\r\n"
                       "\r\n";
  
              pszKeyword = "Address:";
          }
          else if (nHost == 2)
          {
              addrConnect = CService("74.208.43.192", 80); // www.showmyip.com
  
              if (nLookup == 1)
              {
                  CService addrIP("www.showmyip.com", 80, true);
                  if (addrIP.IsValid())
                      addrConnect = addrIP;
              }
  
              pszGet = "GET /simple/ HTTP/1.1\r\n"
                       "Host: www.showmyip.com\r\n"
                       "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n"
                       "Connection: close\r\n"
                       "\r\n";
  
              pszKeyword = NULL; // Returns just IP address
          }
  
          if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet))
              return true;
      }
  
      return false;
  }
  
  void ThreadGetMyExternalIP(void* parg)
  {
      // Make this thread recognisable as the external IP detection thread
      RenameThread("bitcoin-ext-ip");
  
      CNetAddr addrLocalHost;
      if (GetMyExternalIP(addrLocalHost))
      {
          printf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str());
          AddLocal(addrLocalHost, LOCAL_HTTP);
      }
  }
  
  
  
  
  
  void AddressCurrentlyConnected(const CService& addr)
  {
      addrman.Connected(addr);
  }
  
  
  
  
  
  
  
  CNode* FindNode(const CNetAddr& ip)
  {
      {
          LOCK(cs_vNodes);
          BOOST_FOREACH(CNode* pnode, vNodes)
              if ((CNetAddr)pnode->addr == ip)
                  return (pnode);
      }
      return NULL;
  }
  
  CNode* FindNode(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 ((CService)pnode->addr == addr)
                  return (pnode);
      }
      return NULL;
  }
  
  CNode* ConnectNode(CAddress addrConnect, const char *pszDest, int64 nTimeout)
  {
      if (pszDest == NULL) {
          if (IsLocal(addrConnect))
              return NULL;
  
          // Look for an existing connection
          CNode* pnode = FindNode((CService)addrConnect);
          if (pnode)
          {
              if (nTimeout != 0)
                  pnode->AddRef(nTimeout);
              else
                  pnode->AddRef();
              return pnode;
          }
      }
  
  
      /// debug print
      printf("trying connection %s lastseen=%.1fhrs\n",
          pszDest ? pszDest : addrConnect.ToString().c_str(),
          pszDest ? 0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
  
      // Connect
      SOCKET hSocket;
      if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, GetDefaultPort()) : ConnectSocket(addrConnect, hSocket))
      {
          addrman.Attempt(addrConnect);
  
          /// debug print
          printf("connected %s\n", pszDest ? pszDest : addrConnect.ToString().c_str());
  
          // Set to nonblocking
  #ifdef WIN32
          u_long nOne = 1;
          if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR)
              printf("ConnectSocket() : ioctlsocket nonblocking setting failed, error %d\n", WSAGetLastError());
  #else
          if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
              printf("ConnectSocket() : fcntl nonblocking setting failed, error %d\n", errno);
  #endif
  
          // Add node
          CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false);
          if (nTimeout != 0)
              pnode->AddRef(nTimeout);
          else
              pnode->AddRef();
  
          {
              LOCK(cs_vNodes);
              vNodes.push_back(pnode);
          }
  
          pnode->nTimeConnected = GetTime();
          return pnode;
      }
      else
      {
          return NULL;
      }
  }
  
  void CNode::CloseSocketDisconnect()
  {
      fDisconnect = true;
      if (hSocket != INVALID_SOCKET)
      {
          printf("disconnecting node %s\n", addrName.c_str());
          closesocket(hSocket);
          hSocket = INVALID_SOCKET;
          vRecv.clear();
      }
  }
  
  void CNode::Cleanup()
  {
  }
  
  
  void CNode::PushVersion()
  {
      /// when NTP implemented, change to just nTime = GetAdjustedTime()
      int64 nTime = (fInbound ? GetAdjustedTime() : GetTime());
      CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0)));
      CAddress addrMe = GetLocalAddress(&addr);
      RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
      printf("send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString().c_str(), addrYou.ToString().c_str(), addr.ToString().c_str());
      PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
                  nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight);
  }
  
  
  
  
  
  std::map<CNetAddr, int64> 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>::iterator i = setBanned.find(ip);
          if (i != setBanned.end())
          {
              int64 t = (*i).second;
              if (GetTime() < t)
                  fResult = true;
          }
      }
      return fResult;
  }
  
  bool CNode::Misbehaving(int howmuch)
  {
      if (addr.IsLocal())
      {
          printf("Warning: local node %s misbehaving\n", addrName.c_str());
          return false;
      }
  
      nMisbehavior += howmuch;
      if (nMisbehavior >= GetArg("-banscore", 100))
      {
          int64 banTime = GetTime()+GetArg("-bantime", 60*60*24);  // Default 24-hour ban
          {
              LOCK(cs_setBanned);
              if (setBanned[addr] < banTime)
                  setBanned[addr] = banTime;
          }
          CloseSocketDisconnect();
          printf("Disconnected %s for misbehavior (score=%d)\n", addrName.c_str(), nMisbehavior);
          return true;
      }
      return false;
  }
  
  #undef X
  #define X(name) stats.name = name
  void CNode::copyStats(CNodeStats &stats)
  {
      X(nServices);
      X(nLastSend);
      X(nLastRecv);
      X(nTimeConnected);
      X(addrName);
      X(nVersion);
      X(strSubVer);
      X(fInbound);
      X(nReleaseTime);
      X(nStartingHeight);
      X(nMisbehavior);
  }
  #undef X
  
  
  
  
  
  
  
  
  
  
  void ThreadSocketHandler(void* parg)
  {
      IMPLEMENT_RANDOMIZE_STACK(ThreadSocketHandler(parg));
  
      // Make this thread recognisable as the networking thread
      RenameThread("bitcoin-net");
  
      try
      {
          vnThreadsRunning[THREAD_SOCKETHANDLER]++;
          ThreadSocketHandler2(parg);
          vnThreadsRunning[THREAD_SOCKETHANDLER]--;
      }
      catch (std::exception& e) {
          vnThreadsRunning[THREAD_SOCKETHANDLER]--;
          PrintException(&e, "ThreadSocketHandler()");
      } catch (...) {
          vnThreadsRunning[THREAD_SOCKETHANDLER]--;
          throw; // support pthread_cancel()
      }
      printf("ThreadSocketHandler exited\n");
  }
  
  void ThreadSocketHandler2(void* parg)
  {
      printf("ThreadSocketHandler started\n");
      list<CNode*> vNodesDisconnected;
      unsigned int nPrevNodeCount = 0;
  
      loop
      {
          //
          // Disconnect nodes
          //
          {
              LOCK(cs_vNodes);
              // Disconnect unused nodes
              vector<CNode*> vNodesCopy = vNodes;
              BOOST_FOREACH(CNode* pnode, vNodesCopy)
              {
                  if (pnode->fDisconnect ||
                      (pnode->GetRefCount() <= 0 && pnode->vRecv.empty() && pnode->vSend.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();
                      pnode->Cleanup();
  
                      // hold in disconnected pool until all refs are released
                      pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 15 * 60);
                      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_vRecv, lockRecv);
                              if (lockRecv)
                              {
                                  TRY_LOCK(pnode->cs_mapRequests, lockReq);
                                  if (lockReq)
                                  {
                                      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(vNodes.size());
          }
  
  
          //
          // Find which sockets have data to receive
          //
          struct timeval timeout;
          timeout.tv_sec  = 0;
          timeout.tv_usec = 50000; // 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;
  
          BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) {
              FD_SET(hListenSocket, &fdsetRecv);
              hSocketMax = max(hSocketMax, hListenSocket);
          }
          {
              LOCK(cs_vNodes);
              BOOST_FOREACH(CNode* pnode, vNodes)
              {
                  if (pnode->hSocket == INVALID_SOCKET)
                      continue;
                  FD_SET(pnode->hSocket, &fdsetRecv);
                  FD_SET(pnode->hSocket, &fdsetError);
                  hSocketMax = max(hSocketMax, pnode->hSocket);
                  {
                      TRY_LOCK(pnode->cs_vSend, lockSend);
                      if (lockSend && !pnode->vSend.empty())
                          FD_SET(pnode->hSocket, &fdsetSend);
                  }
              }
          }
  
          vnThreadsRunning[THREAD_SOCKETHANDLER]--;
          int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
          vnThreadsRunning[THREAD_SOCKETHANDLER]++;
          if (fShutdown)
              return;
          if (nSelect == SOCKET_ERROR)
          {
              int nErr = WSAGetLastError();
              if (hSocketMax != INVALID_SOCKET)
              {
                  printf("socket select error %d\n", nErr);
                  for (unsigned int i = 0; i <= hSocketMax; i++)
                      FD_SET(i, &fdsetRecv);
              }
              FD_ZERO(&fdsetSend);
              FD_ZERO(&fdsetError);
              Sleep(timeout.tv_usec/1000);
          }
  
  
          //
          // Accept new connections
          //
          BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
          if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv))
          {
  #ifdef USE_IPV6
              struct sockaddr_storage sockaddr;
  #else
              struct sockaddr sockaddr;
  #endif
              socklen_t len = sizeof(sockaddr);
              SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len);
              CAddress addr;
              int nInbound = 0;
  
              if (hSocket != INVALID_SOCKET)
                  if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
                      printf("warning: unknown socket family\n");
  
              {
                  LOCK(cs_vNodes);
                  BOOST_FOREACH(CNode* pnode, vNodes)
                      if (pnode->fInbound)
                          nInbound++;
              }
  
              if (hSocket == INVALID_SOCKET)
              {
                  if (WSAGetLastError() != WSAEWOULDBLOCK)
                      printf("socket error accept failed: %d\n", WSAGetLastError());
              }
              else if (nInbound >= GetArg("-maxconnections", 125) - MAX_OUTBOUND_CONNECTIONS)
              {
                  {
                      LOCK(cs_setservAddNodeAddresses);
                      if (!setservAddNodeAddresses.count(addr))
                          closesocket(hSocket);
                  }
              }
              else if (CNode::IsBanned(addr))
              {
                  printf("connection from %s dropped (banned)\n", addr.ToString().c_str());
                  closesocket(hSocket);
              }
              else
              {
                  printf("accepted connection %s\n", addr.ToString().c_str());
                  CNode* pnode = new CNode(hSocket, addr, "", true);
                  pnode->AddRef();
                  {
                      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)
          {
              if (fShutdown)
                  return;
  
              //
              // Receive
              //
              if (pnode->hSocket == INVALID_SOCKET)
                  continue;
              if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError))
              {
                  TRY_LOCK(pnode->cs_vRecv, lockRecv);
                  if (lockRecv)
                  {
                      CDataStream& vRecv = pnode->vRecv;
                      unsigned int nPos = vRecv.size();
  
                      if (nPos > ReceiveBufferSize()) {
                          if (!pnode->fDisconnect)
                              printf("socket recv flood control disconnect (%d bytes)\n", vRecv.size());
                          pnode->CloseSocketDisconnect();
                      }
                      else {
                          // typical socket buffer is 8K-64K
                          char pchBuf[0x10000];
                          int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
                          if (nBytes > 0)
                          {
                              vRecv.resize(nPos + nBytes);
                              memcpy(&vRecv[nPos], pchBuf, nBytes);
                              pnode->nLastRecv = GetTime();
                          }
                          else if (nBytes == 0)
                          {
                              // socket closed gracefully
                              if (!pnode->fDisconnect)
                                  printf("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)
                                      printf("socket recv error %d\n", nErr);
                                  pnode->CloseSocketDisconnect();
                              }
                          }
                      }
                  }
              }
  
              //
              // Send
              //
              if (pnode->hSocket == INVALID_SOCKET)
                  continue;
              if (FD_ISSET(pnode->hSocket, &fdsetSend))
              {
                  TRY_LOCK(pnode->cs_vSend, lockSend);
                  if (lockSend)
                  {
                      CDataStream& vSend = pnode->vSend;
                      if (!vSend.empty())
                      {
                          int nBytes = send(pnode->hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL | MSG_DONTWAIT);
                          if (nBytes > 0)
                          {
                              vSend.erase(vSend.begin(), vSend.begin() + nBytes);
                              pnode->nLastSend = GetTime();
                          }
                          else if (nBytes < 0)
                          {
                              // error
                              int nErr = WSAGetLastError();
                              if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
                              {
                                  printf("socket send error %d\n", nErr);
                                  pnode->CloseSocketDisconnect();
                              }
                          }
                      }
                  }
              }
  
              //
              // Inactivity checking
              //
              if (pnode->vSend.empty())
                  pnode->nLastSendEmpty = GetTime();
              if (GetTime() - pnode->nTimeConnected > 60)
              {
                  if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
                  {
                      printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0);
                      pnode->fDisconnect = true;
                  }
                  else if (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60)
                  {
                      printf("socket not sending\n");
                      pnode->fDisconnect = true;
                  }
                  else if (GetTime() - pnode->nLastRecv > 90*60)
                  {
                      printf("socket inactivity timeout\n");
                      pnode->fDisconnect = true;
                  }
              }
          }
          {
              LOCK(cs_vNodes);
              BOOST_FOREACH(CNode* pnode, vNodesCopy)
                  pnode->Release();
          }
  
          Sleep(10);
      }
  }
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  // DNS seeds
  // Each pair gives a source name and a seed name.
  // The first name is used as information source for addrman.
  // The second name should resolve to a list of seed addresses.
  static const char *strDNSSeed[][2] = {
      {"snekcoin.com", "node.snekcoin.com"},
  };
  
  void ThreadDNSAddressSeed(void* parg)
  {
      IMPLEMENT_RANDOMIZE_STACK(ThreadDNSAddressSeed(parg));
  
      // Make this thread recognisable as the DNS seeding thread
      RenameThread("bitcoin-dnsseed");
  
      try
      {
          vnThreadsRunning[THREAD_DNSSEED]++;
          ThreadDNSAddressSeed2(parg);
          vnThreadsRunning[THREAD_DNSSEED]--;
      }
      catch (std::exception& e) {
          vnThreadsRunning[THREAD_DNSSEED]--;
          PrintException(&e, "ThreadDNSAddressSeed()");
      } catch (...) {
          vnThreadsRunning[THREAD_DNSSEED]--;
          throw; // support pthread_cancel()
      }
      printf("ThreadDNSAddressSeed exited\n");
  }
  
  void ThreadDNSAddressSeed2(void* parg)
  {
      printf("ThreadDNSAddressSeed started\n");
      int found = 0;
  
      if (!fTestNet)
      {
          printf("Loading addresses from DNS seeds (could take a while)\n");
  
          for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) {
              if (GetNameProxy()) {
                  AddOneShot(strDNSSeed[seed_idx][1]);
              } else {
                  vector<CNetAddr> vaddr;
                  vector<CAddress> vAdd;
                  if (LookupHost(strDNSSeed[seed_idx][1], vaddr))
                  {
                      BOOST_FOREACH(CNetAddr& ip, vaddr)
                      {
                          int nOneDay = 24*3600;
                          CAddress addr = CAddress(CService(ip, 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(strDNSSeed[seed_idx][0], true));
              }
          }
      }
  
      printf("%d addresses found from DNS seeds\n", found);
  }
  
  
  
  
  
  
  
  
  
  
  
  
  unsigned int pnSeed[] =
  {
      0x2EFDCB71, 0xCC1B3AD6, 0xADA77149,
  };
  
  void DumpAddresses()
  {
      int64 nStart = GetTimeMillis();
  
      CAddrDB adb;
      adb.Write(addrman);
  
      printf("Flushed %d addresses to peers.dat  %"PRI64d"ms\n",
             addrman.size(), GetTimeMillis() - nStart);
  }
  
  void ThreadDumpAddress2(void* parg)
  {
      vnThreadsRunning[THREAD_DUMPADDRESS]++;
      while (!fShutdown)
      {
          DumpAddresses();
          vnThreadsRunning[THREAD_DUMPADDRESS]--;
          Sleep(100000);
          vnThreadsRunning[THREAD_DUMPADDRESS]++;
      }
      vnThreadsRunning[THREAD_DUMPADDRESS]--;
  }
  
  void ThreadDumpAddress(void* parg)
  {
      IMPLEMENT_RANDOMIZE_STACK(ThreadDumpAddress(parg));
  
      // Make this thread recognisable as the address dumping thread
      RenameThread("bitcoin-adrdump");
  
      try
      {
          ThreadDumpAddress2(parg);
      }
      catch (std::exception& e) {
          PrintException(&e, "ThreadDumpAddress()");
      }
      printf("ThreadDumpAddress exited\n");
  }
  
  void ThreadOpenConnections(void* parg)
  {
      IMPLEMENT_RANDOMIZE_STACK(ThreadOpenConnections(parg));
  
      // Make this thread recognisable as the connection opening thread
      RenameThread("bitcoin-opencon");
  
      try
      {
          vnThreadsRunning[THREAD_OPENCONNECTIONS]++;
          ThreadOpenConnections2(parg);
          vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
      }
      catch (std::exception& e) {
          vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
          PrintException(&e, "ThreadOpenConnections()");
      } catch (...) {
          vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
          PrintException(NULL, "ThreadOpenConnections()");
      }
      printf("ThreadOpenConnections exited\n");
  }
  
  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 ThreadOpenConnections2(void* parg)
  {
      printf("ThreadOpenConnections started\n");
  
      // Connect to specific addresses
      if (mapArgs.count("-connect"))
      {
          for (int64 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++)
                  {
                      Sleep(500);
                      if (fShutdown)
                          return;
                  }
              }
          }
      }
  
      // Initiate network connections
      int64 nStart = GetTime();
      loop
      {
          ProcessOneShot();
  
          vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
          Sleep(500);
          vnThreadsRunning[THREAD_OPENCONNECTIONS]++;
          if (fShutdown)
              return;
  
  
          vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
          CSemaphoreGrant grant(*semOutbound);
          vnThreadsRunning[THREAD_OPENCONNECTIONS]++;
          if (fShutdown)
              return;
  
          // Add seed nodes if IRC isn't working
          if (addrman.size()==0 && (GetTime() - nStart > 60) && !fTestNet)
          {
              std::vector<CAddress> vAdd;
              for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
              {
                  // It'll only connect to one or two seed nodes because once it connects,
                  // it'll get a pile of addresses with newer timestamps.
                  // Seed nodes are given a random 'last seen time' of between one and two
                  // weeks ago.
                  const int64 nOneWeek = 7*24*60*60;
                  struct in_addr ip;
                  memcpy(&ip, &pnSeed[i], sizeof(ip));
                  CAddress addr(CService(ip, GetDefaultPort()));
                  addr.nTime = GetTime()-GetRand(nOneWeek)-nOneWeek;
                  vAdd.push_back(addr);
              }
              addrman.Add(vAdd, CNetAddr("127.0.0.1"));
          }
  
          //
          // 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 nANow = GetAdjustedTime();
  
          int nTries = 0;
          loop
          {
              // use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections)
              CAddress addr = addrman.Select(10 + min(nOutbound,8)*10);
  
              // if we selected an invalid address, restart
              if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
                  break;
  
              nTries++;
  
              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() != GetDefaultPort() && nTries < 50)
                  continue;
  
              addrConnect = addr;
              break;
          }
  
          if (addrConnect.IsValid())
              OpenNetworkConnection(addrConnect, &grant);
      }
  }
  
  void ThreadOpenAddedConnections(void* parg)
  {
      IMPLEMENT_RANDOMIZE_STACK(ThreadOpenAddedConnections(parg));
  
      // Make this thread recognisable as the connection opening thread
      RenameThread("bitcoin-opencon");
  
      try
      {
          vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++;
          ThreadOpenAddedConnections2(parg);
          vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--;
      }
      catch (std::exception& e) {
          vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--;
          PrintException(&e, "ThreadOpenAddedConnections()");
      } catch (...) {
          vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--;
          PrintException(NULL, "ThreadOpenAddedConnections()");
      }
      printf("ThreadOpenAddedConnections exited\n");
  }
  
  void ThreadOpenAddedConnections2(void* parg)
  {
      printf("ThreadOpenAddedConnections started\n");
  
      if (mapArgs.count("-addnode") == 0)
          return;
  
      if (GetNameProxy()) {
          while(!fShutdown) {
              BOOST_FOREACH(string& strAddNode, mapMultiArgs["-addnode"]) {
                  CAddress addr;
                  CSemaphoreGrant grant(*semOutbound);
                  OpenNetworkConnection(addr, &grant, strAddNode.c_str());
                  Sleep(500);
              }
              vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--;
              Sleep(120000); // Retry every 2 minutes
              vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++;
          }
          return;
      }
  
      vector<vector<CService> > vservAddressesToAdd(0);
      BOOST_FOREACH(string& strAddNode, mapMultiArgs["-addnode"])
      {
          vector<CService> vservNode(0);
          if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0))
          {
              vservAddressesToAdd.push_back(vservNode);
              {
                  LOCK(cs_setservAddNodeAddresses);
                  BOOST_FOREACH(CService& serv, vservNode)
                      setservAddNodeAddresses.insert(serv);
              }
          }
      }
      loop
      {
          vector<vector<CService> > vservConnectAddresses = vservAddressesToAdd;
          // 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 (vector<vector<CService> >::iterator it = vservConnectAddresses.begin(); it != vservConnectAddresses.end(); it++)
                      BOOST_FOREACH(CService& addrNode, *(it))
                          if (pnode->addr == addrNode)
                          {
                              it = vservConnectAddresses.erase(it);
                              it--;
                              break;
                          }
          }
          BOOST_FOREACH(vector<CService>& vserv, vservConnectAddresses)
          {
              CSemaphoreGrant grant(*semOutbound);
              OpenNetworkConnection(CAddress(*(vserv.begin())), &grant);
              Sleep(500);
              if (fShutdown)
                  return;
          }
          if (fShutdown)
              return;
          vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--;
          Sleep(120000); // Retry every 2 minutes
          vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++;
          if (fShutdown)
              return;
      }
  }
  
  // if succesful, this moves the passed grant to the constructed node
  bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *strDest, bool fOneShot)
  {
      //
      // Initiate outbound network connection
      //
      if (fShutdown)
          return false;
      if (!strDest)
          if (IsLocal(addrConnect) ||
              FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) ||
              FindNode(addrConnect.ToStringIPPort().c_str()))
              return false;
      if (strDest && FindNode(strDest))
          return false;
  
      vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
      CNode* pnode = ConnectNode(addrConnect, strDest);
      vnThreadsRunning[THREAD_OPENCONNECTIONS]++;
      if (fShutdown)
          return false;
      if (!pnode)
          return false;
      if (grantOutbound)
          grantOutbound->MoveTo(pnode->grantOutbound);
      pnode->fNetworkNode = true;
      if (fOneShot)
          pnode->fOneShot = true;
  
      return true;
  }
  
  
  
  
  
  
  
  
  void ThreadMessageHandler(void* parg)
  {
      IMPLEMENT_RANDOMIZE_STACK(ThreadMessageHandler(parg));
  
      // Make this thread recognisable as the message handling thread
      RenameThread("bitcoin-msghand");
  
      try
      {
          vnThreadsRunning[THREAD_MESSAGEHANDLER]++;
          ThreadMessageHandler2(parg);
          vnThreadsRunning[THREAD_MESSAGEHANDLER]--;
      }
      catch (std::exception& e) {
          vnThreadsRunning[THREAD_MESSAGEHANDLER]--;
          PrintException(&e, "ThreadMessageHandler()");
      } catch (...) {
          vnThreadsRunning[THREAD_MESSAGEHANDLER]--;
          PrintException(NULL, "ThreadMessageHandler()");
      }
      printf("ThreadMessageHandler exited\n");
  }
  
  void ThreadMessageHandler2(void* parg)
  {
      printf("ThreadMessageHandler started\n");
      SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
      while (!fShutdown)
      {
          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())];
          BOOST_FOREACH(CNode* pnode, vNodesCopy)
          {
              // Receive messages
              {
                  TRY_LOCK(pnode->cs_vRecv, lockRecv);
                  if (lockRecv)
                      ProcessMessages(pnode);
              }
              if (fShutdown)
                  return;
  
              // Send messages
              {
                  TRY_LOCK(pnode->cs_vSend, lockSend);
                  if (lockSend)
                      SendMessages(pnode, pnode == pnodeTrickle);
              }
              if (fShutdown)
                  return;
          }
  
          {
              LOCK(cs_vNodes);
              BOOST_FOREACH(CNode* pnode, vNodesCopy)
                  pnode->Release();
          }
  
          // Wait and allow messages to bunch up.
          // Reduce vnThreadsRunning so StopNode has permission to exit while
          // we're sleeping, but we must always check fShutdown after doing this.
          vnThreadsRunning[THREAD_MESSAGEHANDLER]--;
          Sleep(100);
          if (fRequestShutdown)
              StartShutdown();
          vnThreadsRunning[THREAD_MESSAGEHANDLER]++;
          if (fShutdown)
              return;
      }
  }
  
  
  
  
  
  
  bool BindListenPort(const CService &addrBind, string& strError)
  {
      strError = "";
      int nOne = 1;
  
  #ifdef WIN32
      // Initialize Windows Sockets
      WSADATA wsadata;
      int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
      if (ret != NO_ERROR)
      {
          strError = strprintf("Error: TCP/IP socket library failed to start (WSAStartup returned error %d)", ret);
          printf("%s\n", strError.c_str());
          return false;
      }
  #endif
  
      // Create socket for listening for incoming connections
  #ifdef USE_IPV6
      struct sockaddr_storage sockaddr;
  #else
      struct sockaddr sockaddr;
  #endif
      socklen_t len = sizeof(sockaddr);
      if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))
      {
          strError = strprintf("Error: bind address family for %s not supported", addrBind.ToString().c_str());
          printf("%s\n", strError.c_str());
          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 %d)", WSAGetLastError());
          printf("%s\n", strError.c_str());
          return false;
      }
  
  #ifdef SO_NOSIGPIPE
      // Different way of disabling SIGPIPE on BSD
      setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));
  #endif
  
  #ifndef WIN32
      // 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
  
  
  #ifdef WIN32
      // Set to nonblocking, incoming connections will also inherit this
      if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR)
  #else
      if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
  #endif
      {
          strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError());
          printf("%s\n", strError.c_str());
          return false;
      }
  
  #ifdef USE_IPV6
      // 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
          setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int));
  #endif
  #ifdef WIN32
          int nProtLevel = 10 /* PROTECTION_LEVEL_UNRESTRICTED */;
          int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */;
          // this call is allowed to fail
          setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (const char*)&nProtLevel, sizeof(int));
  #endif
      }
  #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. SnekCoin is probably already running."), addrBind.ToString().c_str());
          else
              strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %d, %s)"), addrBind.ToString().c_str(), nErr, strerror(nErr));
          printf("%s\n", strError.c_str());
          return false;
      }
      printf("Bound to %s\n", addrBind.ToString().c_str());
  
      // Listen for incoming connections
      if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)
      {
          strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError());
          printf("%s\n", strError.c_str());
          return false;
      }
  
      vhListenSocket.push_back(hListenSocket);
  
      if (addrBind.IsRoutable() && fDiscover)
          AddLocal(addrBind, LOCAL_BIND);
  
      return true;
  }
  
  void static Discover()
  {
      if (!fDiscover)
          return;
  
  #ifdef WIN32
      // Get local host ip
      char pszHostName[1000] = "";
      if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR)
      {
          vector<CNetAddr> vaddr;
          if (LookupHost(pszHostName, vaddr))
          {
              BOOST_FOREACH (const CNetAddr &addr, vaddr)
              {
                  AddLocal(addr, LOCAL_IF);
              }
          }
      }
  #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))
                      printf("IPv4 %s: %s\n", ifa->ifa_name, addr.ToString().c_str());
              }
  #ifdef USE_IPV6
              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))
                      printf("IPv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str());
              }
  #endif
          }
          freeifaddrs(myaddrs);
      }
  #endif
  
      CreateThread(ThreadGetMyExternalIP, NULL);
  }
  
  void StartNode(void* parg)
  {
      // Make this thread recognisable as the startup thread
      RenameThread("bitcoin-start");
  
      if (semOutbound == NULL) {
          // initialize semaphore
          int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, (int)GetArg("-maxconnections", 125));
          semOutbound = new CSemaphore(nMaxOutbound);
      }
  
      if (pnodeLocalHost == NULL)
          pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices));
  
      Discover();
  
      //
      // Start threads
      //
  
      if (!GetBoolArg("-dnsseed", true))
          printf("DNS seeding disabled\n");
      else
          if (!CreateThread(ThreadDNSAddressSeed, NULL))
              printf("Error: CreateThread(ThreadDNSAddressSeed) failed\n");
  
  
      // Get addresses from IRC and advertise ours
      if (!CreateThread(ThreadIRCSeed, NULL))
          printf("Error: CreateThread(ThreadIRCSeed) failed\n");
  
      // Send and receive from sockets, accept connections
      if (!CreateThread(ThreadSocketHandler, NULL))
          printf("Error: CreateThread(ThreadSocketHandler) failed\n");
  
      // Initiate outbound connections from -addnode
      if (!CreateThread(ThreadOpenAddedConnections, NULL))
          printf("Error: CreateThread(ThreadOpenAddedConnections) failed\n");
  
      // Initiate outbound connections
      if (!CreateThread(ThreadOpenConnections, NULL))
          printf("Error: CreateThread(ThreadOpenConnections) failed\n");
  
      // Process messages
      if (!CreateThread(ThreadMessageHandler, NULL))
          printf("Error: CreateThread(ThreadMessageHandler) failed\n");
  
      // Dump network addresses
      if (!CreateThread(ThreadDumpAddress, NULL))
          printf("Error; CreateThread(ThreadDumpAddress) failed\n");
  
      // Generate coins in the background
      GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain);
  }
  
  bool StopNode()
  {
      printf("StopNode()\n");
      fShutdown = true;
      nTransactionsUpdated++;
      int64 nStart = GetTime();
      if (semOutbound)
          for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
              semOutbound->post();
      do
      {
          int nThreadsRunning = 0;
          for (int n = 0; n < THREAD_MAX; n++)
              nThreadsRunning += vnThreadsRunning[n];
          if (nThreadsRunning == 0)
              break;
          if (GetTime() - nStart > 20)
              break;
          Sleep(20);
      } while(true);
      if (vnThreadsRunning[THREAD_SOCKETHANDLER] > 0) printf("ThreadSocketHandler still running\n");
      if (vnThreadsRunning[THREAD_OPENCONNECTIONS] > 0) printf("ThreadOpenConnections still running\n");
      if (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0) printf("ThreadMessageHandler still running\n");
      if (vnThreadsRunning[THREAD_MINER] > 0) printf("ThreadBitcoinMiner still running\n");
      if (vnThreadsRunning[THREAD_RPCLISTENER] > 0) printf("ThreadRPCListener still running\n");
      if (vnThreadsRunning[THREAD_RPCHANDLER] > 0) printf("ThreadsRPCServer still running\n");
  
  
      if (vnThreadsRunning[THREAD_DNSSEED] > 0) printf("ThreadDNSAddressSeed still running\n");
      if (vnThreadsRunning[THREAD_ADDEDCONNECTIONS] > 0) printf("ThreadOpenAddedConnections still running\n");
      if (vnThreadsRunning[THREAD_DUMPADDRESS] > 0) printf("ThreadDumpAddresses still running\n");
      while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCHANDLER] > 0)
          Sleep(20);
      Sleep(50);
      DumpAddresses();
      return true;
  }
  
  class CNetCleanup
  {
  public:
      CNetCleanup()
      {
      }
      ~CNetCleanup()
      {
          // Close sockets
          BOOST_FOREACH(CNode* pnode, vNodes)
              if (pnode->hSocket != INVALID_SOCKET)
                  closesocket(pnode->hSocket);
          BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
              if (hListenSocket != INVALID_SOCKET)
                  if (closesocket(hListenSocket) == SOCKET_ERROR)
                      printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError());
  
  #ifdef WIN32
          // Shutdown Windows Sockets
          WSACleanup();
  #endif
      }
  }
  instance_of_cnetcleanup;
  