Move DNS Seed lookup to a new thread.
authorMatt Corallo <matt@bluematt.me>
Mon, 21 Nov 2011 17:25:00 +0000 (12:25 -0500)
committerLuke Dashjr <luke-jr+git@utopios.org>
Fri, 2 Dec 2011 17:56:26 +0000 (12:56 -0500)
1  2 
src/init.cpp
src/net.cpp
src/net.h

diff --combined src/init.cpp
@@@ -4,7 -4,7 +4,7 @@@
  // file license.txt or http://www.opensource.org/licenses/mit-license.php.
  #include "headers.h"
  #include "db.h"
 -#include "rpc.h"
 +#include "bitcoinrpc.h"
  #include "net.h"
  #include "init.h"
  #include "strlcpy.h"
  #include <boost/filesystem/fstream.hpp>
  #include <boost/interprocess/sync/file_lock.hpp>
  
 +#if defined(BITCOIN_NEED_QT_PLUGINS) && !defined(_BITCOIN_QT_PLUGINS_INCLUDED)
 +#define _BITCOIN_QT_PLUGINS_INCLUDED
 +#define __INSURE__
 +#include <QtPlugin>
 +Q_IMPORT_PLUGIN(qcncodecs)
 +Q_IMPORT_PLUGIN(qjpcodecs)
 +Q_IMPORT_PLUGIN(qtwcodecs)
 +Q_IMPORT_PLUGIN(qkrcodecs)
 +#endif
 +
  using namespace std;
  using namespace boost;
  
