From 0a61b0df1224a5470bcddab302bc199ca5a9e356 Mon Sep 17 00:00:00 2001 From: s_nakamoto Date: Sun, 29 Aug 2010 16:58:15 +0000 Subject: propset svn:eol-style native git-svn-id: https://bitcoin.svn.sourceforge.net/svnroot/bitcoin/trunk@146 1a98c847-1fd6-4fd8-948a-caf3550aa51b --- net.cpp | 2932 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 1466 insertions(+), 1466 deletions(-) (limited to 'net.cpp') diff --git a/net.cpp b/net.cpp index 1cdecd0980..9aaa38f32d 100644 --- a/net.cpp +++ b/net.cpp @@ -1,1466 +1,1466 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. - -#include "headers.h" - -static const int MAX_OUTBOUND_CONNECTIONS = 8; - -void ThreadMessageHandler2(void* parg); -void ThreadSocketHandler2(void* parg); -void ThreadOpenConnections2(void* parg); -bool OpenNetworkConnection(const CAddress& addrConnect); - - - - - -// -// Global state variables -// -bool fClient = false; -uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); -CAddress addrLocalHost(0, DEFAULT_PORT, nLocalServices); -CNode* pnodeLocalHost = NULL; -uint64 nLocalHostNonce = 0; -array vnThreadsRunning; -SOCKET hListenSocket = INVALID_SOCKET; -int64 nThreadSocketHandlerHeartbeat = INT64_MAX; - -vector vNodes; -CCriticalSection cs_vNodes; -map, CAddress> mapAddresses; -CCriticalSection cs_mapAddresses; -map mapRelay; -deque > vRelayExpiration; -CCriticalSection cs_mapRelay; -map mapAlreadyAskedFor; - -// Settings -int fUseProxy = false; -CAddress addrProxy("127.0.0.1:9050"); - - - - - -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); -} - - - - - -bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) -{ - hSocketRet = INVALID_SOCKET; - - SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (hSocket == INVALID_SOCKET) - return false; -#ifdef BSD - int set = 1; - setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); -#endif - - bool fRoutable = !(addrConnect.GetByte(3) == 10 || (addrConnect.GetByte(3) == 192 && addrConnect.GetByte(2) == 168)); - bool fProxy = (fUseProxy && fRoutable); - struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr()); - - if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) - { - closesocket(hSocket); - return false; - } - - if (fProxy) - { - printf("proxy connecting %s\n", addrConnect.ToStringLog().c_str()); - char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; - memcpy(pszSocks4IP + 2, &addrConnect.port, 2); - memcpy(pszSocks4IP + 4, &addrConnect.ip, 4); - char* pszSocks4 = pszSocks4IP; - int nSize = sizeof(pszSocks4IP); - - int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL); - if (ret != nSize) - { - closesocket(hSocket); - return error("Error sending to proxy"); - } - char pchRet[8]; - if (recv(hSocket, pchRet, 8, 0) != 8) - { - closesocket(hSocket); - return error("Error reading proxy response"); - } - if (pchRet[1] != 0x5a) - { - closesocket(hSocket); - if (pchRet[1] != 0x5b) - printf("ERROR: Proxy returned error %d\n", pchRet[1]); - return false; - } - printf("proxy connected %s\n", addrConnect.ToStringLog().c_str()); - } - - hSocketRet = hSocket; - return true; -} - - - -bool GetMyExternalIP2(const CAddress& addrConnect, const char* pszGet, const char* pszKeyword, unsigned int& 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()) - { - loop - { - if (!RecvLine(hSocket, strLine)) - { - closesocket(hSocket); - return false; - } - if (strLine.find(pszKeyword) != -1) - { - strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword)); - break; - } - } - closesocket(hSocket); - if (strLine.find("<")) - 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); - CAddress addr(strLine.c_str()); - printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); - if (addr.ip == 0 || addr.ip == INADDR_NONE || !addr.IsRoutable()) - return false; - ipRet = addr.ip; - return true; - } - } - closesocket(hSocket); - return error("GetMyExternalIP() : connection closed"); -} - - -bool GetMyExternalIP(unsigned int& ipRet) -{ - CAddress addrConnect; - const char* pszGet; - const char* pszKeyword; - - if (fUseProxy) - return false; - - for (int nLookup = 0; nLookup <= 1; nLookup++) - for (int nHost = 1; nHost <= 2; nHost++) - { - if (nHost == 1) - { - addrConnect = CAddress("70.86.96.218:80"); // www.ipaddressworld.com - - if (nLookup == 1) - { - struct hostent* phostent = gethostbyname("www.ipaddressworld.com"); - if (phostent && phostent->h_addr_list && phostent->h_addr_list[0]) - addrConnect = CAddress(*(u_long*)phostent->h_addr_list[0], htons(80)); - } - - pszGet = "GET /ip.php HTTP/1.1\r\n" - "Host: www.ipaddressworld.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 = "IP:"; - } - else if (nHost == 2) - { - addrConnect = CAddress("208.78.68.70:80"); // checkip.dyndns.org - - if (nLookup == 1) - { - struct hostent* phostent = gethostbyname("checkip.dyndns.org"); - if (phostent && phostent->h_addr_list && phostent->h_addr_list[0]) - addrConnect = CAddress(*(u_long*)phostent->h_addr_list[0], htons(80)); - } - - 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:"; - } - - if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) - return true; - } - - return false; -} - - - - - -bool AddAddress(CAddress addr) -{ - if (!addr.IsRoutable()) - return false; - if (addr.ip == addrLocalHost.ip) - return false; - CRITICAL_BLOCK(cs_mapAddresses) - { - map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); - if (it == mapAddresses.end()) - { - // New address - printf("AddAddress(%s)\n", addr.ToStringLog().c_str()); - mapAddresses.insert(make_pair(addr.GetKey(), addr)); - CAddrDB().WriteAddress(addr); - return true; - } - else - { - bool fUpdated = false; - CAddress& addrFound = (*it).second; - if ((addrFound.nServices | addr.nServices) != addrFound.nServices) - { - // Services have been added - addrFound.nServices |= addr.nServices; - fUpdated = true; - } - bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); - int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); - if (addrFound.nTime < addr.nTime - nUpdateInterval) - { - // Periodically update most recently seen time - addrFound.nTime = addr.nTime; - fUpdated = true; - } - if (fUpdated) - CAddrDB().WriteAddress(addrFound); - } - } - return false; -} - -void AddressCurrentlyConnected(const CAddress& addr) -{ - CRITICAL_BLOCK(cs_mapAddresses) - { - // Only if it's been published already - map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); - if (it != mapAddresses.end()) - { - CAddress& addrFound = (*it).second; - int64 nUpdateInterval = 20 * 60; - if (addrFound.nTime < GetAdjustedTime() - nUpdateInterval) - { - // Periodically update most recently seen time - addrFound.nTime = GetAdjustedTime(); - CAddrDB addrdb; - addrdb.WriteAddress(addrFound); - } - } - } -} - - - - - -void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) -{ - // If the dialog might get closed before the reply comes back, - // call this in the destructor so it doesn't get called after it's deleted. - CRITICAL_BLOCK(cs_vNodes) - { - foreach(CNode* pnode, vNodes) - { - CRITICAL_BLOCK(pnode->cs_mapRequests) - { - for (map::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) - { - CRequestTracker& tracker = (*mi).second; - if (tracker.fn == fn && tracker.param1 == param1) - pnode->mapRequests.erase(mi++); - else - mi++; - } - } - } - } -} - - - - - - - -// -// Subscription methods for the broadcast and subscription system. -// Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. -// -// The subscription system uses a meet-in-the-middle strategy. -// With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers -// subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. -// - -bool AnySubscribed(unsigned int nChannel) -{ - if (pnodeLocalHost->IsSubscribed(nChannel)) - return true; - CRITICAL_BLOCK(cs_vNodes) - foreach(CNode* pnode, vNodes) - if (pnode->IsSubscribed(nChannel)) - return true; - return false; -} - -bool CNode::IsSubscribed(unsigned int nChannel) -{ - if (nChannel >= vfSubscribe.size()) - return false; - return vfSubscribe[nChannel]; -} - -void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) -{ - if (nChannel >= vfSubscribe.size()) - return; - - if (!AnySubscribed(nChannel)) - { - // Relay subscribe - CRITICAL_BLOCK(cs_vNodes) - foreach(CNode* pnode, vNodes) - if (pnode != this) - pnode->PushMessage("subscribe", nChannel, nHops); - } - - vfSubscribe[nChannel] = true; -} - -void CNode::CancelSubscribe(unsigned int nChannel) -{ - if (nChannel >= vfSubscribe.size()) - return; - - // Prevent from relaying cancel if wasn't subscribed - if (!vfSubscribe[nChannel]) - return; - vfSubscribe[nChannel] = false; - - if (!AnySubscribed(nChannel)) - { - // Relay subscription cancel - CRITICAL_BLOCK(cs_vNodes) - foreach(CNode* pnode, vNodes) - if (pnode != this) - pnode->PushMessage("sub-cancel", nChannel); - } -} - - - - - - - - - -CNode* FindNode(unsigned int ip) -{ - CRITICAL_BLOCK(cs_vNodes) - { - foreach(CNode* pnode, vNodes) - if (pnode->addr.ip == ip) - return (pnode); - } - return NULL; -} - -CNode* FindNode(CAddress addr) -{ - CRITICAL_BLOCK(cs_vNodes) - { - foreach(CNode* pnode, vNodes) - if (pnode->addr == addr) - return (pnode); - } - return NULL; -} - -CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) -{ - if (addrConnect.ip == addrLocalHost.ip) - return NULL; - - // Look for an existing connection - CNode* pnode = FindNode(addrConnect.ip); - if (pnode) - { - if (nTimeout != 0) - pnode->AddRef(nTimeout); - else - pnode->AddRef(); - return pnode; - } - - /// debug print - printf("trying connection %s lastseen=%.1fhrs lasttry=%.1fhrs\n", - addrConnect.ToStringLog().c_str(), - (double)(addrConnect.nTime - GetAdjustedTime())/3600.0, - (double)(addrConnect.nLastTry - GetAdjustedTime())/3600.0); - - CRITICAL_BLOCK(cs_mapAddresses) - mapAddresses[addrConnect.GetKey()].nLastTry = GetAdjustedTime(); - - // Connect - SOCKET hSocket; - if (ConnectSocket(addrConnect, hSocket)) - { - /// debug print - printf("connected %s\n", addrConnect.ToStringLog().c_str()); - - // Set to nonblocking -#ifdef __WXMSW__ - u_long nOne = 1; - if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) - printf("ConnectSocket() : ioctlsocket nonblocking setting failed, error %d\n", WSAGetLastError()); -#else - if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) - printf("ConnectSocket() : fcntl nonblocking setting failed, error %d\n", errno); -#endif - - // Add node - CNode* pnode = new CNode(hSocket, addrConnect, false); - if (nTimeout != 0) - pnode->AddRef(nTimeout); - else - pnode->AddRef(); - CRITICAL_BLOCK(cs_vNodes) - vNodes.push_back(pnode); - - pnode->nTimeConnected = GetTime(); - return pnode; - } - else - { - return NULL; - } -} - -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", addr.ToStringLog().c_str()); - closesocket(hSocket); - hSocket = INVALID_SOCKET; - } -} - -void CNode::Cleanup() -{ - // All of a nodes broadcasts and subscriptions are automatically torn down - // when it goes down, so a node has to stay up to keep its broadcast going. - - // Cancel subscriptions - for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) - if (vfSubscribe[nChannel]) - CancelSubscribe(nChannel); -} - - - - - - - - - - - - - -void ThreadSocketHandler(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadSocketHandler(parg)); - try - { - vnThreadsRunning[0]++; - ThreadSocketHandler2(parg); - vnThreadsRunning[0]--; - } - catch (std::exception& e) { - vnThreadsRunning[0]--; - PrintException(&e, "ThreadSocketHandler()"); - } catch (...) { - vnThreadsRunning[0]--; - throw; // support pthread_cancel() - } - printf("ThreadSocketHandler exiting\n"); -} - -void ThreadSocketHandler2(void* parg) -{ - printf("ThreadSocketHandler started\n"); - list vNodesDisconnected; - int nPrevNodeCount = 0; - - loop - { - // - // Disconnect nodes - // - CRITICAL_BLOCK(cs_vNodes) - { - // Disconnect unused nodes - vector vNodesCopy = vNodes; - foreach(CNode* pnode, vNodesCopy) - { - if (pnode->fDisconnect || - (pnode->GetRefCount() <= 0 && pnode->vRecv.empty() && pnode->vSend.empty())) - { - // remove from vNodes - vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); - - // 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 vNodesDisconnectedCopy = vNodesDisconnected; - foreach(CNode* pnode, vNodesDisconnectedCopy) - { - // wait until threads are done using it - if (pnode->GetRefCount() <= 0) - { - bool fDelete = false; - TRY_CRITICAL_BLOCK(pnode->cs_vSend) - TRY_CRITICAL_BLOCK(pnode->cs_vRecv) - TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) - TRY_CRITICAL_BLOCK(pnode->cs_inventory) - fDelete = true; - if (fDelete) - { - vNodesDisconnected.remove(pnode); - delete pnode; - } - } - } - } - if (vNodes.size() != nPrevNodeCount) - { - nPrevNodeCount = vNodes.size(); - MainFrameRepaint(); - } - - - // - // Find which sockets have data to receive - // - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 50000; // frequency to poll pnode->vSend - - fd_set fdsetRecv; - fd_set fdsetSend; - fd_set fdsetError; - FD_ZERO(&fdsetRecv); - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - SOCKET hSocketMax = 0; - FD_SET(hListenSocket, &fdsetRecv); - hSocketMax = max(hSocketMax, hListenSocket); - CRITICAL_BLOCK(cs_vNodes) - { - foreach(CNode* pnode, vNodes) - { - if (pnode->hSocket == INVALID_SOCKET || pnode->hSocket < 0) - continue; - FD_SET(pnode->hSocket, &fdsetRecv); - FD_SET(pnode->hSocket, &fdsetError); - hSocketMax = max(hSocketMax, pnode->hSocket); - TRY_CRITICAL_BLOCK(pnode->cs_vSend) - if (!pnode->vSend.empty()) - FD_SET(pnode->hSocket, &fdsetSend); - } - } - - vnThreadsRunning[0]--; - int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); - vnThreadsRunning[0]++; - if (fShutdown) - return; - if (nSelect == SOCKET_ERROR) - { - int nErr = WSAGetLastError(); - printf("socket select error %d\n", nErr); - for (int i = 0; i <= hSocketMax; i++) - FD_SET(i, &fdsetRecv); - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - Sleep(timeout.tv_usec/1000); - } - - - // - // Accept new connections - // - if (FD_ISSET(hListenSocket, &fdsetRecv)) - { - struct sockaddr_in sockaddr; - socklen_t len = sizeof(sockaddr); - SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); - CAddress addr(sockaddr); - if (hSocket == INVALID_SOCKET) - { - if (WSAGetLastError() != WSAEWOULDBLOCK) - printf("socket error accept failed: %d\n", WSAGetLastError()); - } - else if (mapArgs.count("-maxconnections") && (int)vNodes.size() >= atoi(mapArgs["-maxconnections"]) - MAX_OUTBOUND_CONNECTIONS) - { - closesocket(hSocket); - } - else - { - printf("accepted connection %s\n", addr.ToStringLog().c_str()); - CNode* pnode = new CNode(hSocket, addr, true); - pnode->AddRef(); - CRITICAL_BLOCK(cs_vNodes) - vNodes.push_back(pnode); - } - } - - - // - // Service each socket - // - vector vNodesCopy; - CRITICAL_BLOCK(cs_vNodes) - { - vNodesCopy = vNodes; - foreach(CNode* pnode, vNodesCopy) - pnode->AddRef(); - } - foreach(CNode* pnode, vNodesCopy) - { - if (fShutdown) - return; - - // - // Receive - // - if (pnode->hSocket == INVALID_SOCKET) - continue; - if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) - { - TRY_CRITICAL_BLOCK(pnode->cs_vRecv) - { - CDataStream& vRecv = pnode->vRecv; - unsigned int nPos = vRecv.size(); - - // typical socket buffer is 8K-64K - char pchBuf[0x10000]; - int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); - if (nBytes > 0) - { - vRecv.resize(nPos + nBytes); - memcpy(&vRecv[nPos], pchBuf, nBytes); - pnode->nLastRecv = GetTime(); - } - else if (nBytes == 0) - { - // socket closed gracefully - if (!pnode->fDisconnect) - printf("socket closed\n"); - pnode->CloseSocketDisconnect(); - } - else if (nBytes < 0) - { - // error - int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) - { - if (!pnode->fDisconnect) - printf("socket recv error %d\n", nErr); - pnode->CloseSocketDisconnect(); - } - } - } - } - - // - // Send - // - if (pnode->hSocket == INVALID_SOCKET) - continue; - if (FD_ISSET(pnode->hSocket, &fdsetSend)) - { - TRY_CRITICAL_BLOCK(pnode->cs_vSend) - { - CDataStream& vSend = pnode->vSend; - if (!vSend.empty()) - { - int nBytes = send(pnode->hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL | MSG_DONTWAIT); - if (nBytes > 0) - { - vSend.erase(vSend.begin(), vSend.begin() + nBytes); - pnode->nLastSend = GetTime(); - } - else if (nBytes < 0) - { - // error - int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) - { - printf("socket send error %d\n", nErr); - pnode->CloseSocketDisconnect(); - } - } - } - } - } - - // - // Inactivity checking - // - if (pnode->vSend.empty()) - pnode->nLastSendEmpty = GetTime(); - if (GetTime() - pnode->nTimeConnected > 60) - { - if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) - { - printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); - pnode->fDisconnect = true; - } - else if (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60) - { - printf("socket not sending\n"); - pnode->fDisconnect = true; - } - else if (GetTime() - pnode->nLastRecv > 90*60) - { - printf("socket inactivity timeout\n"); - pnode->fDisconnect = true; - } - } - } - CRITICAL_BLOCK(cs_vNodes) - { - foreach(CNode* pnode, vNodesCopy) - pnode->Release(); - } - - nThreadSocketHandlerHeartbeat = GetTime(); - Sleep(10); - } -} - - - - - - - - - - - - - -unsigned int pnSeed[] = -{ - // 2010/06 - 0x35218252, 0x9c9c9618, 0xda6bacad, 0xb9aca862, 0x97c235c6, - 0x146f9562, 0xb67b9e4b, 0x87cf4bc0, 0xb83945d0, 0x984333ad, - 0xbbeec555, 0x6f0eb440, 0xe0005318, 0x7797e460, 0xddc60fcc, - 0xb3bbd24a, 0x1ac85746, 0x641846a6, 0x85ee1155, 0xbb2e7a4c, - 0x9cb8514b, 0xfc342648, 0x62958fae, 0xd0a8c87a, 0xa800795b, - 0xda8c814e, 0x256a0c80, 0x3f23ec63, 0xd565df43, 0x997d9044, - 0xaa121448, 0xbed8688e, 0x59d09a5e, 0xb2931243, 0x3730ba18, - 0xdd3462d0, 0x4e4d1448, 0x171df645, 0x84ee1155, - 0x248ac445, 0x0e634444, 0x0ded1b63, 0x30c01e60, - 0xa2b9a094, 0x29e4fd43, 0x9ce61b4c, 0xdae09744, - - // 2010/08 - 0x5ae6bf43, 0x460be257, 0x7245c0cf, 0x4e0f028d, 0x26501760, 0x38643255, 0x67094f4f, 0x480449b8, - 0x16545143, 0x1f082e5a, 0xaa428018, 0xe411e793, 0x14c1f862, 0x2726105b, 0x9b33ea50, 0xeeef86ca, - 0xe3210d44, 0x0dca8b63, 0x3f9dfb18, 0x860340ad, 0xf33ba17a, 0x9018375c, 0x1de4e353, 0x0fa52dcb, - 0x89c4555b, 0x109cf37b, 0x28c55b40, 0x04c801ae, 0x275c1e80, 0x6f7f745d, 0x7a2a5653, 0xa28e26d8, - 0xa4e65db2, 0x99a06580, 0xf253ba44, 0x82cf6ab8, 0x859c2e8e, 0xf71a815d, 0xc18f1454, 0x71c8a943, - 0x90d24e18, 0x311789b2, 0x74aba645, 0xde0bbfc3, 0xad724fad, 0xbf1ae15e, 0xbaa6fb54, 0x06e4d145, - 0x51528645, 0x72120cd4, 0xd4cfd145, 0x0a7afed8, 0x9b9a5fad, 0x9e9ff45e, 0x10128355, 0xd44e8646, - 0x04a07b47, 0x5fc9d547, 0xe0491e45, 0xbac21b41, 0x7aa31bae, 0x10483c5f, 0x94a23055, 0x73d9dc47, - 0x1a99c247, 0x822fe847, 0x7e57ba48, 0xb19ea843, 0xa60621b2, 0x778cf163, 0x125c6556, 0xf94ba44f, - 0xa61a0948, 0x6c839e4b, 0x29af5348, 0x68d84845, 0x752b95c3, 0xcf0d4663, 0x08e11e56, 0x75109550, - 0x5f24b94c, 0x42426d4d, 0xfbbc0a4c, 0x70a9a246, 0xda7837cb, 0xae2a986d, 0xe283c358, 0x0c7ca954, - 0x8e9bde59, 0x61521760, 0x6884444c, 0xa194e548, 0x9b8809cc, 0x16e96a8f, 0x956ff859, 0xfad5e555, - 0x0ea70c80, 0x5b4ce26d, 0x7984444c, 0x1080d24a, 0x22a686cf, 0x6bf8c2ad, 0xb0f7485f, 0x06b66e56, - 0x668373bc, 0x75506279, 0x3868694e, 0x12a5954b, 0x3a8b62d1, 0xb74fcbad, 0xa7dc3360, 0xc070b359, - 0xa2b87242, 0xc45cab7c, 0x69882050, 0x14a5464b, 0x386acad5, 0x80b85db2, 0x1f78a062, 0xc608c55b, - 0x4257d543, 0x7636ad80, 0x4432d655, 0xb2114d4b, 0x32639bd9, 0xadd75db2, 0x9be5a362, 0x6831bc5e, - 0xf7f77046, 0x8f35ba81, 0x09bb4e59, 0xd0fb6b4e, 0xc5daa445, 0x9c611618, 0x355dcc62, 0xf2cf435e, - 0x31e72c46, 0xdd8a43ad, 0x171f9c5b, 0xb4c2e355, 0xbe8af945, 0x613d3942, 0xe6f9e863, 0x7a3d855f, - 0xa66adc47, 0x261089b2, 0x5a27105b, 0x6c28105b, 0xdd247946, 0xe6c3a445, 0x43a1ec63, 0x99b4dd5f, - 0xb6834347, 0x5e9649bc, 0xf9dd545d, 0x6ae4c15b, 0xa5318a47, 0x7984ec47, 0x93a73b63, 0x0c60195f, - 0xa5c85e4b, 0xa0a36dc2, 0x0739a95e, 0x3d44c15b, 0xfb940f4b, 0xd67c9148, 0x614f9876, 0x0a241c5f, - 0xad9da74c, 0x4459abc8, 0x12e71b5f, 0x1c534a5d, 0x8ff5fc50, 0x2ca8864b, 0xd894fd80, 0x82ab3160, - 0x390d804e, 0x2cf310cc, 0x680dad80, 0x691be15e, 0x5a8f4652, 0xaad0784d, 0x0d2431ad, -}; - - - -void ThreadOpenConnections(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadOpenConnections(parg)); - try - { - vnThreadsRunning[1]++; - ThreadOpenConnections2(parg); - vnThreadsRunning[1]--; - } - catch (std::exception& e) { - vnThreadsRunning[1]--; - PrintException(&e, "ThreadOpenConnections()"); - } catch (...) { - vnThreadsRunning[1]--; - PrintException(NULL, "ThreadOpenConnections()"); - } - printf("ThreadOpenConnections exiting\n"); -} - -void ThreadOpenConnections2(void* parg) -{ - printf("ThreadOpenConnections started\n"); - - // Connect to specific addresses - if (mapArgs.count("-connect")) - { - for (int64 nLoop = 0;; nLoop++) - { - foreach(string strAddr, mapMultiArgs["-connect"]) - { - CAddress addr(strAddr, NODE_NETWORK); - if (addr.IsValid()) - OpenNetworkConnection(addr); - for (int i = 0; i < 10 && i < nLoop; i++) - { - Sleep(500); - if (fShutdown) - return; - } - } - } - } - - // Connect to manually added nodes first - if (mapArgs.count("-addnode")) - { - foreach(string strAddr, mapMultiArgs["-addnode"]) - { - CAddress addr(strAddr, NODE_NETWORK); - if (addr.IsValid()) - { - OpenNetworkConnection(addr); - Sleep(500); - if (fShutdown) - return; - } - } - } - - // Initiate network connections - int64 nStart = GetTime(); - loop - { - // Limit outbound connections - vnThreadsRunning[1]--; - Sleep(500); - loop - { - int nOutbound = 0; - CRITICAL_BLOCK(cs_vNodes) - foreach(CNode* pnode, vNodes) - if (!pnode->fInbound) - nOutbound++; - int nMaxOutboundConnections = MAX_OUTBOUND_CONNECTIONS; - if (mapArgs.count("-maxconnections")) - nMaxOutboundConnections = min(nMaxOutboundConnections, atoi(mapArgs["-maxconnections"])); - if (nOutbound < nMaxOutboundConnections) - break; - Sleep(2000); - if (fShutdown) - return; - } - vnThreadsRunning[1]++; - if (fShutdown) - return; - - CRITICAL_BLOCK(cs_mapAddresses) - { - // Add seed nodes if IRC isn't working - static bool fSeedUsed; - bool fTOR = (fUseProxy && addrProxy.port == htons(9050)); - if (mapAddresses.empty() && (GetTime() - nStart > 60 || fTOR)) - { - for (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. - CAddress addr; - addr.ip = pnSeed[i]; - addr.nTime = 0; - AddAddress(addr); - } - fSeedUsed = true; - } - - if (fSeedUsed && mapAddresses.size() > ARRAYLEN(pnSeed) + 100) - { - // Disconnect seed nodes - set setSeed(pnSeed, pnSeed + ARRAYLEN(pnSeed)); - static int64 nSeedDisconnected; - if (nSeedDisconnected == 0) - { - nSeedDisconnected = GetTime(); - CRITICAL_BLOCK(cs_vNodes) - foreach(CNode* pnode, vNodes) - if (setSeed.count(pnode->addr.ip)) - pnode->fDisconnect = true; - } - - // Keep setting timestamps to 0 so they won't reconnect - if (GetTime() - nSeedDisconnected < 60 * 60) - { - foreach(PAIRTYPE(const vector, CAddress)& item, mapAddresses) - { - if (setSeed.count(item.second.ip)) - { - item.second.nTime = 0; - CAddrDB().WriteAddress(item.second); - } - } - } - } - } - - - // - // Choose an address to connect to based on most recently seen - // - CAddress addrConnect; - int64 nBest = INT64_MIN; - - // Only connect to one address per a.b.?.? range. - // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. - set setConnected; - CRITICAL_BLOCK(cs_vNodes) - foreach(CNode* pnode, vNodes) - setConnected.insert(pnode->addr.ip & 0x0000ffff); - - CRITICAL_BLOCK(cs_mapAddresses) - { - foreach(const PAIRTYPE(vector, CAddress)& item, mapAddresses) - { - const CAddress& addr = item.second; - if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.ip & 0x0000ffff)) - continue; - int64 nSinceLastSeen = GetAdjustedTime() - addr.nTime; - int64 nSinceLastTry = GetAdjustedTime() - addr.nLastTry; - - // Randomize the order in a deterministic way, putting the standard port first - int64 nRandomizer = (uint64)(nStart * 4951 + addr.nLastTry * 9567851 + addr.ip * 7789) % (2 * 60 * 60); - if (addr.port != DEFAULT_PORT) - nRandomizer += 2 * 60 * 60; - - // Last seen Base retry frequency - // <1 hour 10 min - // 1 hour 1 hour - // 4 hours 2 hours - // 24 hours 5 hours - // 48 hours 7 hours - // 7 days 13 hours - // 30 days 27 hours - // 90 days 46 hours - // 365 days 93 hours - int64 nDelay = (int64)(3600.0 * sqrt(fabs((double)nSinceLastSeen) / 3600.0) + nRandomizer); - - // Fast reconnect for one hour after last seen - if (nSinceLastSeen < 60 * 60) - nDelay = 10 * 60; - - // Limit retry frequency - if (nSinceLastTry < nDelay) - continue; - - // If we have IRC, we'll be notified when they first come online, - // and again every 24 hours by the refresh broadcast. - if (nGotIRCAddresses > 0 && vNodes.size() >= 2 && nSinceLastSeen > 24 * 60 * 60) - continue; - - // Only try the old stuff if we don't have enough connections - if (vNodes.size() >= 8 && nSinceLastSeen > 24 * 60 * 60) - continue; - - // If multiple addresses are ready, prioritize by time since - // last seen and time since last tried. - int64 nScore = min(nSinceLastTry, (int64)24 * 60 * 60) - nSinceLastSeen - nRandomizer; - if (nScore > nBest) - { - nBest = nScore; - addrConnect = addr; - } - } - } - - if (addrConnect.IsValid()) - OpenNetworkConnection(addrConnect); - } -} - -bool OpenNetworkConnection(const CAddress& addrConnect) -{ - // - // Initiate outbound network connection - // - if (fShutdown) - return false; - if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() || FindNode(addrConnect.ip)) - return false; - - vnThreadsRunning[1]--; - CNode* pnode = ConnectNode(addrConnect); - vnThreadsRunning[1]++; - if (fShutdown) - return false; - if (!pnode) - return false; - pnode->fNetworkNode = true; - - if (addrLocalHost.IsRoutable() && !fUseProxy) - { - // Advertise our address - vector vAddr; - vAddr.push_back(addrLocalHost); - pnode->PushMessage("addr", vAddr); - } - - // Get as many addresses as we can - pnode->PushMessage("getaddr"); - pnode->fGetAddr = true; // don't relay the results of the getaddr - - ////// should the one on the receiving end do this too? - // Subscribe our local subscription list - const unsigned int nHops = 0; - for (unsigned int nChannel = 0; nChannel < pnodeLocalHost->vfSubscribe.size(); nChannel++) - if (pnodeLocalHost->vfSubscribe[nChannel]) - pnode->PushMessage("subscribe", nChannel, nHops); - - return true; -} - - - - - - - - -void ThreadMessageHandler(void* parg) -{ - IMPLEMENT_RANDOMIZE_STACK(ThreadMessageHandler(parg)); - try - { - vnThreadsRunning[2]++; - ThreadMessageHandler2(parg); - vnThreadsRunning[2]--; - } - catch (std::exception& e) { - vnThreadsRunning[2]--; - PrintException(&e, "ThreadMessageHandler()"); - } catch (...) { - vnThreadsRunning[2]--; - PrintException(NULL, "ThreadMessageHandler()"); - } - printf("ThreadMessageHandler exiting\n"); -} - -void ThreadMessageHandler2(void* parg) -{ - printf("ThreadMessageHandler started\n"); - SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); - while (!fShutdown) - { - vector vNodesCopy; - CRITICAL_BLOCK(cs_vNodes) - { - vNodesCopy = vNodes; - foreach(CNode* pnode, vNodesCopy) - pnode->AddRef(); - } - - // Poll the connected nodes for messages - CNode* pnodeTrickle = NULL; - if (!vNodesCopy.empty()) - pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; - foreach(CNode* pnode, vNodesCopy) - { - // Receive messages - TRY_CRITICAL_BLOCK(pnode->cs_vRecv) - ProcessMessages(pnode); - if (fShutdown) - return; - - // Send messages - TRY_CRITICAL_BLOCK(pnode->cs_vSend) - SendMessages(pnode, pnode == pnodeTrickle); - if (fShutdown) - return; - } - - CRITICAL_BLOCK(cs_vNodes) - { - foreach(CNode* pnode, vNodesCopy) - pnode->Release(); - } - - // Wait and allow messages to bunch up - vnThreadsRunning[2]--; - Sleep(100); - vnThreadsRunning[2]++; - if (fShutdown) - return; - } -} - - - - - - - - - -bool BindListenPort(string& strError) -{ - strError = ""; - int nOne = 1; - -#ifdef __WXMSW__ - // 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 - hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (hListenSocket == INVALID_SOCKET) - { - strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - -#ifdef BSD - // Different way of disabling SIGPIPE on BSD - setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); -#endif - -#ifndef __WXMSW__ - // Allow binding if the port is still in TIME_WAIT state after - // the program was closed and restarted. Not an issue on windows. - setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); -#endif - -#ifdef __WXMSW__ - // Set to nonblocking, incoming connections will also inherit this - if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR) -#else - if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) -#endif - { - strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - - // The sockaddr_in structure specifies the address family, - // IP address, and port for the socket that is being bound - struct sockaddr_in sockaddr; - memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sin_family = AF_INET; - sockaddr.sin_addr.s_addr = INADDR_ANY; // bind to all IPs on this computer - sockaddr.sin_port = DEFAULT_PORT; - if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) - { - int nErr = WSAGetLastError(); - if (nErr == WSAEADDRINUSE) - strError = strprintf("Unable to bind to port %d on this computer. Bitcoin is probably already running.", ntohs(sockaddr.sin_port)); - else - strError = strprintf("Error: Unable to bind to port %d on this computer (bind returned error %d)", ntohs(sockaddr.sin_port), nErr); - printf("%s\n", strError.c_str()); - return false; - } - printf("Bound to port %d\n", ntohs(sockaddr.sin_port)); - - // Listen for incoming connections - if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) - { - strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - - return true; -} - -void StartNode(void* parg) -{ - if (pnodeLocalHost == NULL) - pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); - -#ifdef __WXMSW__ - // Get local host ip - char pszHostName[1000] = ""; - if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) - { - struct hostent* phostent = gethostbyname(pszHostName); - if (phostent) - { - // Take the first IP that isn't loopback 127.x.x.x - for (int i = 0; phostent->h_addr_list[i] != NULL; i++) - printf("host ip %d: %s\n", i, CAddress(*(unsigned int*)phostent->h_addr_list[i]).ToStringIP().c_str()); - for (int i = 0; phostent->h_addr_list[i] != NULL; i++) - { - CAddress addr(*(unsigned int*)phostent->h_addr_list[i], DEFAULT_PORT, nLocalServices); - if (addr.IsValid() && addr.GetByte(3) != 127) - { - addrLocalHost = addr; - break; - } - } - } - } -#else - // Get local host ip - struct ifaddrs* myaddrs; - if (getifaddrs(&myaddrs) == 0) - { - for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) - { - if (ifa->ifa_addr == NULL) continue; - if ((ifa->ifa_flags & IFF_UP) == 0) continue; - if (strcmp(ifa->ifa_name, "lo") == 0) continue; - if (strcmp(ifa->ifa_name, "lo0") == 0) continue; - char pszIP[100]; - if (ifa->ifa_addr->sa_family == AF_INET) - { - struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); - if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s4->sin_addr), pszIP, sizeof(pszIP)) != NULL) - printf("ipv4 %s: %s\n", ifa->ifa_name, pszIP); - - // Take the first IP that isn't loopback 127.x.x.x - CAddress addr(*(unsigned int*)&s4->sin_addr, DEFAULT_PORT, nLocalServices); - if (addr.IsValid() && addr.GetByte(3) != 127) - { - addrLocalHost = addr; - break; - } - } - else if (ifa->ifa_addr->sa_family == AF_INET6) - { - struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); - if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s6->sin6_addr), pszIP, sizeof(pszIP)) != NULL) - printf("ipv6 %s: %s\n", ifa->ifa_name, pszIP); - } - } - freeifaddrs(myaddrs); - } -#endif - printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); - - // Get our external IP address for incoming connections - if (fUseProxy) - { - // Proxies can't take incoming connections - addrLocalHost.ip = CAddress("0.0.0.0").ip; - printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); - } - else - { - if (addrIncoming.IsValid()) - addrLocalHost.ip = addrIncoming.ip; - - if (GetMyExternalIP(addrLocalHost.ip)) - { - addrIncoming = addrLocalHost; - CWalletDB().WriteSetting("addrIncoming", addrIncoming); - printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); - } - } - - // - // Start threads - // - - // Get addresses from IRC and advertise ours - if (!CreateThread(ThreadIRCSeed, NULL)) - printf("Error: CreateThread(ThreadIRCSeed) failed\n"); - - // Send and receive from sockets, accept connections - pthread_t hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); - - // Initiate outbound connections - if (!CreateThread(ThreadOpenConnections, NULL)) - printf("Error: CreateThread(ThreadOpenConnections) failed\n"); - - // Process messages - if (!CreateThread(ThreadMessageHandler, NULL)) - printf("Error: CreateThread(ThreadMessageHandler) failed\n"); - - // Generate coins in the background - GenerateBitcoins(fGenerateBitcoins); - - // - // Thread monitoring - // Not really needed anymore, the cause of the hanging was fixed - // - loop - { - Sleep(1000); - if (fShutdown) - return; - if (GetTime() - nThreadSocketHandlerHeartbeat > 15 * 60) - { - // First see if closing sockets will free it - printf("*** ThreadSocketHandler is stopped ***\n"); - CRITICAL_BLOCK(cs_vNodes) - { - foreach(CNode* pnode, vNodes) - { - bool fGot = false; - TRY_CRITICAL_BLOCK(pnode->cs_vRecv) - TRY_CRITICAL_BLOCK(pnode->cs_vSend) - fGot = true; - if (!fGot) - { - printf("*** closing socket\n"); - pnode->CloseSocketDisconnect(); - } - } - } - Sleep(10000); - if (fShutdown) - return; - if (GetTime() - nThreadSocketHandlerHeartbeat < 60) - continue; - - // Hopefully it never comes to this. - // We know it'll always be hung in the recv or send call. - // cs_vRecv or cs_vSend may be left permanently unreleased, - // but we always only use TRY_CRITICAL_SECTION on them. - printf("*** Restarting ThreadSocketHandler ***\n"); - TerminateThread(hThreadSocketHandler, 0); - #ifdef __WXMSW__ - CloseHandle(hThreadSocketHandler); - #endif - vnThreadsRunning[0] = 0; - - // Restart - hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); - nThreadSocketHandlerHeartbeat = GetTime(); - } - } -} - -bool StopNode() -{ - printf("StopNode()\n"); - fShutdown = true; - nTransactionsUpdated++; - int64 nStart = GetTime(); - while (vnThreadsRunning[0] > 0 || vnThreadsRunning[2] > 0 || vnThreadsRunning[3] > 0 || vnThreadsRunning[4] > 0) - { - if (GetTime() - nStart > 20) - break; - Sleep(20); - } - if (vnThreadsRunning[0] > 0) printf("ThreadSocketHandler still running\n"); - if (vnThreadsRunning[1] > 0) printf("ThreadOpenConnections still running\n"); - if (vnThreadsRunning[2] > 0) printf("ThreadMessageHandler still running\n"); - if (vnThreadsRunning[3] > 0) printf("ThreadBitcoinMiner still running\n"); - if (vnThreadsRunning[4] > 0) printf("ThreadRPCServer still running\n"); - while (vnThreadsRunning[2] > 0 || vnThreadsRunning[4] > 0) - Sleep(20); - Sleep(50); - - return true; -} - -class CNetCleanup -{ -public: - CNetCleanup() - { - } - ~CNetCleanup() - { - // Close sockets - foreach(CNode* pnode, vNodes) - if (pnode->hSocket != INVALID_SOCKET) - closesocket(pnode->hSocket); - if (hListenSocket != INVALID_SOCKET) - if (closesocket(hListenSocket) == SOCKET_ERROR) - printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); - -#ifdef __WXMSW__ - // Shutdown Windows Sockets - WSACleanup(); -#endif - } -} -instance_of_cnetcleanup; +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + +static const int MAX_OUTBOUND_CONNECTIONS = 8; + +void ThreadMessageHandler2(void* parg); +void ThreadSocketHandler2(void* parg); +void ThreadOpenConnections2(void* parg); +bool OpenNetworkConnection(const CAddress& addrConnect); + + + + + +// +// Global state variables +// +bool fClient = false; +uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); +CAddress addrLocalHost(0, DEFAULT_PORT, nLocalServices); +CNode* pnodeLocalHost = NULL; +uint64 nLocalHostNonce = 0; +array vnThreadsRunning; +SOCKET hListenSocket = INVALID_SOCKET; +int64 nThreadSocketHandlerHeartbeat = INT64_MAX; + +vector vNodes; +CCriticalSection cs_vNodes; +map, CAddress> mapAddresses; +CCriticalSection cs_mapAddresses; +map mapRelay; +deque > vRelayExpiration; +CCriticalSection cs_mapRelay; +map mapAlreadyAskedFor; + +// Settings +int fUseProxy = false; +CAddress addrProxy("127.0.0.1:9050"); + + + + + +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); +} + + + + + +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) +{ + hSocketRet = INVALID_SOCKET; + + SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (hSocket == INVALID_SOCKET) + return false; +#ifdef BSD + int set = 1; + setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); +#endif + + bool fRoutable = !(addrConnect.GetByte(3) == 10 || (addrConnect.GetByte(3) == 192 && addrConnect.GetByte(2) == 168)); + bool fProxy = (fUseProxy && fRoutable); + struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr()); + + if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + { + closesocket(hSocket); + return false; + } + + if (fProxy) + { + printf("proxy connecting %s\n", addrConnect.ToStringLog().c_str()); + char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; + memcpy(pszSocks4IP + 2, &addrConnect.port, 2); + memcpy(pszSocks4IP + 4, &addrConnect.ip, 4); + char* pszSocks4 = pszSocks4IP; + int nSize = sizeof(pszSocks4IP); + + int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL); + if (ret != nSize) + { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet[8]; + if (recv(hSocket, pchRet, 8, 0) != 8) + { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet[1] != 0x5a) + { + closesocket(hSocket); + if (pchRet[1] != 0x5b) + printf("ERROR: Proxy returned error %d\n", pchRet[1]); + return false; + } + printf("proxy connected %s\n", addrConnect.ToStringLog().c_str()); + } + + hSocketRet = hSocket; + return true; +} + + + +bool GetMyExternalIP2(const CAddress& addrConnect, const char* pszGet, const char* pszKeyword, unsigned int& 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()) + { + loop + { + if (!RecvLine(hSocket, strLine)) + { + closesocket(hSocket); + return false; + } + if (strLine.find(pszKeyword) != -1) + { + strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword)); + break; + } + } + closesocket(hSocket); + if (strLine.find("<")) + 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); + CAddress addr(strLine.c_str()); + printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); + if (addr.ip == 0 || addr.ip == INADDR_NONE || !addr.IsRoutable()) + return false; + ipRet = addr.ip; + return true; + } + } + closesocket(hSocket); + return error("GetMyExternalIP() : connection closed"); +} + + +bool GetMyExternalIP(unsigned int& ipRet) +{ + CAddress addrConnect; + const char* pszGet; + const char* pszKeyword; + + if (fUseProxy) + return false; + + for (int nLookup = 0; nLookup <= 1; nLookup++) + for (int nHost = 1; nHost <= 2; nHost++) + { + if (nHost == 1) + { + addrConnect = CAddress("70.86.96.218:80"); // www.ipaddressworld.com + + if (nLookup == 1) + { + struct hostent* phostent = gethostbyname("www.ipaddressworld.com"); + if (phostent && phostent->h_addr_list && phostent->h_addr_list[0]) + addrConnect = CAddress(*(u_long*)phostent->h_addr_list[0], htons(80)); + } + + pszGet = "GET /ip.php HTTP/1.1\r\n" + "Host: www.ipaddressworld.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 = "IP:"; + } + else if (nHost == 2) + { + addrConnect = CAddress("208.78.68.70:80"); // checkip.dyndns.org + + if (nLookup == 1) + { + struct hostent* phostent = gethostbyname("checkip.dyndns.org"); + if (phostent && phostent->h_addr_list && phostent->h_addr_list[0]) + addrConnect = CAddress(*(u_long*)phostent->h_addr_list[0], htons(80)); + } + + 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:"; + } + + if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) + return true; + } + + return false; +} + + + + + +bool AddAddress(CAddress addr) +{ + if (!addr.IsRoutable()) + return false; + if (addr.ip == addrLocalHost.ip) + return false; + CRITICAL_BLOCK(cs_mapAddresses) + { + map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); + if (it == mapAddresses.end()) + { + // New address + printf("AddAddress(%s)\n", addr.ToStringLog().c_str()); + mapAddresses.insert(make_pair(addr.GetKey(), addr)); + CAddrDB().WriteAddress(addr); + return true; + } + else + { + bool fUpdated = false; + CAddress& addrFound = (*it).second; + if ((addrFound.nServices | addr.nServices) != addrFound.nServices) + { + // Services have been added + addrFound.nServices |= addr.nServices; + fUpdated = true; + } + bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); + int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); + if (addrFound.nTime < addr.nTime - nUpdateInterval) + { + // Periodically update most recently seen time + addrFound.nTime = addr.nTime; + fUpdated = true; + } + if (fUpdated) + CAddrDB().WriteAddress(addrFound); + } + } + return false; +} + +void AddressCurrentlyConnected(const CAddress& addr) +{ + CRITICAL_BLOCK(cs_mapAddresses) + { + // Only if it's been published already + map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); + if (it != mapAddresses.end()) + { + CAddress& addrFound = (*it).second; + int64 nUpdateInterval = 20 * 60; + if (addrFound.nTime < GetAdjustedTime() - nUpdateInterval) + { + // Periodically update most recently seen time + addrFound.nTime = GetAdjustedTime(); + CAddrDB addrdb; + addrdb.WriteAddress(addrFound); + } + } + } +} + + + + + +void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) +{ + // If the dialog might get closed before the reply comes back, + // call this in the destructor so it doesn't get called after it's deleted. + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + { + CRITICAL_BLOCK(pnode->cs_mapRequests) + { + for (map::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) + { + CRequestTracker& tracker = (*mi).second; + if (tracker.fn == fn && tracker.param1 == param1) + pnode->mapRequests.erase(mi++); + else + mi++; + } + } + } + } +} + + + + + + + +// +// Subscription methods for the broadcast and subscription system. +// Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. +// +// The subscription system uses a meet-in-the-middle strategy. +// With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers +// subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. +// + +bool AnySubscribed(unsigned int nChannel) +{ + if (pnodeLocalHost->IsSubscribed(nChannel)) + return true; + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (pnode->IsSubscribed(nChannel)) + return true; + return false; +} + +bool CNode::IsSubscribed(unsigned int nChannel) +{ + if (nChannel >= vfSubscribe.size()) + return false; + return vfSubscribe[nChannel]; +} + +void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) +{ + if (nChannel >= vfSubscribe.size()) + return; + + if (!AnySubscribed(nChannel)) + { + // Relay subscribe + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (pnode != this) + pnode->PushMessage("subscribe", nChannel, nHops); + } + + vfSubscribe[nChannel] = true; +} + +void CNode::CancelSubscribe(unsigned int nChannel) +{ + if (nChannel >= vfSubscribe.size()) + return; + + // Prevent from relaying cancel if wasn't subscribed + if (!vfSubscribe[nChannel]) + return; + vfSubscribe[nChannel] = false; + + if (!AnySubscribed(nChannel)) + { + // Relay subscription cancel + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (pnode != this) + pnode->PushMessage("sub-cancel", nChannel); + } +} + + + + + + + + + +CNode* FindNode(unsigned int ip) +{ + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + if (pnode->addr.ip == ip) + return (pnode); + } + return NULL; +} + +CNode* FindNode(CAddress addr) +{ + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + if (pnode->addr == addr) + return (pnode); + } + return NULL; +} + +CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) +{ + if (addrConnect.ip == addrLocalHost.ip) + return NULL; + + // Look for an existing connection + CNode* pnode = FindNode(addrConnect.ip); + if (pnode) + { + if (nTimeout != 0) + pnode->AddRef(nTimeout); + else + pnode->AddRef(); + return pnode; + } + + /// debug print + printf("trying connection %s lastseen=%.1fhrs lasttry=%.1fhrs\n", + addrConnect.ToStringLog().c_str(), + (double)(addrConnect.nTime - GetAdjustedTime())/3600.0, + (double)(addrConnect.nLastTry - GetAdjustedTime())/3600.0); + + CRITICAL_BLOCK(cs_mapAddresses) + mapAddresses[addrConnect.GetKey()].nLastTry = GetAdjustedTime(); + + // Connect + SOCKET hSocket; + if (ConnectSocket(addrConnect, hSocket)) + { + /// debug print + printf("connected %s\n", addrConnect.ToStringLog().c_str()); + + // Set to nonblocking +#ifdef __WXMSW__ + u_long nOne = 1; + if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) + printf("ConnectSocket() : ioctlsocket nonblocking setting failed, error %d\n", WSAGetLastError()); +#else + if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) + printf("ConnectSocket() : fcntl nonblocking setting failed, error %d\n", errno); +#endif + + // Add node + CNode* pnode = new CNode(hSocket, addrConnect, false); + if (nTimeout != 0) + pnode->AddRef(nTimeout); + else + pnode->AddRef(); + CRITICAL_BLOCK(cs_vNodes) + vNodes.push_back(pnode); + + pnode->nTimeConnected = GetTime(); + return pnode; + } + else + { + return NULL; + } +} + +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", addr.ToStringLog().c_str()); + closesocket(hSocket); + hSocket = INVALID_SOCKET; + } +} + +void CNode::Cleanup() +{ + // All of a nodes broadcasts and subscriptions are automatically torn down + // when it goes down, so a node has to stay up to keep its broadcast going. + + // Cancel subscriptions + for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) + if (vfSubscribe[nChannel]) + CancelSubscribe(nChannel); +} + + + + + + + + + + + + + +void ThreadSocketHandler(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadSocketHandler(parg)); + try + { + vnThreadsRunning[0]++; + ThreadSocketHandler2(parg); + vnThreadsRunning[0]--; + } + catch (std::exception& e) { + vnThreadsRunning[0]--; + PrintException(&e, "ThreadSocketHandler()"); + } catch (...) { + vnThreadsRunning[0]--; + throw; // support pthread_cancel() + } + printf("ThreadSocketHandler exiting\n"); +} + +void ThreadSocketHandler2(void* parg) +{ + printf("ThreadSocketHandler started\n"); + list vNodesDisconnected; + int nPrevNodeCount = 0; + + loop + { + // + // Disconnect nodes + // + CRITICAL_BLOCK(cs_vNodes) + { + // Disconnect unused nodes + vector vNodesCopy = vNodes; + foreach(CNode* pnode, vNodesCopy) + { + if (pnode->fDisconnect || + (pnode->GetRefCount() <= 0 && pnode->vRecv.empty() && pnode->vSend.empty())) + { + // remove from vNodes + vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); + + // 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 vNodesDisconnectedCopy = vNodesDisconnected; + foreach(CNode* pnode, vNodesDisconnectedCopy) + { + // wait until threads are done using it + if (pnode->GetRefCount() <= 0) + { + bool fDelete = false; + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) + TRY_CRITICAL_BLOCK(pnode->cs_inventory) + fDelete = true; + if (fDelete) + { + vNodesDisconnected.remove(pnode); + delete pnode; + } + } + } + } + if (vNodes.size() != nPrevNodeCount) + { + nPrevNodeCount = vNodes.size(); + MainFrameRepaint(); + } + + + // + // Find which sockets have data to receive + // + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 50000; // frequency to poll pnode->vSend + + fd_set fdsetRecv; + fd_set fdsetSend; + fd_set fdsetError; + FD_ZERO(&fdsetRecv); + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + SOCKET hSocketMax = 0; + FD_SET(hListenSocket, &fdsetRecv); + hSocketMax = max(hSocketMax, hListenSocket); + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + { + if (pnode->hSocket == INVALID_SOCKET || pnode->hSocket < 0) + continue; + FD_SET(pnode->hSocket, &fdsetRecv); + FD_SET(pnode->hSocket, &fdsetError); + hSocketMax = max(hSocketMax, pnode->hSocket); + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + if (!pnode->vSend.empty()) + FD_SET(pnode->hSocket, &fdsetSend); + } + } + + vnThreadsRunning[0]--; + int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); + vnThreadsRunning[0]++; + if (fShutdown) + return; + if (nSelect == SOCKET_ERROR) + { + int nErr = WSAGetLastError(); + printf("socket select error %d\n", nErr); + for (int i = 0; i <= hSocketMax; i++) + FD_SET(i, &fdsetRecv); + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); + Sleep(timeout.tv_usec/1000); + } + + + // + // Accept new connections + // + if (FD_ISSET(hListenSocket, &fdsetRecv)) + { + struct sockaddr_in sockaddr; + socklen_t len = sizeof(sockaddr); + SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); + CAddress addr(sockaddr); + if (hSocket == INVALID_SOCKET) + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + printf("socket error accept failed: %d\n", WSAGetLastError()); + } + else if (mapArgs.count("-maxconnections") && (int)vNodes.size() >= atoi(mapArgs["-maxconnections"]) - MAX_OUTBOUND_CONNECTIONS) + { + closesocket(hSocket); + } + else + { + printf("accepted connection %s\n", addr.ToStringLog().c_str()); + CNode* pnode = new CNode(hSocket, addr, true); + pnode->AddRef(); + CRITICAL_BLOCK(cs_vNodes) + vNodes.push_back(pnode); + } + } + + + // + // Service each socket + // + vector vNodesCopy; + CRITICAL_BLOCK(cs_vNodes) + { + vNodesCopy = vNodes; + foreach(CNode* pnode, vNodesCopy) + pnode->AddRef(); + } + foreach(CNode* pnode, vNodesCopy) + { + if (fShutdown) + return; + + // + // Receive + // + if (pnode->hSocket == INVALID_SOCKET) + continue; + if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) + { + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + { + CDataStream& vRecv = pnode->vRecv; + unsigned int nPos = vRecv.size(); + + // typical socket buffer is 8K-64K + char pchBuf[0x10000]; + int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + if (nBytes > 0) + { + vRecv.resize(nPos + nBytes); + memcpy(&vRecv[nPos], pchBuf, nBytes); + pnode->nLastRecv = GetTime(); + } + else if (nBytes == 0) + { + // socket closed gracefully + if (!pnode->fDisconnect) + printf("socket closed\n"); + pnode->CloseSocketDisconnect(); + } + else if (nBytes < 0) + { + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + if (!pnode->fDisconnect) + printf("socket recv error %d\n", nErr); + pnode->CloseSocketDisconnect(); + } + } + } + } + + // + // Send + // + if (pnode->hSocket == INVALID_SOCKET) + continue; + if (FD_ISSET(pnode->hSocket, &fdsetSend)) + { + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + { + CDataStream& vSend = pnode->vSend; + if (!vSend.empty()) + { + int nBytes = send(pnode->hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL | MSG_DONTWAIT); + if (nBytes > 0) + { + vSend.erase(vSend.begin(), vSend.begin() + nBytes); + pnode->nLastSend = GetTime(); + } + else if (nBytes < 0) + { + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + printf("socket send error %d\n", nErr); + pnode->CloseSocketDisconnect(); + } + } + } + } + } + + // + // Inactivity checking + // + if (pnode->vSend.empty()) + pnode->nLastSendEmpty = GetTime(); + if (GetTime() - pnode->nTimeConnected > 60) + { + if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) + { + printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60) + { + printf("socket not sending\n"); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastRecv > 90*60) + { + printf("socket inactivity timeout\n"); + pnode->fDisconnect = true; + } + } + } + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + nThreadSocketHandlerHeartbeat = GetTime(); + Sleep(10); + } +} + + + + + + + + + + + + + +unsigned int pnSeed[] = +{ + // 2010/06 + 0x35218252, 0x9c9c9618, 0xda6bacad, 0xb9aca862, 0x97c235c6, + 0x146f9562, 0xb67b9e4b, 0x87cf4bc0, 0xb83945d0, 0x984333ad, + 0xbbeec555, 0x6f0eb440, 0xe0005318, 0x7797e460, 0xddc60fcc, + 0xb3bbd24a, 0x1ac85746, 0x641846a6, 0x85ee1155, 0xbb2e7a4c, + 0x9cb8514b, 0xfc342648, 0x62958fae, 0xd0a8c87a, 0xa800795b, + 0xda8c814e, 0x256a0c80, 0x3f23ec63, 0xd565df43, 0x997d9044, + 0xaa121448, 0xbed8688e, 0x59d09a5e, 0xb2931243, 0x3730ba18, + 0xdd3462d0, 0x4e4d1448, 0x171df645, 0x84ee1155, + 0x248ac445, 0x0e634444, 0x0ded1b63, 0x30c01e60, + 0xa2b9a094, 0x29e4fd43, 0x9ce61b4c, 0xdae09744, + + // 2010/08 + 0x5ae6bf43, 0x460be257, 0x7245c0cf, 0x4e0f028d, 0x26501760, 0x38643255, 0x67094f4f, 0x480449b8, + 0x16545143, 0x1f082e5a, 0xaa428018, 0xe411e793, 0x14c1f862, 0x2726105b, 0x9b33ea50, 0xeeef86ca, + 0xe3210d44, 0x0dca8b63, 0x3f9dfb18, 0x860340ad, 0xf33ba17a, 0x9018375c, 0x1de4e353, 0x0fa52dcb, + 0x89c4555b, 0x109cf37b, 0x28c55b40, 0x04c801ae, 0x275c1e80, 0x6f7f745d, 0x7a2a5653, 0xa28e26d8, + 0xa4e65db2, 0x99a06580, 0xf253ba44, 0x82cf6ab8, 0x859c2e8e, 0xf71a815d, 0xc18f1454, 0x71c8a943, + 0x90d24e18, 0x311789b2, 0x74aba645, 0xde0bbfc3, 0xad724fad, 0xbf1ae15e, 0xbaa6fb54, 0x06e4d145, + 0x51528645, 0x72120cd4, 0xd4cfd145, 0x0a7afed8, 0x9b9a5fad, 0x9e9ff45e, 0x10128355, 0xd44e8646, + 0x04a07b47, 0x5fc9d547, 0xe0491e45, 0xbac21b41, 0x7aa31bae, 0x10483c5f, 0x94a23055, 0x73d9dc47, + 0x1a99c247, 0x822fe847, 0x7e57ba48, 0xb19ea843, 0xa60621b2, 0x778cf163, 0x125c6556, 0xf94ba44f, + 0xa61a0948, 0x6c839e4b, 0x29af5348, 0x68d84845, 0x752b95c3, 0xcf0d4663, 0x08e11e56, 0x75109550, + 0x5f24b94c, 0x42426d4d, 0xfbbc0a4c, 0x70a9a246, 0xda7837cb, 0xae2a986d, 0xe283c358, 0x0c7ca954, + 0x8e9bde59, 0x61521760, 0x6884444c, 0xa194e548, 0x9b8809cc, 0x16e96a8f, 0x956ff859, 0xfad5e555, + 0x0ea70c80, 0x5b4ce26d, 0x7984444c, 0x1080d24a, 0x22a686cf, 0x6bf8c2ad, 0xb0f7485f, 0x06b66e56, + 0x668373bc, 0x75506279, 0x3868694e, 0x12a5954b, 0x3a8b62d1, 0xb74fcbad, 0xa7dc3360, 0xc070b359, + 0xa2b87242, 0xc45cab7c, 0x69882050, 0x14a5464b, 0x386acad5, 0x80b85db2, 0x1f78a062, 0xc608c55b, + 0x4257d543, 0x7636ad80, 0x4432d655, 0xb2114d4b, 0x32639bd9, 0xadd75db2, 0x9be5a362, 0x6831bc5e, + 0xf7f77046, 0x8f35ba81, 0x09bb4e59, 0xd0fb6b4e, 0xc5daa445, 0x9c611618, 0x355dcc62, 0xf2cf435e, + 0x31e72c46, 0xdd8a43ad, 0x171f9c5b, 0xb4c2e355, 0xbe8af945, 0x613d3942, 0xe6f9e863, 0x7a3d855f, + 0xa66adc47, 0x261089b2, 0x5a27105b, 0x6c28105b, 0xdd247946, 0xe6c3a445, 0x43a1ec63, 0x99b4dd5f, + 0xb6834347, 0x5e9649bc, 0xf9dd545d, 0x6ae4c15b, 0xa5318a47, 0x7984ec47, 0x93a73b63, 0x0c60195f, + 0xa5c85e4b, 0xa0a36dc2, 0x0739a95e, 0x3d44c15b, 0xfb940f4b, 0xd67c9148, 0x614f9876, 0x0a241c5f, + 0xad9da74c, 0x4459abc8, 0x12e71b5f, 0x1c534a5d, 0x8ff5fc50, 0x2ca8864b, 0xd894fd80, 0x82ab3160, + 0x390d804e, 0x2cf310cc, 0x680dad80, 0x691be15e, 0x5a8f4652, 0xaad0784d, 0x0d2431ad, +}; + + + +void ThreadOpenConnections(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadOpenConnections(parg)); + try + { + vnThreadsRunning[1]++; + ThreadOpenConnections2(parg); + vnThreadsRunning[1]--; + } + catch (std::exception& e) { + vnThreadsRunning[1]--; + PrintException(&e, "ThreadOpenConnections()"); + } catch (...) { + vnThreadsRunning[1]--; + PrintException(NULL, "ThreadOpenConnections()"); + } + printf("ThreadOpenConnections exiting\n"); +} + +void ThreadOpenConnections2(void* parg) +{ + printf("ThreadOpenConnections started\n"); + + // Connect to specific addresses + if (mapArgs.count("-connect")) + { + for (int64 nLoop = 0;; nLoop++) + { + foreach(string strAddr, mapMultiArgs["-connect"]) + { + CAddress addr(strAddr, NODE_NETWORK); + if (addr.IsValid()) + OpenNetworkConnection(addr); + for (int i = 0; i < 10 && i < nLoop; i++) + { + Sleep(500); + if (fShutdown) + return; + } + } + } + } + + // Connect to manually added nodes first + if (mapArgs.count("-addnode")) + { + foreach(string strAddr, mapMultiArgs["-addnode"]) + { + CAddress addr(strAddr, NODE_NETWORK); + if (addr.IsValid()) + { + OpenNetworkConnection(addr); + Sleep(500); + if (fShutdown) + return; + } + } + } + + // Initiate network connections + int64 nStart = GetTime(); + loop + { + // Limit outbound connections + vnThreadsRunning[1]--; + Sleep(500); + loop + { + int nOutbound = 0; + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (!pnode->fInbound) + nOutbound++; + int nMaxOutboundConnections = MAX_OUTBOUND_CONNECTIONS; + if (mapArgs.count("-maxconnections")) + nMaxOutboundConnections = min(nMaxOutboundConnections, atoi(mapArgs["-maxconnections"])); + if (nOutbound < nMaxOutboundConnections) + break; + Sleep(2000); + if (fShutdown) + return; + } + vnThreadsRunning[1]++; + if (fShutdown) + return; + + CRITICAL_BLOCK(cs_mapAddresses) + { + // Add seed nodes if IRC isn't working + static bool fSeedUsed; + bool fTOR = (fUseProxy && addrProxy.port == htons(9050)); + if (mapAddresses.empty() && (GetTime() - nStart > 60 || fTOR)) + { + for (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. + CAddress addr; + addr.ip = pnSeed[i]; + addr.nTime = 0; + AddAddress(addr); + } + fSeedUsed = true; + } + + if (fSeedUsed && mapAddresses.size() > ARRAYLEN(pnSeed) + 100) + { + // Disconnect seed nodes + set setSeed(pnSeed, pnSeed + ARRAYLEN(pnSeed)); + static int64 nSeedDisconnected; + if (nSeedDisconnected == 0) + { + nSeedDisconnected = GetTime(); + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (setSeed.count(pnode->addr.ip)) + pnode->fDisconnect = true; + } + + // Keep setting timestamps to 0 so they won't reconnect + if (GetTime() - nSeedDisconnected < 60 * 60) + { + foreach(PAIRTYPE(const vector, CAddress)& item, mapAddresses) + { + if (setSeed.count(item.second.ip)) + { + item.second.nTime = 0; + CAddrDB().WriteAddress(item.second); + } + } + } + } + } + + + // + // Choose an address to connect to based on most recently seen + // + CAddress addrConnect; + int64 nBest = INT64_MIN; + + // Only connect to one address per a.b.?.? range. + // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. + set setConnected; + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + setConnected.insert(pnode->addr.ip & 0x0000ffff); + + CRITICAL_BLOCK(cs_mapAddresses) + { + foreach(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + { + const CAddress& addr = item.second; + if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.ip & 0x0000ffff)) + continue; + int64 nSinceLastSeen = GetAdjustedTime() - addr.nTime; + int64 nSinceLastTry = GetAdjustedTime() - addr.nLastTry; + + // Randomize the order in a deterministic way, putting the standard port first + int64 nRandomizer = (uint64)(nStart * 4951 + addr.nLastTry * 9567851 + addr.ip * 7789) % (2 * 60 * 60); + if (addr.port != DEFAULT_PORT) + nRandomizer += 2 * 60 * 60; + + // Last seen Base retry frequency + // <1 hour 10 min + // 1 hour 1 hour + // 4 hours 2 hours + // 24 hours 5 hours + // 48 hours 7 hours + // 7 days 13 hours + // 30 days 27 hours + // 90 days 46 hours + // 365 days 93 hours + int64 nDelay = (int64)(3600.0 * sqrt(fabs((double)nSinceLastSeen) / 3600.0) + nRandomizer); + + // Fast reconnect for one hour after last seen + if (nSinceLastSeen < 60 * 60) + nDelay = 10 * 60; + + // Limit retry frequency + if (nSinceLastTry < nDelay) + continue; + + // If we have IRC, we'll be notified when they first come online, + // and again every 24 hours by the refresh broadcast. + if (nGotIRCAddresses > 0 && vNodes.size() >= 2 && nSinceLastSeen > 24 * 60 * 60) + continue; + + // Only try the old stuff if we don't have enough connections + if (vNodes.size() >= 8 && nSinceLastSeen > 24 * 60 * 60) + continue; + + // If multiple addresses are ready, prioritize by time since + // last seen and time since last tried. + int64 nScore = min(nSinceLastTry, (int64)24 * 60 * 60) - nSinceLastSeen - nRandomizer; + if (nScore > nBest) + { + nBest = nScore; + addrConnect = addr; + } + } + } + + if (addrConnect.IsValid()) + OpenNetworkConnection(addrConnect); + } +} + +bool OpenNetworkConnection(const CAddress& addrConnect) +{ + // + // Initiate outbound network connection + // + if (fShutdown) + return false; + if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() || FindNode(addrConnect.ip)) + return false; + + vnThreadsRunning[1]--; + CNode* pnode = ConnectNode(addrConnect); + vnThreadsRunning[1]++; + if (fShutdown) + return false; + if (!pnode) + return false; + pnode->fNetworkNode = true; + + if (addrLocalHost.IsRoutable() && !fUseProxy) + { + // Advertise our address + vector vAddr; + vAddr.push_back(addrLocalHost); + pnode->PushMessage("addr", vAddr); + } + + // Get as many addresses as we can + pnode->PushMessage("getaddr"); + pnode->fGetAddr = true; // don't relay the results of the getaddr + + ////// should the one on the receiving end do this too? + // Subscribe our local subscription list + const unsigned int nHops = 0; + for (unsigned int nChannel = 0; nChannel < pnodeLocalHost->vfSubscribe.size(); nChannel++) + if (pnodeLocalHost->vfSubscribe[nChannel]) + pnode->PushMessage("subscribe", nChannel, nHops); + + return true; +} + + + + + + + + +void ThreadMessageHandler(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadMessageHandler(parg)); + try + { + vnThreadsRunning[2]++; + ThreadMessageHandler2(parg); + vnThreadsRunning[2]--; + } + catch (std::exception& e) { + vnThreadsRunning[2]--; + PrintException(&e, "ThreadMessageHandler()"); + } catch (...) { + vnThreadsRunning[2]--; + PrintException(NULL, "ThreadMessageHandler()"); + } + printf("ThreadMessageHandler exiting\n"); +} + +void ThreadMessageHandler2(void* parg) +{ + printf("ThreadMessageHandler started\n"); + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); + while (!fShutdown) + { + vector vNodesCopy; + CRITICAL_BLOCK(cs_vNodes) + { + vNodesCopy = vNodes; + foreach(CNode* pnode, vNodesCopy) + pnode->AddRef(); + } + + // Poll the connected nodes for messages + CNode* pnodeTrickle = NULL; + if (!vNodesCopy.empty()) + pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; + foreach(CNode* pnode, vNodesCopy) + { + // Receive messages + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + ProcessMessages(pnode); + if (fShutdown) + return; + + // Send messages + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + SendMessages(pnode, pnode == pnodeTrickle); + if (fShutdown) + return; + } + + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodesCopy) + pnode->Release(); + } + + // Wait and allow messages to bunch up + vnThreadsRunning[2]--; + Sleep(100); + vnThreadsRunning[2]++; + if (fShutdown) + return; + } +} + + + + + + + + + +bool BindListenPort(string& strError) +{ + strError = ""; + int nOne = 1; + +#ifdef __WXMSW__ + // 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 + hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (hListenSocket == INVALID_SOCKET) + { + strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + +#ifdef BSD + // Different way of disabling SIGPIPE on BSD + setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); +#endif + +#ifndef __WXMSW__ + // Allow binding if the port is still in TIME_WAIT state after + // the program was closed and restarted. Not an issue on windows. + setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); +#endif + +#ifdef __WXMSW__ + // Set to nonblocking, incoming connections will also inherit this + if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR) +#else + if (fcntl(hListenSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) +#endif + { + strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + + // The sockaddr_in structure specifies the address family, + // IP address, and port for the socket that is being bound + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; // bind to all IPs on this computer + sockaddr.sin_port = DEFAULT_PORT; + if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEADDRINUSE) + strError = strprintf("Unable to bind to port %d on this computer. Bitcoin is probably already running.", ntohs(sockaddr.sin_port)); + else + strError = strprintf("Error: Unable to bind to port %d on this computer (bind returned error %d)", ntohs(sockaddr.sin_port), nErr); + printf("%s\n", strError.c_str()); + return false; + } + printf("Bound to port %d\n", ntohs(sockaddr.sin_port)); + + // Listen for incoming connections + if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) + { + strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + + return true; +} + +void StartNode(void* parg) +{ + if (pnodeLocalHost == NULL) + pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); + +#ifdef __WXMSW__ + // Get local host ip + char pszHostName[1000] = ""; + if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) + { + struct hostent* phostent = gethostbyname(pszHostName); + if (phostent) + { + // Take the first IP that isn't loopback 127.x.x.x + for (int i = 0; phostent->h_addr_list[i] != NULL; i++) + printf("host ip %d: %s\n", i, CAddress(*(unsigned int*)phostent->h_addr_list[i]).ToStringIP().c_str()); + for (int i = 0; phostent->h_addr_list[i] != NULL; i++) + { + CAddress addr(*(unsigned int*)phostent->h_addr_list[i], DEFAULT_PORT, nLocalServices); + if (addr.IsValid() && addr.GetByte(3) != 127) + { + addrLocalHost = addr; + break; + } + } + } + } +#else + // Get local host ip + struct ifaddrs* myaddrs; + if (getifaddrs(&myaddrs) == 0) + { + for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == NULL) continue; + if ((ifa->ifa_flags & IFF_UP) == 0) continue; + if (strcmp(ifa->ifa_name, "lo") == 0) continue; + if (strcmp(ifa->ifa_name, "lo0") == 0) continue; + char pszIP[100]; + if (ifa->ifa_addr->sa_family == AF_INET) + { + struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); + if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s4->sin_addr), pszIP, sizeof(pszIP)) != NULL) + printf("ipv4 %s: %s\n", ifa->ifa_name, pszIP); + + // Take the first IP that isn't loopback 127.x.x.x + CAddress addr(*(unsigned int*)&s4->sin_addr, DEFAULT_PORT, nLocalServices); + if (addr.IsValid() && addr.GetByte(3) != 127) + { + addrLocalHost = addr; + break; + } + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); + if (inet_ntop(ifa->ifa_addr->sa_family, (void*)&(s6->sin6_addr), pszIP, sizeof(pszIP)) != NULL) + printf("ipv6 %s: %s\n", ifa->ifa_name, pszIP); + } + } + freeifaddrs(myaddrs); + } +#endif + printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); + + // Get our external IP address for incoming connections + if (fUseProxy) + { + // Proxies can't take incoming connections + addrLocalHost.ip = CAddress("0.0.0.0").ip; + printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); + } + else + { + if (addrIncoming.IsValid()) + addrLocalHost.ip = addrIncoming.ip; + + if (GetMyExternalIP(addrLocalHost.ip)) + { + addrIncoming = addrLocalHost; + CWalletDB().WriteSetting("addrIncoming", addrIncoming); + printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); + } + } + + // + // Start threads + // + + // Get addresses from IRC and advertise ours + if (!CreateThread(ThreadIRCSeed, NULL)) + printf("Error: CreateThread(ThreadIRCSeed) failed\n"); + + // Send and receive from sockets, accept connections + pthread_t hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); + + // Initiate outbound connections + if (!CreateThread(ThreadOpenConnections, NULL)) + printf("Error: CreateThread(ThreadOpenConnections) failed\n"); + + // Process messages + if (!CreateThread(ThreadMessageHandler, NULL)) + printf("Error: CreateThread(ThreadMessageHandler) failed\n"); + + // Generate coins in the background + GenerateBitcoins(fGenerateBitcoins); + + // + // Thread monitoring + // Not really needed anymore, the cause of the hanging was fixed + // + loop + { + Sleep(1000); + if (fShutdown) + return; + if (GetTime() - nThreadSocketHandlerHeartbeat > 15 * 60) + { + // First see if closing sockets will free it + printf("*** ThreadSocketHandler is stopped ***\n"); + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + { + bool fGot = false; + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + fGot = true; + if (!fGot) + { + printf("*** closing socket\n"); + pnode->CloseSocketDisconnect(); + } + } + } + Sleep(10000); + if (fShutdown) + return; + if (GetTime() - nThreadSocketHandlerHeartbeat < 60) + continue; + + // Hopefully it never comes to this. + // We know it'll always be hung in the recv or send call. + // cs_vRecv or cs_vSend may be left permanently unreleased, + // but we always only use TRY_CRITICAL_SECTION on them. + printf("*** Restarting ThreadSocketHandler ***\n"); + TerminateThread(hThreadSocketHandler, 0); + #ifdef __WXMSW__ + CloseHandle(hThreadSocketHandler); + #endif + vnThreadsRunning[0] = 0; + + // Restart + hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); + nThreadSocketHandlerHeartbeat = GetTime(); + } + } +} + +bool StopNode() +{ + printf("StopNode()\n"); + fShutdown = true; + nTransactionsUpdated++; + int64 nStart = GetTime(); + while (vnThreadsRunning[0] > 0 || vnThreadsRunning[2] > 0 || vnThreadsRunning[3] > 0 || vnThreadsRunning[4] > 0) + { + if (GetTime() - nStart > 20) + break; + Sleep(20); + } + if (vnThreadsRunning[0] > 0) printf("ThreadSocketHandler still running\n"); + if (vnThreadsRunning[1] > 0) printf("ThreadOpenConnections still running\n"); + if (vnThreadsRunning[2] > 0) printf("ThreadMessageHandler still running\n"); + if (vnThreadsRunning[3] > 0) printf("ThreadBitcoinMiner still running\n"); + if (vnThreadsRunning[4] > 0) printf("ThreadRPCServer still running\n"); + while (vnThreadsRunning[2] > 0 || vnThreadsRunning[4] > 0) + Sleep(20); + Sleep(50); + + return true; +} + +class CNetCleanup +{ +public: + CNetCleanup() + { + } + ~CNetCleanup() + { + // Close sockets + foreach(CNode* pnode, vNodes) + if (pnode->hSocket != INVALID_SOCKET) + closesocket(pnode->hSocket); + if (hListenSocket != INVALID_SOCKET) + if (closesocket(hListenSocket) == SOCKET_ERROR) + printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); + +#ifdef __WXMSW__ + // Shutdown Windows Sockets + WSACleanup(); +#endif + } +} +instance_of_cnetcleanup; -- cgit v1.2.3