diff options
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 2718 |
1 files changed, 1690 insertions, 1028 deletions
diff --git a/src/net.cpp b/src/net.cpp index c8cb17091..cff4c5450 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1,70 +1,96 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif -#include "irc.h" -#include "db.h" #include "net.h" -#include "init.h" -#include "strlcpy.h" + #include "addrman.h" +#include "chainparams.h" +#include "clientversion.h" +#include "consensus/consensus.h" +#include "crypto/common.h" +#include "hash.h" +#include "primitives/transaction.h" +#include "scheduler.h" #include "ui_interface.h" +#include "utilstrencodings.h" #ifdef WIN32 #include <string.h> +#else +#include <fcntl.h> #endif #ifdef USE_UPNP -#include <miniupnpc/miniwget.h> #include <miniupnpc/miniupnpc.h> +#include <miniupnpc/miniwget.h> #include <miniupnpc/upnpcommands.h> #include <miniupnpc/upnperrors.h> #endif -using namespace std; -using namespace boost; +#include <boost/filesystem.hpp> +#include <boost/thread.hpp> -static const int MAX_OUTBOUND_CONNECTIONS = 8; +// Dump addresses to peers.dat every 15 minutes (900s) +#define DUMP_ADDRESSES_INTERVAL 900 -void ThreadMessageHandler2(void* parg); -void ThreadSocketHandler2(void* parg); -void ThreadOpenConnections2(void* parg); -void ThreadOpenAddedConnections2(void* parg); -#ifdef USE_UPNP -void ThreadMapPort2(void* parg); +#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) +#define MSG_NOSIGNAL 0 #endif -void ThreadDNSAddressSeed2(void* parg); -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); +// 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 -struct LocalServiceInfo { - int nScore; - int nPort; -}; +using namespace std; + +namespace { + const int MAX_OUTBOUND_CONNECTIONS = 8; + + struct ListenSocket { + SOCKET socket; + bool whitelisted; + + ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {} + }; +} // // Global state variables // -bool fClient = false; -static bool fUseUPnP = false; -uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); -static CCriticalSection cs_mapLocalHost; -static map<CNetAddr, LocalServiceInfo> mapLocalHost; +bool fDiscover = true; +bool fListen = true; +uint64_t nLocalServices = NODE_NETWORK; +CCriticalSection cs_mapLocalHost; +map<CNetAddr, LocalServiceInfo> mapLocalHost; static bool vfReachable[NET_MAX] = {}; static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; -uint64 nLocalHostNonce = 0; -array<int, THREAD_MAX> vnThreadsRunning; -static std::vector<SOCKET> vhListenSocket; +uint64_t nLocalHostNonce = 0; +static std::vector<ListenSocket> vhListenSocket; CAddrMan addrman; +int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS; +bool fAddressesInitialized = false; +std::string strSubVersion; vector<CNode*> vNodes; CCriticalSection cs_vNodes; map<CInv, CDataStream> mapRelay; -deque<pair<int64, CInv> > vRelayExpiration; +deque<pair<int64_t, CInv> > vRelayExpiration; CCriticalSection cs_mapRelay; -map<CInv, int64> mapAlreadyAskedFor; +limitedmap<CInv, int64_t> mapAlreadyAskedFor(MAX_INV_SZ); static deque<string> vOneShots; CCriticalSection cs_vOneShots; @@ -72,9 +98,20 @@ 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; -void AddOneShot(string strDest) +// Signals for message handling +static CNodeSignals g_signals; +CNodeSignals& GetNodeSignals() { return g_signals; } + +void AddOneShot(const std::string& strDest) { LOCK(cs_vOneShots); vOneShots.push_back(strDest); @@ -82,24 +119,13 @@ void AddOneShot(string 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); + return (unsigned short)(GetArg("-port", Params().GetDefaultPort())); } // find 'best' local address for a particular peer bool GetLocal(CService& addr, const CNetAddr *paddrPeer) { - if (fUseProxy || mapArgs.count("-connect") || fNoListen) + if (!fListen) return false; int nBestScore = -1; @@ -121,88 +147,87 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer) return nBestScore >= 0; } +//! Convert the pnSeeds6 array into usable address objects. +static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn) +{ + // 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_t nOneWeek = 7*24*60*60; + std::vector<CAddress> vSeedsOut; + vSeedsOut.reserve(vSeedsIn.size()); + for (std::vector<SeedSpec6>::const_iterator i(vSeedsIn.begin()); i != vSeedsIn.end(); ++i) + { + struct in6_addr ip; + memcpy(&ip, i->addr, sizeof(ip)); + CAddress addr(CService(ip, i->port)); + addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek; + vSeedsOut.push_back(addr); + } + return vSeedsOut; +} + // 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",0),0); + CAddress ret(CService("0.0.0.0",GetListenPort()),0); CService addr; if (GetLocal(addr, paddrPeer)) { ret = CAddress(addr); - ret.nServices = nLocalServices; - ret.nTime = GetAdjustedTime(); } + ret.nServices = nLocalServices; + ret.nTime = GetAdjustedTime(); return ret; } -bool RecvLine(SOCKET hSocket, string& strLine) +int GetnScore(const CService& addr) { - strLine = ""; - loop + 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 AdvertizeLocal(CNode *pnode) +{ + if (fListen && pnode->fSuccessfullyConnected) { - char c; - int nBytes = recv(hSocket, &c, 1, 0); - if (nBytes > 0) + 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)) { - if (c == '\n') - continue; - if (c == '\r') - return true; - strLine += c; - if (strLine.size() >= 9000) - return true; + addrLocal.SetIP(pnode->addrLocal); } - else if (nBytes <= 0) + if (addrLocal.IsRoutable()) { - 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; - } + LogPrintf("AdvertizeLocal: advertizing address %s\n", addrLocal.ToString()); + pnode->PushAddress(addrLocal); } } } -// used when scores of local addresses may have changed -// pushes better local address to peers -void static AdvertizeLocal() +void SetReachable(enum Network net, bool fFlag) { - 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; - } - } - } + LOCK(cs_mapLocalHost); + vfReachable[net] = fFlag; + if (net == NET_IPV6 && fFlag) + vfReachable[NET_IPV4] = true; } // learn a new local address @@ -211,23 +236,25 @@ bool AddLocal(const CService& addr, int nScore) if (!addr.IsRoutable()) return false; - printf("AddLocal(%s,%i)\n", addr.ToString().c_str(), nScore); + 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; - info.nPort = addr.GetPort() + (fAlready ? 1 : 0); + info.nScore = nScore + (fAlready ? 1 : 0); + info.nPort = addr.GetPort(); } - enum Network net = addr.GetNetwork(); - vfReachable[net] = true; - if (net == NET_IPV6) vfReachable[NET_IPV4] = true; + SetReachable(addr.GetNetwork()); } - AdvertizeLocal(); - return true; } @@ -236,17 +263,32 @@ bool AddLocal(const CNetAddr &addr, int nScore) return AddLocal(CService(addr, GetListenPort()), nScore); } +bool RemoveLocal(const CService& addr) +{ + LOCK(cs_mapLocalHost); + LogPrintf("RemoveLocal(%s)\n", addr.ToString()); + mapLocalHost.erase(addr); + return true; +} + /** 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(const CNetAddr& addr) +bool IsLimited(enum Network net) { LOCK(cs_mapLocalHost); - return vfLimited[addr.GetNetwork()]; + return vfLimited[net]; +} + +bool IsLimited(const CNetAddr &addr) +{ + return IsLimited(addr.GetNetwork()); } /** vote for a local address */ @@ -258,12 +300,10 @@ bool SeenLocal(const CService& addr) return false; mapLocalHost[addr].nScore++; } - - AdvertizeLocal(); - return true; } + /** check whether a given address is potentially local */ bool IsLocal(const CService& addr) { @@ -271,160 +311,55 @@ bool IsLocal(const CService& addr) return mapLocalHost.count(addr) > 0; } -/** check whether a given address is in a network we can probably connect to */ -bool IsReachable(const CNetAddr& addr) +/** check whether a given network is one we can probably connect to */ +bool IsReachable(enum Network net) { 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; - - if (fNoListen||fUseProxy) - return false; - - 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) +/** check whether a given address is in a network we can probably connect to */ +bool IsReachable(const CNetAddr& addr) { - CNetAddr addrLocalHost; - if (GetMyExternalIP(addrLocalHost)) - { - printf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str()); - AddLocal(addrLocalHost, LOCAL_HTTP); - } + 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; - - - +uint64_t CNode::nMaxOutboundLimit = 0; +uint64_t CNode::nMaxOutboundTotalBytesSentInCycle = 0; +uint64_t CNode::nMaxOutboundTimeframe = 60*60*24; //1 day +uint64_t CNode::nMaxOutboundCycleStartTime = 0; CNode* FindNode(const CNetAddr& ip) { - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if ((CNetAddr)pnode->addr == ip) - return (pnode); - } + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if ((CNetAddr)pnode->addr == ip) + return (pnode); + return NULL; +} + +CNode* FindNode(const CSubNet& subNet) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (subNet.Match((CNetAddr)pnode->addr)) + return (pnode); return NULL; } -CNode* FindNode(std::string addrName) +CNode* FindNode(const std::string& addrName) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -435,16 +370,14 @@ CNode* FindNode(std::string addrName) CNode* FindNode(const CService& addr) { - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if ((CService)pnode->addr == addr) - return (pnode); - } + 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) +CNode* ConnectNode(CAddress addrConnect, const char *pszDest) { if (pszDest == NULL) { if (IsLocal(addrConnect)) @@ -454,45 +387,33 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, int64 nTimeout) CNode* pnode = FindNode((CService)addrConnect); if (pnode) { - if (nTimeout != 0) - pnode->AddRef(nTimeout); - else - pnode->AddRef(); + pnode->AddRef(); return pnode; } } - /// debug print - printf("trying connection %s lastseen=%.1fhrs\n", - pszDest ? pszDest : addrConnect.ToString().c_str(), - pszDest ? 0 : (double)(addrConnect.nTime - GetAdjustedTime())/3600.0); + LogPrint("net", "trying connection %s lastseen=%.1fhrs\n", + pszDest ? pszDest : addrConnect.ToString(), + pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); // Connect SOCKET hSocket; - if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, GetDefaultPort()) : ConnectSocket(addrConnect, hSocket)) + bool proxyConnectionFailed = false; + if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) : + ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) { - addrman.Attempt(addrConnect); - - /// debug print - printf("connected %s\n", pszDest ? pszDest : addrConnect.ToString().c_str()); + if (!IsSelectableSocket(hSocket)) { + LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); + CloseSocket(hSocket); + return NULL; + } - // 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 + addrman.Attempt(addrConnect); // Add node CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); - if (nTimeout != 0) - pnode->AddRef(nTimeout); - else - pnode->AddRef(); + pnode->AddRef(); { LOCK(cs_vNodes); @@ -500,12 +421,15 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, int64 nTimeout) } pnode->nTimeConnected = GetTime(); + 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); } - else - { - return NULL; - } + + return NULL; } void CNode::CloseSocketDisconnect() @@ -513,41 +437,45 @@ void CNode::CloseSocketDisconnect() fDisconnect = true; if (hSocket != INVALID_SOCKET) { - if (fDebug) - printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); - printf("disconnecting node %s\n", addrName.c_str()); - closesocket(hSocket); - hSocket = INVALID_SOCKET; - vRecv.clear(); + LogPrint("net", "disconnecting peer=%d\n", id); + CloseSocket(hSocket); } -} -void CNode::Cleanup() -{ + // 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() { - /// when NTP implemented, change to just nTime = GetAdjustedTime() - int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); - CAddress addrYou = (fUseProxy ? CAddress(CService("0.0.0.0",0)) : addr); + int nBestHeight = g_signals.GetHeight().get_value_or(0); + + int64_t 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)); + 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); + nLocalHostNonce, strSubVersion, nBestHeight, !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)); } -std::map<CNetAddr, int64> CNode::setBanned; +banmap_t CNode::setBanned; CCriticalSection CNode::cs_setBanned; +bool CNode::setBannedIsDirty; void CNode::ClearBanned() { + LOCK(cs_setBanned); setBanned.clear(); + setBannedIsDirty = true; } bool CNode::IsBanned(CNetAddr ip) @@ -555,78 +483,544 @@ bool CNode::IsBanned(CNetAddr ip) bool fResult = false; { LOCK(cs_setBanned); - std::map<CNetAddr, int64>::iterator i = setBanned.find(ip); - if (i != setBanned.end()) + for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++) { - int64 t = (*i).second; - if (GetTime() < t) + CSubNet subNet = (*it).first; + CBanEntry banEntry = (*it).second; + + if(subNet.Match(ip) && GetTime() < banEntry.nBanUntil) fResult = true; } } return fResult; } -bool CNode::Misbehaving(int howmuch) +bool CNode::IsBanned(CSubNet subnet) { - if (addr.IsLocal()) + bool fResult = false; { - printf("Warning: local node %s misbehaving\n", addrName.c_str()); - return false; + LOCK(cs_setBanned); + banmap_t::iterator i = setBanned.find(subnet); + if (i != setBanned.end()) + { + CBanEntry banEntry = (*i).second; + if (GetTime() < banEntry.nBanUntil) + fResult = true; + } } + return fResult; +} + +void CNode::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { + CSubNet subNet(addr); + Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); +} - nMisbehavior += howmuch; - if (nMisbehavior >= GetArg("-banscore", 100)) +void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { + CBanEntry banEntry(GetTime()); + banEntry.banReason = banReason; + if (bantimeoffset <= 0) { - int64 banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban + bantimeoffset = GetArg("-bantime", 60*60*24); // Default 24-hour ban + sinceUnixEpoch = false; + } + banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; + + + LOCK(cs_setBanned); + if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) + setBanned[subNet] = banEntry; + + setBannedIsDirty = true; +} + +bool CNode::Unban(const CNetAddr &addr) { + CSubNet subNet(addr); + return Unban(subNet); +} + +bool CNode::Unban(const CSubNet &subNet) { + LOCK(cs_setBanned); + if (setBanned.erase(subNet)) + { + setBannedIsDirty = true; + return true; + } + return false; +} + +void CNode::GetBanned(banmap_t &banMap) +{ + LOCK(cs_setBanned); + banMap = setBanned; //create a thread safe copy +} + +void CNode::SetBanned(const banmap_t &banMap) +{ + LOCK(cs_setBanned); + setBanned = banMap; + setBannedIsDirty = true; +} + +void CNode::SweepBanned() +{ + int64_t now = GetTime(); + + LOCK(cs_setBanned); + banmap_t::iterator it = setBanned.begin(); + while(it != setBanned.end()) + { + CBanEntry banEntry = (*it).second; + if(now > banEntry.nBanUntil) { - LOCK(cs_setBanned); - if (setBanned[addr] < banTime) - setBanned[addr] = banTime; + setBanned.erase(it++); + setBannedIsDirty = true; } - CloseSocketDisconnect(); - printf("Disconnected %s for misbehavior (score=%d)\n", addrName.c_str(), nMisbehavior); - return true; + else + ++it; + } +} + +bool CNode::BannedSetIsDirty() +{ + LOCK(cs_setBanned); + return setBannedIsDirty; +} + +void CNode::SetBannedSetDirty(bool dirty) +{ + LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag + setBannedIsDirty = dirty; +} + + +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(fRelayTxes); + X(nLastSend); + X(nLastRecv); + X(nTimeConnected); + X(nTimeOffset); + 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 (Bitcoin users should be well used to small numbers with many decimal places by now :) + stats.dPingTime = (((double)nPingUsecTime) / 1e6); + stats.dPingMin = (((double)nMinPingUsecTime) / 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(Params().MessageStart(), 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\n", 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; + +class CNodeRef { +public: + CNodeRef(CNode *pnode) : _pnode(pnode) { + LOCK(cs_vNodes); + _pnode->AddRef(); + } + + ~CNodeRef() { + LOCK(cs_vNodes); + _pnode->Release(); + } + + CNode& operator *() const {return *_pnode;}; + CNode* operator ->() const {return _pnode;}; + + CNodeRef& operator =(const CNodeRef& other) + { + if (this != &other) { + LOCK(cs_vNodes); + _pnode->Release(); + _pnode = other._pnode; + _pnode->AddRef(); + } + return *this; + } + CNodeRef(const CNodeRef& other): + _pnode(other._pnode) + { + LOCK(cs_vNodes); + _pnode->AddRef(); + } +private: + CNode *_pnode; +}; +static bool ReverseCompareNodeMinPingTime(const CNodeRef &a, const CNodeRef &b) +{ + return a->nMinPingUsecTime > b->nMinPingUsecTime; +} +static bool ReverseCompareNodeTimeConnected(const CNodeRef &a, const CNodeRef &b) +{ + return a->nTimeConnected > b->nTimeConnected; +} -void ThreadSocketHandler(void* parg) +class CompareNetGroupKeyed { - IMPLEMENT_RANDOMIZE_STACK(ThreadSocketHandler(parg)); - try + std::vector<unsigned char> vchSecretKey; +public: + CompareNetGroupKeyed() + { + vchSecretKey.resize(32, 0); + GetRandBytes(vchSecretKey.data(), vchSecretKey.size()); + } + + bool operator()(const CNodeRef &a, const CNodeRef &b) + { + std::vector<unsigned char> vchGroupA, vchGroupB; + CSHA256 hashA, hashB; + std::vector<unsigned char> vchA(32), vchB(32); + + vchGroupA = a->addr.GetGroup(); + vchGroupB = b->addr.GetGroup(); + + hashA.Write(begin_ptr(vchGroupA), vchGroupA.size()); + hashB.Write(begin_ptr(vchGroupB), vchGroupB.size()); + + hashA.Write(begin_ptr(vchSecretKey), vchSecretKey.size()); + hashB.Write(begin_ptr(vchSecretKey), vchSecretKey.size()); + + hashA.Finalize(begin_ptr(vchA)); + hashB.Finalize(begin_ptr(vchB)); + + return vchA < vchB; + } +}; + +static bool AttemptToEvictConnection(bool fPreferNewConnection) { + std::vector<CNodeRef> vEvictionCandidates; + { + LOCK(cs_vNodes); + + BOOST_FOREACH(CNode *node, vNodes) { + if (node->fWhitelisted) + continue; + if (!node->fInbound) + continue; + if (node->fDisconnect) + continue; + if (node->addr.IsLocal()) + continue; + vEvictionCandidates.push_back(CNodeRef(node)); + } + } + + if (vEvictionCandidates.empty()) return false; + + // Protect connections with certain characteristics + + // Deterministically select 4 peers to protect by netgroup. + // An attacker cannot predict which netgroups will be protected. + static CompareNetGroupKeyed comparerNetGroupKeyed; + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), comparerNetGroupKeyed); + vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); + + if (vEvictionCandidates.empty()) return false; + + // Protect the 8 nodes with the best ping times. + // An attacker cannot manipulate this metric without physically moving nodes closer to the target. + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime); + vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); + + if (vEvictionCandidates.empty()) return false; + + // Protect the half of the remaining nodes which have been connected the longest. + // This replicates the existing implicit behavior. + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected); + vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end()); + + if (vEvictionCandidates.empty()) return false; + + // Identify the network group with the most connections + std::vector<unsigned char> naMostConnections; + unsigned int nMostConnections = 0; + std::map<std::vector<unsigned char>, std::vector<CNodeRef> > mapAddrCounts; + BOOST_FOREACH(const CNodeRef &node, vEvictionCandidates) { + mapAddrCounts[node->addr.GetGroup()].push_back(node); + + if (mapAddrCounts[node->addr.GetGroup()].size() > nMostConnections) { + nMostConnections = mapAddrCounts[node->addr.GetGroup()].size(); + naMostConnections = node->addr.GetGroup(); + } + } + + // Reduce to the network group with the most connections + vEvictionCandidates = mapAddrCounts[naMostConnections]; + + // Do not disconnect peers if there is only 1 connection from their network group + if (vEvictionCandidates.size() <= 1) + // unless we prefer the new connection (for whitelisted peers) + if (!fPreferNewConnection) + return false; + + // Disconnect the most recent connection from the network group with the most connections + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected); + vEvictionCandidates[0]->fDisconnect = true; + + return true; +} + +static void AcceptConnection(const ListenSocket& hListenSocket) { + struct sockaddr_storage sockaddr; + socklen_t len = sizeof(sockaddr); + SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len); + CAddress addr; + int nInbound = 0; + int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS; + + 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) { - vnThreadsRunning[THREAD_SOCKETHANDLER]++; - ThreadSocketHandler2(parg); - vnThreadsRunning[THREAD_SOCKETHANDLER]--; + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK) + LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr)); + return; } - catch (std::exception& e) { - vnThreadsRunning[THREAD_SOCKETHANDLER]--; - PrintException(&e, "ThreadSocketHandler()"); - } catch (...) { - vnThreadsRunning[THREAD_SOCKETHANDLER]--; - throw; // support pthread_cancel() + + if (!IsSelectableSocket(hSocket)) + { + LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString()); + CloseSocket(hSocket); + return; + } + + // According to the internet TCP_NODELAY is not carried into accepted sockets + // on all platforms. Set it again here just to be sure. + int set = 1; +#ifdef WIN32 + setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int)); +#else + setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int)); +#endif + + if (CNode::IsBanned(addr) && !whitelisted) + { + LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); + CloseSocket(hSocket); + return; + } + + if (nInbound >= nMaxInbound) + { + if (!AttemptToEvictConnection(whitelisted)) { + // No connection to evict, disconnect the new connection + LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n"); + CloseSocket(hSocket); + return; + } + } + + CNode* pnode = new CNode(hSocket, addr, "", true); + pnode->AddRef(); + pnode->fWhitelisted = whitelisted; + + LogPrint("net", "connection from %s accepted\n", addr.ToString()); + + { + LOCK(cs_vNodes); + vNodes.push_back(pnode); } - printf("ThreadSocketHandler exiting\n"); } -void ThreadSocketHandler2(void* parg) +void ThreadSocketHandler() { - printf("ThreadSocketHandler started\n"); - list<CNode*> vNodesDisconnected; unsigned int nPrevNodeCount = 0; - - loop + while (true) { // // Disconnect nodes @@ -638,7 +1032,7 @@ void ThreadSocketHandler2(void* parg) BOOST_FOREACH(CNode* pnode, vNodesCopy) { if (pnode->fDisconnect || - (pnode->GetRefCount() <= 0 && pnode->vRecv.empty() && pnode->vSend.empty())) + (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()); @@ -648,16 +1042,15 @@ void ThreadSocketHandler2(void* parg) // 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) @@ -670,16 +1063,12 @@ void ThreadSocketHandler2(void* parg) TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) { - TRY_LOCK(pnode->cs_vRecv, lockRecv); + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) { - TRY_LOCK(pnode->cs_mapRequests, lockReq); - if (lockReq) - { - TRY_LOCK(pnode->cs_inventory, lockInv); - if (lockInv) - fDelete = true; - } + TRY_LOCK(pnode->cs_inventory, lockInv); + if (lockInv) + fDelete = true; } } } @@ -691,13 +1080,11 @@ void ThreadSocketHandler2(void* parg) } } } - if (vNodes.size() != nPrevNodeCount) - { + if(vNodes.size() != nPrevNodeCount) { nPrevNodeCount = vNodes.size(); - MainFrameRepaint(); + uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); } - // // Find which sockets have data to receive // @@ -712,106 +1099,85 @@ void ThreadSocketHandler2(void* parg) FD_ZERO(&fdsetSend); FD_ZERO(&fdsetError); SOCKET hSocketMax = 0; + bool have_fds = false; - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) { - FD_SET(hListenSocket, &fdsetRecv); - hSocketMax = max(hSocketMax, hListenSocket); + 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, &fdsetRecv); 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->vSend.empty()) + 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); } } } - vnThreadsRunning[THREAD_SOCKETHANDLER]--; - int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); - vnThreadsRunning[THREAD_SOCKETHANDLER]++; - if (fShutdown) - return; + int nSelect = select(have_fds ? hSocketMax + 1 : 0, + &fdsetRecv, &fdsetSend, &fdsetError, &timeout); + boost::this_thread::interruption_point(); + if (nSelect == SOCKET_ERROR) { - int nErr = WSAGetLastError(); - if (hSocketMax != INVALID_SOCKET) + if (have_fds) { - printf("socket select error %d\n", nErr); + 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); - Sleep(timeout.tv_usec/1000); + MilliSleep(timeout.tv_usec/1000); } - // // Accept new connections // - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) - if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) + BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) { -#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)) + if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) { - 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); - } + AcceptConnection(hListenSocket); } } - // // Service each socket // @@ -824,8 +1190,7 @@ void ThreadSocketHandler2(void* parg) } BOOST_FOREACH(CNode* pnode, vNodesCopy) { - if (fShutdown) - return; + boost::this_thread::interruption_point(); // // Receive @@ -834,32 +1199,26 @@ void ThreadSocketHandler2(void* parg) continue; if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) { - TRY_LOCK(pnode->cs_vRecv, lockRecv); + TRY_LOCK(pnode->cs_vRecvMsg, 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); + 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) - printf("socket closed\n"); + LogPrint("net", "socket closed\n"); pnode->CloseSocketDisconnect(); } else if (nBytes < 0) @@ -869,7 +1228,7 @@ void ThreadSocketHandler2(void* parg) if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { if (!pnode->fDisconnect) - printf("socket recv error %d\n", nErr); + LogPrintf("socket recv error %s\n", NetworkErrorString(nErr)); pnode->CloseSocketDisconnect(); } } @@ -886,55 +1245,33 @@ void ThreadSocketHandler2(void* parg) { 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(); - } - } - if (vSend.size() > SendBufferSize()) { - if (!pnode->fDisconnect) - printf("socket send flood control disconnect (%d bytes)\n", vSend.size()); - pnode->CloseSocketDisconnect(); - } - } - } + SocketSendData(pnode); } // // Inactivity checking // - if (pnode->vSend.empty()) - pnode->nLastSendEmpty = GetTime(); - if (GetTime() - pnode->nTimeConnected > 60) + int64_t nTime = GetTime(); + if (nTime - 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); + 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 (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60) + else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) { - printf("socket not sending\n"); + LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend); pnode->fDisconnect = true; } - else if (GetTime() - pnode->nLastRecv > 90*60) + else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60)) { - printf("socket inactivity timeout\n"); + 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; } } @@ -944,8 +1281,6 @@ void ThreadSocketHandler2(void* parg) BOOST_FOREACH(CNode* pnode, vNodesCopy) pnode->Release(); } - - Sleep(10); } } @@ -958,32 +1293,9 @@ void ThreadSocketHandler2(void* parg) #ifdef USE_UPNP -void ThreadMapPort(void* parg) +void ThreadMapPort() { - IMPLEMENT_RANDOMIZE_STACK(ThreadMapPort(parg)); - try - { - vnThreadsRunning[THREAD_UPNP]++; - ThreadMapPort2(parg); - vnThreadsRunning[THREAD_UPNP]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_UPNP]--; - PrintException(&e, "ThreadMapPort()"); - } catch (...) { - vnThreadsRunning[THREAD_UPNP]--; - PrintException(NULL, "ThreadMapPort()"); - } - printf("ThreadMapPort exiting\n"); -} - -void ThreadMapPort2(void* parg) -{ - printf("ThreadMapPort started\n"); - - char port[6]; - sprintf(port, "%d", GetListenPort()); - + std::string port = strprintf("%u", GetListenPort()); const char * multicastif = 0; const char * minissdpdpath = 0; struct UPNPDev * devlist = 0; @@ -992,10 +1304,14 @@ void ThreadMapPort2(void* parg) #ifndef UPNPDISCOVER_SUCCESS /* miniupnpc 1.5 */ devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); -#else +#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; @@ -1005,97 +1321,85 @@ void ThreadMapPort2(void* parg) r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); if (r == 1) { - if (GetBoolArg("-discover", true)) { + if (fDiscover) { char externalIPAddress[40]; r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); if(r != UPNPCOMMAND_SUCCESS) - printf("UPnP: GetExternalIPAddress() returned %d\n", r); + LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); else { if(externalIPAddress[0]) { - printf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); + LogPrintf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP); } else - printf("UPnP: GetExternalIPAddress failed.\n"); + LogPrintf("UPnP: GetExternalIPAddress failed.\n"); } } string strDesc = "Bitcoin " + FormatFullVersion(); -#ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port, port, lanaddr, strDesc.c_str(), "TCP", 0); -#else - /* miniupnpc 1.6 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port, port, lanaddr, strDesc.c_str(), "TCP", 0, "0"); -#endif - if(r!=UPNPCOMMAND_SUCCESS) - printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", - port, port, lanaddr, r, strupnperror(r)); - else - printf("UPnP Port Mapping successful.\n"); - int i = 1; - loop { - if (fShutdown || !fUseUPnP) - { - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port, "TCP", 0); - printf("UPNP_DeletePortMapping() returned : %d\n", r); - freeUPNPDevlist(devlist); devlist = 0; - FreeUPNPUrls(&urls); - return; - } - if (i % 600 == 0) // Refresh every 20 minutes - { + try { + while (true) { #ifndef UPNPDISCOVER_SUCCESS /* miniupnpc 1.5 */ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port, port, lanaddr, strDesc.c_str(), "TCP", 0); + 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, port, lanaddr, strDesc.c_str(), "TCP", 0, "0"); + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); #endif if(r!=UPNPCOMMAND_SUCCESS) - printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r)); else - printf("UPnP Port Mapping successful.\n");; + LogPrintf("UPnP Port Mapping successful.\n");; + + MilliSleep(20*60*1000); // Refresh every 20 minutes } - Sleep(2000); - i++; + } + catch (const 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 { - printf("No valid UPnP IGDs found\n"); + LogPrintf("No valid UPnP IGDs found\n"); freeUPNPDevlist(devlist); devlist = 0; if (r != 0) FreeUPNPUrls(&urls); - loop { - if (fShutdown || !fUseUPnP) - return; - Sleep(2000); - } } } -void MapPort(bool fMapPort) +void MapPort(bool fUseUPnP) { - if (fUseUPnP != fMapPort) + static boost::thread* upnp_thread = NULL; + + if (fUseUPnP) { - fUseUPnP = fMapPort; + if (upnp_thread) { + upnp_thread->interrupt(); + upnp_thread->join(); + delete upnp_thread; + } + upnp_thread = new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort)); } - if (fUseUPnP && vnThreadsRunning[THREAD_UPNP] < 1) - { - if (!CreateThread(ThreadMapPort, NULL)) - printf("Error: ThreadMapPort(ThreadMapPort) failed\n"); + else if (upnp_thread) { + upnp_thread->interrupt(); + upnp_thread->join(); + delete upnp_thread; + upnp_thread = NULL; } } + #else -void MapPort(bool /* unused fMapPort */) +void MapPort(bool) { // Intentionally left blank. } @@ -1106,214 +1410,80 @@ void MapPort(bool /* unused fMapPort */) - - - -// 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] = { - {"xf2.org", "bitseed.xf2.org"}, - {"bluematt.me", "dnsseed.bluematt.me"}, - {"bitcoin.sipa.be", "seed.bitcoin.sipa.be"}, - {"dashjr.org", "dnsseed.bitcoin.dashjr.org"}, -}; - -void ThreadDNSAddressSeed(void* parg) +void ThreadDNSAddressSeed() { - IMPLEMENT_RANDOMIZE_STACK(ThreadDNSAddressSeed(parg)); - 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() + // 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; + } } - printf("ThreadDNSAddressSeed exiting\n"); -} -void ThreadDNSAddressSeed2(void* parg) -{ - printf("ThreadDNSAddressSeed started\n"); + const vector<CDNSSeedData> &vSeeds = Params().DNSSeeds(); int found = 0; - if (!fTestNet) - { - printf("Loading addresses from DNS seeds (could take a while)\n"); + LogPrintf("Loading addresses from DNS seeds (could take a while)\n"); - for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) { - if (fProxyNameLookup) { - AddOneShot(strDNSSeed[seed_idx][1]); - } else { - vector<CNetAddr> vaddr; - vector<CAddress> vAdd; - if (LookupHost(strDNSSeed[seed_idx][1], vaddr)) + 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(const CNetAddr& ip, vIPs) { - 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++; - } + 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(strDNSSeed[seed_idx][0], true)); } + addrman.Add(vAdd, CNetAddr(seed.name, true)); } } - printf("%d addresses found from DNS seeds\n", found); -} - - - - - - - - - - - - -unsigned int pnSeed[] = -{ - 0x959bd347, 0xf8de42b2, 0x73bc0518, 0xea6edc50, 0x21b00a4d, 0xc725b43d, 0xd665464d, 0x1a2a770e, - 0x27c93946, 0x65b2fa46, 0xb80ae255, 0x66b3b446, 0xb1877a3e, 0x6ee89e3e, 0xc3175b40, 0x2a01a83c, - 0x95b1363a, 0xa079ad3d, 0xe6ca801f, 0x027f4f4a, 0x34f7f03a, 0xf790f04a, 0x16ca801f, 0x2f4d5e40, - 0x3a4d5e40, 0xc43a322e, 0xc8159753, 0x14d4724c, 0x7919a118, 0xe0bdb34e, 0x68a16b2e, 0xff64b44d, - 0x6099115b, 0x9b57b05b, 0x7bd1b4ad, 0xdf95944f, 0x29d2b73d, 0xafa8db79, 0xe247ba41, 0x24078348, - 0xf722f03c, 0x33567ebc, 0xace64ed4, 0x984d3932, 0xb5f34e55, 0x27b7024d, 0x94579247, 0x8894042e, - 0x9357d34c, 0x1063c24b, 0xcaa228b1, 0xa3c5a8b2, 0x5dc64857, 0xa2c23643, 0xa8369a54, 0x31203077, - 0x00707c5c, 0x09fc0b3a, 0x272e9e2e, 0xf80f043e, 0x9449ca3e, 0x5512c33e, 0xd106b555, 0xe8024157, - 0xe288ec29, 0xc79c5461, 0xafb63932, 0xdb02ab4b, 0x0e512777, 0x8a145a4c, 0xb201ff4f, 0x5e09314b, - 0xcd9bfbcd, 0x1c023765, 0x4394e75c, 0xa728bd4d, 0x65331552, 0xa98420b1, 0x89ecf559, 0x6e80801f, - 0xf404f118, 0xefd62b51, 0x05918346, 0x9b186d5f, 0xacabab46, 0xf912e255, 0xc188ea62, 0xcc55734e, - 0xc668064d, 0xd77a4558, 0x46201c55, 0xf17dfc80, 0xf7142f2e, 0x87bfb718, 0x8aa54fb2, 0xc451d518, - 0xc4ae8831, 0x8dd44d55, 0x5bbd206c, 0x64536b5d, 0x5c667e60, 0x3b064242, 0xfe963a42, 0xa28e6dc8, - 0xe8a9604a, 0xc989464e, 0xd124a659, 0x50065140, 0xa44dfe5e, 0x1079e655, 0x3fb986d5, 0x47895b18, - 0x7d3ce4ad, 0x4561ba50, 0x296eec62, 0x255b41ad, 0xaed35ec9, 0x55556f12, 0xc7d3154d, 0x3297b65d, - 0x8930121f, 0xabf42e4e, 0x4a29e044, 0x1212685d, 0x676c1e40, 0xce009744, 0x383a8948, 0xa2dbd0ad, - 0xecc2564d, 0x07dbc252, 0x887ee24b, 0x5171644c, 0x6bb798c1, 0x847f495d, 0x4cbb7145, 0x3bb81c32, - 0x45eb262e, 0xc8015a4e, 0x250a361b, 0xf694f946, 0xd64a183e, 0xd4f1dd59, 0x8f20ffd4, 0x51d9e55c, - 0x09521763, 0x5e02002e, 0x32c8074d, 0xe685762e, 0x8290b0bc, 0x762a922e, 0xfc5ee754, 0x83a24829, - 0x775b224d, 0x6295bb4d, 0x38ec0555, 0xbffbba50, 0xe5560260, 0x86b16a7c, 0xd372234e, 0x49a3c24b, - 0x2f6a171f, 0x4d75ed60, 0xae94115b, 0xcb543744, 0x63080c59, 0x3f9c724c, 0xc977ce18, 0x532efb18, - 0x69dc3b2e, 0x5f94d929, 0x1732bb4d, 0x9c814b4d, 0xe6b3762e, 0xc024f662, 0x8face35b, 0x6b5b044d, - 0x798c7b57, 0x79a6b44c, 0x067d3057, 0xf9e94e5f, 0x91cbe15b, 0x71405eb2, 0x2662234e, 0xcbcc4a6d, - 0xbf69d54b, 0xa79b4e55, 0xec6d3e51, 0x7c0b3c02, 0x60f83653, 0x24c1e15c, 0x1110b62e, 0x10350f59, - 0xa56f1d55, 0x3509e7a9, 0xeb128354, 0x14268e2e, 0x934e28bc, 0x8e32692e, 0x8331a21f, 0x3e633932, - 0xc812b12e, 0xc684bf2e, 0x80112d2e, 0xe0ddc96c, 0xc630ca4a, 0x5c09b3b2, 0x0b580518, 0xc8e9d54b, - 0xd169aa43, 0x17d0d655, 0x1d029963, 0x7ff87559, 0xcb701f1f, 0x6fa3e85d, 0xe45e9a54, 0xf05d1802, - 0x44d03b2e, 0x837b692e, 0xccd4354e, 0x3d6da13c, 0x3423084d, 0xf707c34a, 0x55f6db3a, 0xad26e442, - 0x6233a21f, 0x09e80e59, 0x8caeb54d, 0xbe870941, 0xb407d20e, 0x20b51018, 0x56fb152e, 0x460d2a4e, - 0xbb9a2946, 0x560eb12e, 0xed83dd29, 0xd6724f53, 0xa50aafb8, 0x451346d9, 0x88348e2e, 0x7312fead, - 0x8ecaf96f, 0x1bda4e5f, 0xf1671e40, 0x3c8c3e3b, 0x4716324d, 0xdde24ede, 0xf98cd17d, 0xa91d4644, - 0x28124eb2, 0x147d5129, 0xd022042e, 0x61733d3b, 0xad0d5e02, 0x8ce2932e, 0xe5c18502, 0x549c1e32, - 0x9685801f, 0x86e217ad, 0xd948214b, 0x4110f462, 0x3a2e894e, 0xbd35492e, 0x87e0d558, 0x64b8ef7d, - 0x7c3eb962, 0x72a84b3e, 0x7cd667c9, 0x28370a2e, 0x4bc60e7b, 0x6fc1ec60, 0x14a6983f, 0x86739a4b, - 0x46954e5f, 0x32e2e15c, 0x2e9326cf, 0xe5801c5e, 0x379607b2, 0x32151145, 0xf0e39744, 0xacb54c55, - 0xa37dfb60, 0x83b55cc9, 0x388f7ca5, 0x15034f5f, 0x3e94965b, 0x68e0ffad, 0x35280f59, 0x8fe190cf, - 0x7c6ba5b2, 0xa5e9db43, 0x4ee1fc60, 0xd9d94e5f, 0x04040677, 0x0ea9b35e, 0x5961f14f, 0x67fda063, - 0xa48a5a31, 0xc6524e55, 0x283d325e, 0x3f37515f, 0x96b94b3e, 0xacce620e, 0x6481cc5b, 0xa4a06d4b, - 0x9e95d2d9, 0xe40c03d5, 0xc2f4514b, 0xb79aad44, 0xf64be843, 0xb2064070, 0xfca00455, 0x429dfa4e, - 0x2323f173, 0xeda4185e, 0xabd5227d, 0x9efd4d58, 0xb1104758, 0x4811e955, 0xbd9ab355, 0xe921f44b, - 0x9f166dce, 0x09e279b2, 0xe0c9ac7b, 0x7901a5ad, 0xa145d4b0, 0x79104671, 0xec31e35a, 0x4fe0b555, - 0xc7d9cbad, 0xad057f55, 0xe94cc759, 0x7fe0b043, 0xe4529f2e, 0x0d4dd4b2, 0x9f11a54d, 0x031e2e4e, - 0xe6014f5f, 0x11d1ca6c, 0x26bd7f61, 0xeb86854f, 0x4d347b57, 0x116bbe2e, 0xdba7234e, 0x7bcbfd2e, - 0x174dd4b2, 0x6686762e, 0xb089ba50, 0xc6258246, 0x087e767b, 0xc4a8cb4a, 0x595dba50, 0x7f0ae502, - 0x7b1dbd5a, 0xa0603492, 0x57d1af4b, 0x9e21ffd4, 0x6393064d, 0x7407376e, 0xe484762e, 0x122a4e53, - 0x4a37aa43, 0x3888a6be, 0xee77864e, 0x039c8dd5, 0x688d89af, 0x0e988f62, 0x08218246, 0xfc2f8246, - 0xd1d97040, 0xd64cd4b2, 0x5ae4a6b8, 0x7d0de9bc, 0x8d304d61, 0x06c5c672, 0xa4c8bd4d, 0xe0fd373b, - 0x575ebe4d, 0x72d26277, 0x55570f55, 0x77b154d9, 0xe214293a, 0xfc740f4b, 0xfe3f6a57, 0xa9c55f02, - 0xae4054db, 0x2394d918, 0xb511b24a, 0xb8741ab2, 0x0758e65e, 0xc7b5795b, 0xb0a30a4c, 0xaf7f170c, - 0xf3b4762e, 0x8179576d, 0x738a1581, 0x4b95b64c, 0x9829b618, 0x1bea932e, 0x7bdeaa4b, 0xcb5e0281, - 0x65618f54, 0x0658474b, 0x27066acf, 0x40556d65, 0x7d204d53, 0xf28bc244, 0xdce23455, 0xadc0ff54, - 0x3863c948, 0xcee34e5f, 0xdeb85e02, 0x2ed17a61, 0x6a7b094d, 0x7f0cfc40, 0x59603f54, 0x3220afbc, - 0xb5dfd962, 0x125d21c0, 0x13f8d243, 0xacfefb4e, 0x86c2c147, 0x3d8bbd59, 0xbd02a21f, 0x2593042e, - 0xc6a17a7c, 0x28925861, 0xb487ed44, 0xb5f4fd6d, 0x90c28a45, 0x5a14f74d, 0x43d71b4c, 0x728ebb5d, - 0x885bf950, 0x08134dd0, 0x38ec046e, 0xc575684b, 0x50082d2e, 0xa2f47757, 0x270f86ae, 0xf3ff6462, - 0x10ed3f4e, 0x4b58d462, 0xe01ce23e, 0x8c5b092e, 0x63e52f4e, 0x22c1e85d, 0xa908f54e, 0x8591624f, - 0x2c0fb94e, 0xa280ba3c, 0xb6f41b4c, 0x24f9aa47, 0x27201647, 0x3a3ea6dc, 0xa14fc3be, 0x3c34bdd5, - 0x5b8d4f5b, 0xaadeaf4b, 0xc71cab50, 0x15697a4c, 0x9a1a734c, 0x2a037d81, 0x2590bd59, 0x48ec2741, - 0x53489c5b, 0x7f00314b, 0x2170d362, 0xf2e92542, 0x42c10b44, 0x98f0f118, 0x883a3456, 0x099a932e, - 0xea38f7bc, 0x644e9247, 0xbb61b62e, 0x30e0863d, 0x5f51be54, 0x207215c7, 0x5f306c45, 0xaa7f3932, - 0x98da7d45, 0x4e339b59, 0x2e411581, 0xa808f618, 0xad2c0c59, 0x54476741, 0x09e99fd1, 0x5db8f752, - 0xc16df8bd, 0x1dd4b44f, 0x106edf2e, 0x9e15c180, 0x2ad6b56f, 0x633a5332, 0xff33787c, 0x077cb545, - 0x6610be6d, 0x75aad2c4, 0x72fb4d5b, 0xe81e0f59, 0x576f6332, 0x47333373, 0x351ed783, 0x2d90fb50, - 0x8d5e0f6c, 0x5b27a552, 0xdb293ebb, 0xe55ef950, 0x4b133ad8, 0x75df975a, 0x7b6a8740, 0xa899464b, - 0xfab15161, 0x10f8b64d, 0xd055ea4d, 0xee8e146b, 0x4b14afb8, 0x4bc1c44a, 0x9b961dcc, 0xd111ff43, - 0xfca0b745, 0xc800e412, 0x0afad9d1, 0xf751c350, 0xf9f0cccf, 0xa290a545, 0x8ef13763, 0x7ec70d59, - 0x2b066acf, 0x65496c45, 0xade02c1b, 0xae6eb077, 0x92c1e65b, 0xc064e6a9, 0xc649e56d, 0x5287a243, - 0x36de4f5b, 0x5b1df6ad, 0x65c39a59, 0xdba805b2, 0x20067aa8, 0x6457e56d, 0x3cee26cf, 0xfd3ff26d, - 0x04f86d4a, 0x06b8e048, 0xa93bcd5c, 0x91135852, 0xbe90a643, 0x8fa0094d, 0x06d8215f, 0x2677094d, - 0xd735685c, 0x164a00c9, 0x5209ac5f, 0xa9564c5c, 0x3b504f5f, 0xcc826bd0, 0x4615042e, 0x5fe13b4a, - 0x8c81b86d, 0x879ab68c, 0x1de564b8, 0x434487d8, 0x2dcb1b63, 0x82ab524a, 0xb0676abb, 0xa13d9c62, - 0xdbb5b86d, 0x5b7f4b59, 0xaddfb44d, 0xad773532, 0x3997054c, 0x72cebd89, 0xb194544c, 0xc5b8046e, - 0x6e1adeb2, 0xaa5abb51, 0xefb54b44, 0x15efc54f, 0xe9f1bc4d, 0x5f401b6c, 0x97f018ad, 0xc82f9252, - 0x2cdc762e, 0x8e52e56d, 0x1827175e, 0x9b7d7d80, 0xb2ad6845, 0x51065140, 0x71180a18, 0x5b27006c, - 0x0621e255, 0x721cbe58, 0x670c0cb8, 0xf8bd715d, 0xe0bdc5d9, 0xed843501, 0x4b84554d, 0x7f1a18bc, - 0x53bcaf47, 0x5729d35f, 0xf0dda246, 0x22382bd0, 0x4d641fb0, 0x316afcde, 0x50a22f1f, 0x73608046, - 0xc461d84a, 0xb2dbe247, -}; + LogPrintf("%d addresses found from DNS seeds\n", found); +} + + + + + + + + + + + void DumpAddresses() { + int64_t nStart = GetTimeMillis(); + CAddrDB adb; - adb.WriteAddrman(addrman); -} + adb.Write(addrman); -void ThreadDumpAddress2(void* parg) -{ - vnThreadsRunning[THREAD_DUMPADDRESS]++; - while (!fShutdown) - { - DumpAddresses(); - vnThreadsRunning[THREAD_DUMPADDRESS]--; - Sleep(100000); - vnThreadsRunning[THREAD_DUMPADDRESS]++; - } - vnThreadsRunning[THREAD_DUMPADDRESS]--; + LogPrint("net", "Flushed %d addresses to peers.dat %dms\n", + addrman.size(), GetTimeMillis() - nStart); } -void ThreadDumpAddress(void* parg) +void DumpData() { - IMPLEMENT_RANDOMIZE_STACK(ThreadDumpAddress(parg)); - try - { - ThreadDumpAddress2(parg); - } - catch (std::exception& e) { - PrintException(&e, "ThreadDumpAddress()"); - } - printf("ThreadDumpAddress exiting\n"); -} + DumpAddresses(); -void ThreadOpenConnections(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadOpenConnections(parg)); - try + if (CNode::BannedSetIsDirty()) { - 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()"); + DumpBanlist(); + CNode::SetBannedSetDirty(false); } - printf("ThreadOpenConnections exiting\n"); } void static ProcessOneShot() @@ -1334,68 +1504,46 @@ void static ProcessOneShot() } } -void ThreadOpenConnections2(void* parg) +void ThreadOpenConnections() { - printf("ThreadOpenConnections started\n"); - // Connect to specific addresses - if (mapArgs.count("-connect")) + if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { - for (int64 nLoop = 0;; nLoop++) + for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); - BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) + BOOST_FOREACH(const std::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; + MilliSleep(500); } } + MilliSleep(500); } } // Initiate network connections - int64 nStart = GetTime(); - loop + int64_t nStart = GetTime(); + while (true) { ProcessOneShot(); - vnThreadsRunning[THREAD_OPENCONNECTIONS]--; - Sleep(500); - vnThreadsRunning[THREAD_OPENCONNECTIONS]++; - if (fShutdown) - return; - + MilliSleep(500); - vnThreadsRunning[THREAD_OPENCONNECTIONS]--; CSemaphoreGrant grant(*semOutbound); - vnThreadsRunning[THREAD_OPENCONNECTIONS]++; - if (fShutdown) - return; - - // Add seed nodes if IRC isn't working - bool fTOR = (fUseProxy && addrProxy.GetPort() == 9050); - if (addrman.size()==0 && (GetTime() - nStart > 60 || fTOR) && !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); + 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(convertSeed6(Params().FixedSeeds()), CNetAddr("127.0.0.1")); + done = true; } - addrman.Add(vAdd, CNetAddr("127.0.0.1")); } // @@ -1403,32 +1551,37 @@ void ThreadOpenConnections2(void* parg) // CAddress addrConnect; - // Only connect to one address per a.b.?.? range. + // 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) { - setConnected.insert(pnode->addr.GetGroup()); - if (!pnode->fInbound) + if (!pnode->fInbound) { + setConnected.insert(pnode->addr.GetGroup()); nOutbound++; + } } } - int64 nANow = GetAdjustedTime(); + int64_t nANow = GetAdjustedTime(); int nTries = 0; - loop + while (true) { - // use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections) - CAddress addr = addrman.Select(10 + min(nOutbound,8)*10); + CAddrInfo 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; @@ -1438,7 +1591,7 @@ void ThreadOpenConnections2(void* parg) continue; // do not allow non-default ports, unless after 50 invalid addresses selected already - if (addr.GetPort() != GetDefaultPort() && nTries < 50) + if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50) continue; addrConnect = addr; @@ -1450,117 +1603,95 @@ void ThreadOpenConnections2(void* parg) } } -void ThreadOpenAddedConnections(void* parg) +void ThreadOpenAddedConnections() { - IMPLEMENT_RANDOMIZE_STACK(ThreadOpenAddedConnections(parg)); - try { - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++; - ThreadOpenAddedConnections2(parg); - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; + LOCK(cs_vAddedNodes); + vAddedNodes = mapMultiArgs["-addnode"]; } - catch (std::exception& e) { - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; - PrintException(&e, "ThreadOpenAddedConnections()"); - } catch (...) { - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; - PrintException(NULL, "ThreadOpenAddedConnections()"); - } - printf("ThreadOpenAddedConnections exiting\n"); -} - -void ThreadOpenAddedConnections2(void* parg) -{ - printf("ThreadOpenAddedConnections started\n"); - if (mapArgs.count("-addnode") == 0) - return; - - if (fProxyNameLookup) { - while(!fShutdown) { - BOOST_FOREACH(string& strAddNode, mapMultiArgs["-addnode"]) { + if (HaveNameProxy()) { + while(true) { + list<string> lAddresses(0); + { + LOCK(cs_vAddedNodes); + BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) + lAddresses.push_back(strAddNode); + } + BOOST_FOREACH(const std::string& strAddNode, lAddresses) { CAddress addr; CSemaphoreGrant grant(*semOutbound); OpenNetworkConnection(addr, &grant, strAddNode.c_str()); - Sleep(500); + MilliSleep(500); } - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; - Sleep(120000); // Retry every 2 minutes - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++; + MilliSleep(120000); // Retry every 2 minutes } - return; } - vector<vector<CService> > vservAddressesToAdd(0); - BOOST_FOREACH(string& strAddNode, mapMultiArgs["-addnode"]) + for (unsigned int i = 0; true; i++) { - vector<CService> vservNode(0); - if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0)) + list<string> lAddresses(0); { - vservAddressesToAdd.push_back(vservNode); + LOCK(cs_vAddedNodes); + BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) + lAddresses.push_back(strAddNode); + } + + list<vector<CService> > lservAddressesToAdd(0); + BOOST_FOREACH(const std::string& strAddNode, lAddresses) { + vector<CService> vservNode(0); + if(Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0)) { - LOCK(cs_setservAddNodeAddresses); - BOOST_FOREACH(CService& serv, vservNode) - setservAddNodeAddresses.insert(serv); + lservAddressesToAdd.push_back(vservNode); + { + LOCK(cs_setservAddNodeAddresses); + BOOST_FOREACH(const 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)) + for (list<vector<CService> >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++) + BOOST_FOREACH(const CService& addrNode, *(it)) if (pnode->addr == addrNode) { - it = vservConnectAddresses.erase(it); + it = lservAddressesToAdd.erase(it); it--; break; } } - BOOST_FOREACH(vector<CService>& vserv, vservConnectAddresses) + BOOST_FOREACH(vector<CService>& vserv, lservAddressesToAdd) { CSemaphoreGrant grant(*semOutbound); - OpenNetworkConnection(CAddress(*(vserv.begin())), &grant); - Sleep(500); - if (fShutdown) - return; + OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); + MilliSleep(500); } - if (fShutdown) - return; - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; - Sleep(120000); // Retry every 2 minutes - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++; - if (fShutdown) - return; + MilliSleep(120000); // Retry every 2 minutes } } -// if succesful, this moves the passed grant to the constructed node -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *strDest, bool fOneShot) +// 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 // - if (fShutdown) - return false; - if (!strDest) + boost::this_thread::interruption_point(); + if (!pszDest) { if (IsLocal(addrConnect) || FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || - FindNode(addrConnect.ToStringIPPort().c_str())) + FindNode(addrConnect.ToStringIPPort())) return false; - if (strDest && FindNode(strDest)) + } else if (FindNode(std::string(pszDest))) return false; - vnThreadsRunning[THREAD_OPENCONNECTIONS]--; - CNode* pnode = ConnectNode(addrConnect, strDest); - vnThreadsRunning[THREAD_OPENCONNECTIONS]++; - if (fShutdown) - return false; + CNode* pnode = ConnectNode(addrConnect, pszDest); + boost::this_thread::interruption_point(); + if (!pnode) return false; if (grantOutbound) @@ -1573,68 +1704,61 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu } - - - - - - -void ThreadMessageHandler(void* parg) +void ThreadMessageHandler() { - IMPLEMENT_RANDOMIZE_STACK(ThreadMessageHandler(parg)); - 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 exiting\n"); -} + boost::mutex condition_mutex; + boost::unique_lock<boost::mutex> lock(condition_mutex); -void ThreadMessageHandler2(void* parg) -{ - printf("ThreadMessageHandler started\n"); SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); - while (!fShutdown) + while (true) { vector<CNode*> vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) + 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_vRecv, lockRecv); + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) - ProcessMessages(pnode); + { + if (!g_signals.ProcessMessages(pnode)) + pnode->CloseSocketDisconnect(); + + if (pnode->nSendSize < SendBufferSize()) + { + if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) + { + fSleep = false; + } + } + } } - if (fShutdown) - return; + boost::this_thread::interruption_point(); // Send messages { TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) - SendMessages(pnode, pnode == pnodeTrickle); + g_signals.SendMessages(pnode, pnode == pnodeTrickle || pnode->fWhitelisted); } - if (fShutdown) - return; + boost::this_thread::interruption_point(); } { @@ -1643,16 +1767,8 @@ void ThreadMessageHandler2(void* parg) 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) - Shutdown(NULL); - vnThreadsRunning[THREAD_MESSAGEHANDLER]++; - if (fShutdown) - return; + if (fSleep) + messageHandlerCondition.timed_wait(lock, boost::posix_time::microsec_clock::universal_time() + boost::posix_time::milliseconds(100)); } } @@ -1661,121 +1777,112 @@ void ThreadMessageHandler2(void* parg) -bool BindListenPort(const CService &addrBind, string& strError) +bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted) { 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()); + 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 %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); + 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 - -#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. + // the program was closed and restarted. 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) + // Disable Nagle's algorithm + setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&nOne, sizeof(int)); #else - if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) + setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int)); + setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&nOne, sizeof(int)); #endif - { - strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); + + // 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; } -#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 +#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 = 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)); + int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED; + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (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. Bitcoin is probably already running."), addrBind.ToString().c_str()); + strError = strprintf(_("Unable to bind to %s on this computer. Bitcoin Core is probably already running."), addrBind.ToString()); 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()); + 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; } - printf("Bound to %s\n", addrBind.ToString().c_str()); + 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 %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); + 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(hListenSocket); + vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted)); - if (addrBind.IsRoutable() && GetBoolArg("-discover", true)) + if (addrBind.IsRoutable() && fDiscover && !fWhitelisted) AddLocal(addrBind, LOCAL_BIND); return true; } -void static Discover() +void static Discover(boost::thread_group& threadGroup) { - if (!GetBoolArg("-discover", true)) + if (!fDiscover) return; #ifdef WIN32 - // Get local host ip - char pszHostName[1000] = ""; + // Get local host IP + char pszHostName[256] = ""; if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { vector<CNetAddr> vaddr; @@ -1783,7 +1890,8 @@ void static Discover() { BOOST_FOREACH (const CNetAddr &addr, vaddr) { - AddLocal(addr, LOCAL_IF); + if (AddLocal(addr, LOCAL_IF)) + LogPrintf("%s: %s - %s\n", __func__, pszHostName, addr.ToString()); } } } @@ -1803,144 +1911,130 @@ void static Discover() 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()); + LogPrintf("%s: IPv4 %s: %s\n", __func__, ifa->ifa_name, addr.ToString()); } -#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()); + LogPrintf("%s: IPv6 %s: %s\n", __func__, ifa->ifa_name, addr.ToString()); } -#endif } freeifaddrs(myaddrs); } #endif +} - if (!fUseProxy && !mapArgs.count("-connect") && !fNoListen) +void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) +{ + uiInterface.InitMessage(_("Loading addresses...")); + // Load addresses for peers.dat + int64_t nStart = GetTimeMillis(); { - CreateThread(ThreadGetMyExternalIP, NULL); + CAddrDB adb; + if (!adb.Read(addrman)) + LogPrintf("Invalid or missing peers.dat; recreating\n"); } -} -void StartNode(void* parg) -{ -#ifdef USE_UPNP -#if USE_UPNP - fUseUPnP = GetBoolArg("-upnp", true); -#else - fUseUPnP = GetBoolArg("-upnp", false); -#endif -#endif + //try to read stored banlist + CBanDB bandb; + banmap_t banmap; + if (!bandb.Read(banmap)) + LogPrintf("Invalid or missing banlist.dat; recreating\n"); + + CNode::SetBanned(banmap); //thread save setter + CNode::SetBannedSetDirty(false); //no need to write down just read or nonexistent data + CNode::SweepBanned(); //sweap out unused entries + + 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, (int)GetArg("-maxconnections", 125)); + 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(); + Discover(threadGroup); // // Start threads // if (!GetBoolArg("-dnsseed", true)) - printf("DNS seeding disabled\n"); + LogPrintf("DNS seeding disabled\n"); else - if (!CreateThread(ThreadDNSAddressSeed, NULL)) - printf("Error: CreateThread(ThreadDNSAddressSeed) failed\n"); + threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "dnsseed", &ThreadDNSAddressSeed)); // Map ports with UPnP - if (fHaveUPnP) - MapPort(fUseUPnP); - - // Get addresses from IRC and advertise ours - if (!CreateThread(ThreadIRCSeed, NULL)) - printf("Error: CreateThread(ThreadIRCSeed) failed\n"); + MapPort(GetBoolArg("-upnp", DEFAULT_UPNP)); // Send and receive from sockets, accept connections - if (!CreateThread(ThreadSocketHandler, NULL)) - printf("Error: CreateThread(ThreadSocketHandler) failed\n"); + threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "net", &ThreadSocketHandler)); // Initiate outbound connections from -addnode - if (!CreateThread(ThreadOpenAddedConnections, NULL)) - printf("Error: CreateThread(ThreadOpenAddedConnections) failed\n"); + threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "addcon", &ThreadOpenAddedConnections)); // Initiate outbound connections - if (!CreateThread(ThreadOpenConnections, NULL)) - printf("Error: CreateThread(ThreadOpenConnections) failed\n"); + threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "opencon", &ThreadOpenConnections)); // Process messages - if (!CreateThread(ThreadMessageHandler, NULL)) - printf("Error: CreateThread(ThreadMessageHandler) failed\n"); + threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler)); // Dump network addresses - if (!CreateThread(ThreadDumpAddress, NULL)) - printf("Error; CreateThread(ThreadDumpAddress) failed\n"); - - // Generate coins in the background - GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); + scheduler.scheduleEvery(&DumpData, DUMP_ADDRESSES_INTERVAL); } bool StopNode() { - printf("StopNode()\n"); - fShutdown = true; - nTransactionsUpdated++; - int64 nStart = GetTime(); + LogPrintf("StopNode()\n"); + MapPort(false); if (semOutbound) for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++) semOutbound->post(); - do + + if (fAddressesInitialized) { - 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 (fHaveUPnP && vnThreadsRunning[THREAD_UPNP] > 0) printf("ThreadMapPort 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(); + DumpData(); + fAddressesInitialized = false; + } + return true; } class CNetCleanup { public: - CNetCleanup() - { - } + 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()); + 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 @@ -1949,3 +2043,571 @@ public: } } instance_of_cnetcleanup; + + + + + + + +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 CNode::RecordBytesRecv(uint64_t bytes) +{ + LOCK(cs_totalBytesRecv); + nTotalBytesRecv += bytes; +} + +void CNode::RecordBytesSent(uint64_t bytes) +{ + LOCK(cs_totalBytesSent); + nTotalBytesSent += bytes; + + uint64_t now = GetTime(); + if (nMaxOutboundCycleStartTime + nMaxOutboundTimeframe < now) + { + // timeframe expired, reset cycle + nMaxOutboundCycleStartTime = now; + nMaxOutboundTotalBytesSentInCycle = 0; + } + + // TODO, exclude whitebind peers + nMaxOutboundTotalBytesSentInCycle += bytes; +} + +void CNode::SetMaxOutboundTarget(uint64_t limit) +{ + LOCK(cs_totalBytesSent); + uint64_t recommendedMinimum = (nMaxOutboundTimeframe / 600) * MAX_BLOCK_SIZE; + nMaxOutboundLimit = limit; + + if (limit > 0 && limit < recommendedMinimum) + LogPrintf("Max outbound target is very small (%s bytes) and will be overshot. Recommended minimum is %s bytes.\n", nMaxOutboundLimit, recommendedMinimum); +} + +uint64_t CNode::GetMaxOutboundTarget() +{ + LOCK(cs_totalBytesSent); + return nMaxOutboundLimit; +} + +uint64_t CNode::GetMaxOutboundTimeframe() +{ + LOCK(cs_totalBytesSent); + return nMaxOutboundTimeframe; +} + +uint64_t CNode::GetMaxOutboundTimeLeftInCycle() +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundLimit == 0) + return 0; + + if (nMaxOutboundCycleStartTime == 0) + return nMaxOutboundTimeframe; + + uint64_t cycleEndTime = nMaxOutboundCycleStartTime + nMaxOutboundTimeframe; + uint64_t now = GetTime(); + return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime(); +} + +void CNode::SetMaxOutboundTimeframe(uint64_t timeframe) +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundTimeframe != timeframe) + { + // reset measure-cycle in case of changing + // the timeframe + nMaxOutboundCycleStartTime = GetTime(); + } + nMaxOutboundTimeframe = timeframe; +} + +bool CNode::OutboundTargetReached(bool historicalBlockServingLimit) +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundLimit == 0) + return false; + + if (historicalBlockServingLimit) + { + // keep a large enought buffer to at least relay each block once + uint64_t timeLeftInCycle = GetMaxOutboundTimeLeftInCycle(); + uint64_t buffer = timeLeftInCycle / 600 * MAX_BLOCK_SIZE; + if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer) + return true; + } + else if (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) + return true; + + return false; +} + +uint64_t CNode::GetOutboundTargetBytesLeft() +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundLimit == 0) + return 0; + + return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle; +} + +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 temp output file, and associate with CAutoFile + boost::filesystem::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fopen(pathTmp.string().c_str(), "wb"); + CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: Failed to open file %s", __func__, pathTmp.string()); + + // Write and commit header, data + try { + fileout << ssPeers; + } + catch (const std::exception& e) { + return error("%s: Serialize or I/O error - %s", __func__, e.what()); + } + FileCommit(fileout.Get()); + fileout.fclose(); + + // replace existing peers.dat, if any, with new peers.dat.XXXX + if (!RenameOver(pathTmp, pathAddr)) + return error("%s: Rename-into-place failed", __func__); + + 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 + uint64_t fileSize = boost::filesystem::file_size(pathAddr); + uint64_t dataSize = 0; + // Don't try to resize to a negative number if file is small + if (fileSize >= sizeof(uint256)) + dataSize = fileSize - sizeof(uint256); + 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 (const 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 (const 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, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) : + ssSend(SER_NETWORK, INIT_PROTO_VERSION), + addrKnown(5000, 0.001), + setInventoryKnown(SendBufferSize() / 1000) +{ + nServices = 0; + hSocket = hSocketIn; + nRecvVersion = INIT_PROTO_VERSION; + nLastSend = 0; + nLastRecv = 0; + nSendBytes = 0; + nRecvBytes = 0; + nTimeConnected = GetTime(); + nTimeOffset = 0; + 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 = uint256(); + nStartingHeight = -1; + fGetAddr = false; + fRelayTxes = false; + pfilter = new CBloomFilter(); + nPingNonceSent = 0; + nPingUsecStart = 0; + nPingUsecTime = 0; + fPingQueued = false; + nMinPingUsecTime = std::numeric_limits<int64_t>::max(); + + { + 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(Params().MessageStart(), 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) + { + LEAVE_CRITICAL_SECTION(cs_vSend); + return; + } + // Set the size + unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; + WriteLE32((uint8_t*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], 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); +} + +// +// CBanDB +// + +CBanDB::CBanDB() +{ + pathBanlist = GetDataDir() / "banlist.dat"; +} + +bool CBanDB::Write(const banmap_t& banSet) +{ + // Generate random temporary filename + unsigned short randv = 0; + GetRandBytes((unsigned char*)&randv, sizeof(randv)); + std::string tmpfn = strprintf("banlist.dat.%04x", randv); + + // serialize banlist, checksum data up to that point, then append csum + CDataStream ssBanlist(SER_DISK, CLIENT_VERSION); + ssBanlist << FLATDATA(Params().MessageStart()); + ssBanlist << banSet; + uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end()); + ssBanlist << hash; + + // open temp output file, and associate with CAutoFile + boost::filesystem::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fopen(pathTmp.string().c_str(), "wb"); + CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: Failed to open file %s", __func__, pathTmp.string()); + + // Write and commit header, data + try { + fileout << ssBanlist; + } + catch (const std::exception& e) { + return error("%s: Serialize or I/O error - %s", __func__, e.what()); + } + FileCommit(fileout.Get()); + fileout.fclose(); + + // replace existing banlist.dat, if any, with new banlist.dat.XXXX + if (!RenameOver(pathTmp, pathBanlist)) + return error("%s: Rename-into-place failed", __func__); + + return true; +} + +bool CBanDB::Read(banmap_t& banSet) +{ + // open input file, and associate with CAutoFile + FILE *file = fopen(pathBanlist.string().c_str(), "rb"); + CAutoFile filein(file, SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: Failed to open file %s", __func__, pathBanlist.string()); + + // use file size to size memory buffer + uint64_t fileSize = boost::filesystem::file_size(pathBanlist); + uint64_t dataSize = 0; + // Don't try to resize to a negative number if file is small + if (fileSize >= sizeof(uint256)) + dataSize = fileSize - sizeof(uint256); + 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 (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + filein.fclose(); + + CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.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 .. + ssBanlist >> 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 + ssBanlist >> banSet; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + + return true; +} + +void DumpBanlist() +{ + int64_t nStart = GetTimeMillis(); + + CNode::SweepBanned(); //clean unused entries (if bantime has expired) + + CBanDB bandb; + banmap_t banmap; + CNode::GetBanned(banmap); + bandb.Write(banmap); + + LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", + banmap.size(), GetTimeMillis() - nStart); +} |