@@@ -34,7 -24,7 +34,7 @@@ CWallet* pwalletMain
  
  void ExitTimeout(void* parg)
  {
 -#ifdef __WXMSW__
 +#ifdef WIN32
      Sleep(5000);
      ExitProcess(0);
  #endif
@@@ -90,7 -80,7 +90,7 @@@ void HandleSIGTERM(int
  //
  // Start
  //
 -#ifndef GUI
 +#if !defined(QT_GUI)
  int main(int argc, char* argv[])
  {
      bool fRet = false;
@@@ -131,10 -121,10 +131,10 @@@ bool AppInit2(int argc, char* argv[]
      // Disable confusing "helpful" text message on abort, ctrl-c
      _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
  #endif
 -#ifndef __WXMSW__
 +#ifndef WIN32
      umask(077);
  #endif
 -#ifndef __WXMSW__
 +#ifndef WIN32
      // Clean shutdown on SIGTERM
      struct sigaction sa;
      sa.sa_handler = HandleSIGTERM;
              "  -connect=<ip>    \t\t  " + _("Connect only to the specified node\n") +
              "  -nolisten        \t  "   + _("Don't accept connections from outside\n") +
              "  -nodnsseed       \t  "   + _("Don't bootstrap list of peers using DNS\n") +
 +            "  -banscore=<n>    \t  "   + _("Threshold for disconnecting misbehaving peers (default: 100)\n") +
 +            "  -bantime=<n>     \t  "   + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)\n") +
              "  -maxreceivebuffer=<n>\t  " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 10000)\n") +
              "  -maxsendbuffer=<n>\t  "   + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 10000)\n") +
  #ifdef USE_UPNP
  #ifdef GUI
              "  -server          \t\t  " + _("Accept command line and JSON-RPC commands\n") +
  #endif
 -#ifndef __WXMSW__
 +#ifndef WIN32
              "  -daemon          \t\t  " + _("Run in the background as a daemon and accept commands\n") +
  #endif
              "  -testnet         \t\t  " + _("Use the test network\n") +
          strUsage += string() +
              "  -?               \t\t  " + _("This help message\n");
  
          // Remove tabs
          strUsage.erase(std::remove(strUsage.begin(), strUsage.end(), '\t'), strUsage.end());
          fprintf(stderr, "%s", strUsage.c_str());
 -#endif
          return false;
      }
  
      fDebug = GetBoolArg("-debug");
      fAllowDNS = GetBoolArg("-dns");
  
 -#ifndef __WXMSW__
 +#ifndef WIN32
      fDaemon = GetBoolArg("-daemon");
  #else
      fDaemon = false;
          fServer = GetBoolArg("-server");
  
      /* force fServer when running without GUI */
 -#ifndef GUI
 +#if !defined(QT_GUI)
      fServer = true;
  #endif
 -
      fPrintToConsole = GetBoolArg("-printtoconsole");
      fPrintToDebugger = GetBoolArg("-printtodebugger");
  
      fNoListen = GetBoolArg("-nolisten") || fTOR;
      fLogTimestamps = GetBoolArg("-logtimestamps");
  
 +#ifndef QT_GUI
      for (int i = 1; i < argc; i++)
          if (!IsSwitchChar(argv[i][0]))
              fCommandLine = true;
          int ret = CommandLineRPC(argc, argv);
          exit(ret);
      }
 +#endif
  
 -#ifndef __WXMSW__
 +#ifndef WIN32
      if (fDaemon)
      {
          // Daemonize
          ShrinkDebugFile();
      printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
      printf("Bitcoin version %s\n", FormatFullVersion().c_str());
 -#ifdef GUI
 -    printf("OS version %s\n", ((string)wxGetOsDescription()).c_str());
 -    printf("System default language is %d %s\n", g_locale.GetSystemLanguage(), ((string)g_locale.GetSysName()).c_str());
 -    printf("Language file %s (%s)\n", (string("locale/") + (string)g_locale.GetCanonicalName() + "/LC_MESSAGES/bitcoin.mo").c_str(), ((string)g_locale.GetLocale()).c_str());
 -#endif
      printf("Default data directory %s\n", GetDefaultDataDir().c_str());
  
      if (GetBoolArg("-loadblockindextest"))
          return false;
      }
  
 -    //
 -    // Limit to single instance per user
 -    // Required to protect the database files if we're going to keep deleting log.*
 -    //
 -#if defined(__WXMSW__) && defined(GUI)
 -    // wxSingleInstanceChecker doesn't work on Linux
 -    wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH");
 -    for (int i = 0; i < strMutexName.size(); i++)
 -        if (!isalnum(strMutexName[i]))
 -            strMutexName[i] = '.';
 -    wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
 -    if (psingleinstancechecker->IsAnotherRunning())
 -    {
 -        printf("Existing instance found\n");
 -        unsigned int nStart = GetTime();
 -        loop
 -        {
 -            // Show the previous instance and exit
 -            HWND hwndPrev = FindWindowA("wxWindowClassNR", "Bitcoin");
 -            if (hwndPrev)
 -            {
 -                if (IsIconic(hwndPrev))
 -                    ShowWindow(hwndPrev, SW_RESTORE);
 -                SetForegroundWindow(hwndPrev);
 -                return false;
 -            }
 -
 -            if (GetTime() > nStart + 60)
 -                return false;
 -
 -            // Resume this instance if the other exits
 -            delete psingleinstancechecker;
 -            Sleep(1000);
 -            psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
 -            if (!psingleinstancechecker->IsAnotherRunning())
 -                break;
 -        }
 -    }
 -#endif
 -
      // Make sure only a single bitcoin process is using the data directory.
      string strLockFile = GetDataDir() + "/.lock";
      FILE* file = fopen(strLockFile.c_str(), "a"); // empty lock file; created if it doesn't exist.
      strErrors = "";
      int64 nStart;
  
 +    InitMessage(_("Loading addresses..."));
      printf("Loading addresses...\n");
      nStart = GetTimeMillis();
      if (!LoadAddresses())
          strErrors += _("Error loading addr.dat      \n");
      printf(" addresses   %15"PRI64d"ms\n", GetTimeMillis() - nStart);
  
 +    InitMessage(_("Loading block index..."));
      printf("Loading block index...\n");
      nStart = GetTimeMillis();
      if (!LoadBlockIndex())
          strErrors += _("Error loading blkindex.dat      \n");
      printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);
  
 +    InitMessage(_("Loading wallet..."));
      printf("Loading wallet...\n");
      nStart = GetTimeMillis();
      bool fFirstRun;
      }
      if (pindexBest != pindexRescan)
      {
 +        InitMessage(_("Rescanning..."));
          printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight);
          nStart = GetTimeMillis();
          pwalletMain->ScanForWalletTransactions(pindexRescan, true);
          printf(" rescan      %15"PRI64d"ms\n", GetTimeMillis() - nStart);
      }
  
 +    InitMessage(_("Done loading"));
      printf("Done loading\n");
  
          //// debug print
          }
      }
  
-     if (GetBoolArg("-nodnsseed"))
-         printf("DNS seeding disabled\n");
-     else
-         DNSAddressSeed();
      if (mapArgs.count("-paytxfee"))
      {
          if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee))
      }
  
      //
 -    // Create the main window and start the node
 +    // Start the node
      //
 -#ifdef GUI
 -    if (!fDaemon)
 -        CreateMainWindow();
 -#endif
 -
      if (!CheckDiskSpace())
          return false;
  
      RandAddSeedPerfmon();
  
      if (!CreateThread(StartNode, NULL))
 -        wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin");
 +        wxMessageBox(_("Error: CreateThread(StartNode) failed"), "Bitcoin");
  
      if (fServer)
          CreateThread(ThreadRPCServer, NULL);
  
 -#if defined(__WXMSW__) && defined(GUI)
 -    if (fFirstRun)
 -        SetStartOnSystemStartup(true);
 -#endif
 -
 -#ifndef GUI
 +#if !defined(QT_GUI)
      while (1)
          Sleep(5000);
  #endif
diff --combined src/net.cpp
@@@ -10,7 -10,7 +10,7 @@@
  #include "init.h"
  #include "strlcpy.h"
  
 -#ifdef __WXMSW__
 +#ifdef WIN32
  #include <string.h>
  #endif
  
@@@ -32,6 -32,7 +32,7 @@@ void ThreadOpenConnections2(void* parg)
  #ifdef USE_UPNP
  void ThreadMapPort2(void* parg);
  #endif
+ void ThreadDNSAddressSeed2(void* parg);
  bool OpenNetworkConnection(const CAddress& addrConnect);
  
  
@@@ -102,7 -103,7 +103,7 @@@ bool ConnectSocket(const CAddress& addr
      bool fProxy = (fUseProxy && addrConnect.IsRoutable());
      struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr());
  
 -#ifdef __WXMSW__
 +#ifdef WIN32
      u_long fNonblock = 1;
      if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
  #else
                  return false;
              }
              socklen_t nRetSize = sizeof(nRet);
 -#ifdef __WXMSW__
 +#ifdef WIN32
              if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR)
  #else
              if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR)
                  return false;
              }
          }
 -#ifdef __WXMSW__
 +#ifdef WIN32
          else if (WSAGetLastError() != WSAEISCONN)
  #else
          else
      CNode::ConnectNode immediately turns the socket back to non-blocking
      but we'll turn it back to blocking just in case
      */
 -#ifdef __WXMSW__
 +#ifdef WIN32
      fNonblock = 0;
      if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
  #else
@@@ -674,7 -675,7 +675,7 @@@ CNode* ConnectNode(CAddress addrConnect
          printf("connected %s\n", addrConnect.ToString().c_str());
  
          // Set to nonblocking
 -#ifdef __WXMSW__
 +#ifdef WIN32
          u_long nOne = 1;
          if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR)
              printf("ConnectSocket() : ioctlsocket nonblocking setting failed, error %d\n", WSAGetLastError());
@@@ -726,52 -727,6 +727,52 @@@ void CNode::Cleanup(
  }
  
  
 +std::map<unsigned int, int64> CNode::setBanned;
 +CCriticalSection CNode::cs_setBanned;
 +
 +void CNode::ClearBanned()
 +{
 +    setBanned.clear();
 +}
 +
 +bool CNode::IsBanned(unsigned int ip)
 +{
 +    bool fResult = false;
 +    CRITICAL_BLOCK(cs_setBanned)
 +    {
 +        std::map<unsigned int, 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", addr.ToString().c_str());
 +        return false;
 +    }
 +
 +    nMisbehavior += howmuch;
 +    if (nMisbehavior >= GetArg("-banscore", 100))
 +    {
 +        int64 banTime = GetTime()+GetArg("-bantime", 60*60*24);  // Default 24-hour ban
 +        CRITICAL_BLOCK(cs_setBanned)
 +            if (setBanned[addr.ip] < banTime)
 +                setBanned[addr.ip] = banTime;
 +        CloseSocketDisconnect();
 +        printf("Disconnected %s for misbehavior (score=%d)\n", addr.ToString().c_str(), nMisbehavior);
 +        return true;
 +    }
 +    return false;
 +}
 +
  
  
  
@@@ -942,11 -897,6 +943,11 @@@ void ThreadSocketHandler2(void* parg
              {
                  closesocket(hSocket);
              }
 +            else if (CNode::IsBanned(addr.ip))
 +            {
 +                printf("connetion from %s dropped (banned)\n", addr.ToString().c_str());
 +                closesocket(hSocket);
 +            }
              else
              {
                  printf("accepted connection %s\n", addr.ToString().c_str());
@@@ -1213,8 -1163,28 +1214,28 @@@ static const char *strDNSSeed[] = 
      "dnsseed.bluematt.me",
  };
  
- void DNSAddressSeed()
+ void ThreadDNSAddressSeed(void* parg)
  {
+     IMPLEMENT_RANDOMIZE_STACK(ThreadDNSAddressSeed(parg));
+     try
+     {
+         vnThreadsRunning[6]++;
+         ThreadDNSAddressSeed2(parg);
+         vnThreadsRunning[6]--;
+     }
+     catch (std::exception& e) {
+         vnThreadsRunning[6]--;
+         PrintException(&e, "ThreadDNSAddressSeed()");
+     } catch (...) {
+         vnThreadsRunning[6]--;
+         throw; // support pthread_cancel()
+     }
+     printf("ThreadDNSAddressSeed exiting\n");
+ }
+ void ThreadDNSAddressSeed2(void* parg)
+ {
+     printf("ThreadDNSAddressSeed started\n");
      int found = 0;
  
      if (!fTestNet)
  
  
  
  unsigned int pnSeed[] =
  {
      0x6884ac63, 0x3ffecead, 0x2919b953, 0x0942fe50, 0x7a1d922e, 0xcdd6734a, 0x953a5bb6, 0x2c46922e,
@@@ -1437,8 -1416,6 +1467,8 @@@ void ThreadOpenConnections2(void* parg
              BOOST_FOREACH(CNode* pnode, vNodes)
                  setConnected.insert(pnode->addr.ip & 0x0000ffff);
  
 +        int64 nANow = GetAdjustedTime();
 +
          CRITICAL_BLOCK(cs_mapAddresses)
          {
              BOOST_FOREACH(const PAIRTYPE(vector<unsigned char>, CAddress)& item, mapAddresses)
                  const CAddress& addr = item.second;
                  if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.ip & 0x0000ffff))
                      continue;
 -                int64 nSinceLastSeen = GetAdjustedTime() - addr.nTime;
 -                int64 nSinceLastTry = GetAdjustedTime() - addr.nLastTry;
 +                int64 nSinceLastSeen = nANow - addr.nTime;
 +                int64 nSinceLastTry = nANow - addr.nLastTry;
  
                  // Randomize the order in a deterministic way, putting the standard port first
                  int64 nRandomizer = (uint64)(nStart * 4951 + addr.nLastTry * 9567851 + addr.ip * 7789) % (2 * 60 * 60);
@@@ -1506,8 -1483,7 +1536,8 @@@ bool OpenNetworkConnection(const CAddre
      //
      if (fShutdown)
          return false;
 -    if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() || FindNode(addrConnect.ip))
 +    if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() ||
 +        FindNode(addrConnect.ip) || CNode::IsBanned(addrConnect.ip))
          return false;
  
      vnThreadsRunning[1]--;
@@@ -1611,7 -1587,7 +1641,7 @@@ bool BindListenPort(string& strError
      int nOne = 1;
      addrLocalHost.port = htons(GetListenPort());
  
 -#ifdef __WXMSW__
 +#ifdef WIN32
      // Initialize Windows Sockets
      WSADATA wsadata;
      int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
      setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));
  #endif
  
 -#ifndef __WXMSW__
 +#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 __WXMSW__
 +#ifdef WIN32
      // Set to nonblocking, incoming connections will also inherit this
      if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR)
  #else
@@@ -1690,7 -1666,7 +1720,7 @@@ void StartNode(void* parg
      if (pnodeLocalHost == NULL)
          pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress("127.0.0.1", 0, false, nLocalServices));
  
 -#ifdef __WXMSW__
 +#ifdef WIN32
      // Get local host ip
      char pszHostName[1000] = "";
      if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR)
      // Start threads
      //
  
+     if (GetBoolArg("-nodnsseed"))
+         printf("DNS seeding disabled\n");
+     else
+         if (!CreateThread(ThreadDNSAddressSeed, NULL))
+             printf("Error: CreateThread(ThreadDNSAddressSeed) failed\n");
      // Map ports with UPnP
      if (fHaveUPnP)
          MapPort(fUseUPnP);
@@@ -1803,6 -1785,7 +1839,7 @@@ bool StopNode(
      if (vnThreadsRunning[3] > 0) printf("ThreadBitcoinMiner still running\n");
      if (vnThreadsRunning[4] > 0) printf("ThreadRPCServer still running\n");
      if (fHaveUPnP && vnThreadsRunning[5] > 0) printf("ThreadMapPort still running\n");
+     if (vnThreadsRunning[6] > 0) printf("ThreadDNSAddressSeed still running\n");
      while (vnThreadsRunning[2] > 0 || vnThreadsRunning[4] > 0)
          Sleep(20);
      Sleep(50);
@@@ -1826,7 -1809,7 +1863,7 @@@ public
              if (closesocket(hListenSocket) == SOCKET_ERROR)
                  printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError());
  
 -#ifdef __WXMSW__
 +#ifdef WIN32
          // Shutdown Windows Sockets
          WSACleanup();
  #endif
diff --combined src/net.h
+++ b/src/net.h
@@@ -10,7 -10,7 +10,7 @@@
  #include <boost/foreach.hpp>
  #include <openssl/rand.h>
  
 -#ifndef __WXMSW__
 +#ifndef WIN32
  #include <arpa/inet.h>
  #endif
  
@@@ -40,7 -40,6 +40,6 @@@ CNode* ConnectNode(CAddress addrConnect
  void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1);
  bool AnySubscribed(unsigned int nChannel);
  void MapPort(bool fMapPort);
- void DNSAddressSeed();
  bool BindListenPort(std::string& strError=REF(std::string()));
  void StartNode(void* parg);
  bool StopNode();
@@@ -124,13 -123,6 +123,13 @@@ public
      bool fDisconnect;
  protected:
      int nRefCount;
 +
 +    // Denial-of-service detection/prevention
 +    // Key is ip address, value is banned-until-time
 +    static std::map<unsigned int, int64> setBanned;
 +    static CCriticalSection cs_setBanned;
 +    int nMisbehavior;
 +
  public:
      int64 nReleaseTime;
      std::map<uint256, CRequestTracker> mapRequests;
      // publish and subscription
      std::vector<char> vfSubscribe;
  
 -
      CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false)
      {
          nServices = 0;
          nStartingHeight = -1;
          fGetAddr = false;
          vfSubscribe.assign(256, false);
 +        nMisbehavior = 0;
  
          // Be shy and don't send version until we hear
          if (!fInbound)
@@@ -575,25 -567,6 +574,25 @@@ public
      void CancelSubscribe(unsigned int nChannel);
      void CloseSocketDisconnect();
      void Cleanup();
 +
 +
 +    // Denial-of-service detection/prevention
 +    // The idea is to detect peers that are behaving
 +    // badly and disconnect/ban them, but do it in a
 +    // one-coding-mistake-won't-shatter-the-entire-network
 +    // way.
 +    // IMPORTANT:  There should be nothing I can give a
 +    // node that it will forward on that will make that
 +    // node's peers drop it. If there is, an attacker
 +    // can isolate a node and/or try to split the network.
 +    // Dropping a node for sending stuff that is invalid
 +    // now but might be valid in a later version is also
 +    // dangerous, because it can cause a network split
 +    // between nodes running old code and nodes running
 +    // new code.
 +    static void ClearBanned(); // needed for unit testing
 +    static bool IsBanned(unsigned int ip);
 +    bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot
  };