mirror of
https://github.com/dashpay/dash.git
synced 2024-12-27 21:12:48 +01:00
8bb5edc1c9
Thanks joepie91 for the translation of the new copyright notices into Dutch. Thanks sipa for the translation of the new copyright notices into French. Thanks megu for the translation of the new copyright notices into Spanish. Thanks justmoon/Blitzboom for the translation of the new copyright notices into German. Thanks Joozero for the translation of the new copyright notices into Italian. Remaining translations were provided by Google Translate.
1600 lines
51 KiB
C++
1600 lines
51 KiB
C++
// 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"
|
|
|
|
#ifdef USE_UPNP
|
|
#include <miniupnpc/miniwget.h>
|
|
#include <miniupnpc/miniupnpc.h>
|
|
#include <miniupnpc/upnpcommands.h>
|
|
#include <miniupnpc/upnperrors.h>
|
|
#endif
|
|
|
|
static const int MAX_OUTBOUND_CONNECTIONS = 8;
|
|
|
|
void ThreadMessageHandler2(void* parg);
|
|
void ThreadSocketHandler2(void* parg);
|
|
void ThreadOpenConnections2(void* parg);
|
|
#ifdef USE_UPNP
|
|
void ThreadMapPort2(void* parg);
|
|
#endif
|
|
bool OpenNetworkConnection(const CAddress& addrConnect);
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Global state variables
|
|
//
|
|
bool fClient = false;
|
|
uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK);
|
|
CAddress addrLocalHost(0, 0, nLocalServices);
|
|
CNode* pnodeLocalHost = NULL;
|
|
uint64 nLocalHostNonce = 0;
|
|
array<int, 10> vnThreadsRunning;
|
|
SOCKET hListenSocket = INVALID_SOCKET;
|
|
|
|
vector<CNode*> vNodes;
|
|
CCriticalSection cs_vNodes;
|
|
map<vector<unsigned char>, CAddress> mapAddresses;
|
|
CCriticalSection cs_mapAddresses;
|
|
map<CInv, CDataStream> mapRelay;
|
|
deque<pair<int64, CInv> > vRelayExpiration;
|
|
CCriticalSection cs_mapRelay;
|
|
map<CInv, int64> 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()) // 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) != -1)
|
|
{
|
|
strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword));
|
|
break;
|
|
}
|
|
}
|
|
closesocket(hSocket);
|
|
if (strLine.find("<") != -1)
|
|
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");
|
|
}
|
|
|
|
// We now get our external IP from the IRC server first and only use this as a backup
|
|
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++)
|
|
{
|
|
// 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 = CAddress("91.198.22.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:";
|
|
}
|
|
else if (nHost == 2)
|
|
{
|
|
addrConnect = CAddress("74.208.43.192:80"); // www.showmyip.com
|
|
|
|
if (nLookup == 1)
|
|
{
|
|
struct hostent* phostent = gethostbyname("www.showmyip.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 /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)
|
|
{
|
|
// Wait for IRC to get it first
|
|
if (!GetBoolArg("-noirc"))
|
|
{
|
|
for (int i = 0; i < 2 * 60; i++)
|
|
{
|
|
Sleep(1000);
|
|
if (fGotExternalIP || fShutdown)
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Fallback in case IRC fails to get it
|
|
if (GetMyExternalIP(addrLocalHost.ip))
|
|
{
|
|
printf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str());
|
|
if (addrLocalHost.IsRoutable())
|
|
{
|
|
// If we already connected to a few before we had our IP, go back and addr them.
|
|
// setAddrKnown automatically filters any duplicate sends.
|
|
CAddress addr(addrLocalHost);
|
|
addr.nTime = GetAdjustedTime();
|
|
CRITICAL_BLOCK(cs_vNodes)
|
|
foreach(CNode* pnode, vNodes)
|
|
pnode->PushAddress(addr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool AddAddress(CAddress addr, int64 nTimePenalty)
|
|
{
|
|
if (!addr.IsRoutable())
|
|
return false;
|
|
if (addr.ip == addrLocalHost.ip)
|
|
return false;
|
|
addr.nTime = max((int64)0, (int64)addr.nTime - nTimePenalty);
|
|
CRITICAL_BLOCK(cs_mapAddresses)
|
|
{
|
|
map<vector<unsigned char>, 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<vector<unsigned char>, 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<uint256, CRequestTracker>::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<CNode*> vNodesDisconnected;
|
|
int nPrevNodeCount = 0;
|
|
|
|
loop
|
|
{
|
|
//
|
|
// Disconnect nodes
|
|
//
|
|
CRITICAL_BLOCK(cs_vNodes)
|
|
{
|
|
// Disconnect unused nodes
|
|
vector<CNode*> 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<CNode*> 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;
|
|
|
|
if(hListenSocket != INVALID_SOCKET)
|
|
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 (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv))
|
|
{
|
|
struct sockaddr_in sockaddr;
|
|
socklen_t len = sizeof(sockaddr);
|
|
SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len);
|
|
CAddress addr(sockaddr);
|
|
int nInbound = 0;
|
|
|
|
CRITICAL_BLOCK(cs_vNodes)
|
|
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)
|
|
{
|
|
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<CNode*> 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();
|
|
|
|
if (nPos > 1000*GetArg("-maxreceivebuffer", 10*1000)) {
|
|
if (!pnode->fDisconnect)
|
|
printf("socket recv flood control disconnect (%d bytes)\n", vRecv.size());
|
|
pnode->CloseSocketDisconnect();
|
|
}
|
|
else {
|
|
// typical socket buffer is 8K-64K
|
|
char pchBuf[0x10000];
|
|
int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
|
|
if (nBytes > 0)
|
|
{
|
|
vRecv.resize(nPos + nBytes);
|
|
memcpy(&vRecv[nPos], pchBuf, nBytes);
|
|
pnode->nLastRecv = GetTime();
|
|
}
|
|
else if (nBytes == 0)
|
|
{
|
|
// socket closed gracefully
|
|
if (!pnode->fDisconnect)
|
|
printf("socket closed\n");
|
|
pnode->CloseSocketDisconnect();
|
|
}
|
|
else if (nBytes < 0)
|
|
{
|
|
// error
|
|
int nErr = WSAGetLastError();
|
|
if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
|
|
{
|
|
if (!pnode->fDisconnect)
|
|
printf("socket recv error %d\n", nErr);
|
|
pnode->CloseSocketDisconnect();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send
|
|
//
|
|
if (pnode->hSocket == INVALID_SOCKET)
|
|
continue;
|
|
if (FD_ISSET(pnode->hSocket, &fdsetSend))
|
|
{
|
|
TRY_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();
|
|
}
|
|
}
|
|
if (vSend.size() > 1000*GetArg("-maxsendbuffer", 10*1000)) {
|
|
if (!pnode->fDisconnect)
|
|
printf("socket send flood control disconnect (%d bytes)\n", vSend.size());
|
|
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();
|
|
}
|
|
|
|
Sleep(10);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef USE_UPNP
|
|
void ThreadMapPort(void* parg)
|
|
{
|
|
IMPLEMENT_RANDOMIZE_STACK(ThreadMapPort(parg));
|
|
try
|
|
{
|
|
vnThreadsRunning[5]++;
|
|
ThreadMapPort2(parg);
|
|
vnThreadsRunning[5]--;
|
|
}
|
|
catch (std::exception& e) {
|
|
vnThreadsRunning[5]--;
|
|
PrintException(&e, "ThreadMapPort()");
|
|
} catch (...) {
|
|
vnThreadsRunning[5]--;
|
|
PrintException(NULL, "ThreadMapPort()");
|
|
}
|
|
printf("ThreadMapPort exiting\n");
|
|
}
|
|
|
|
void ThreadMapPort2(void* parg)
|
|
{
|
|
printf("ThreadMapPort started\n");
|
|
|
|
char port[6];
|
|
sprintf(port, "%d", ntohs(GetDefaultPort()));
|
|
|
|
const char * rootdescurl = 0;
|
|
const char * multicastif = 0;
|
|
const char * minissdpdpath = 0;
|
|
struct UPNPDev * devlist = 0;
|
|
char lanaddr[64];
|
|
|
|
devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0);
|
|
|
|
struct UPNPUrls urls;
|
|
struct IGDdatas data;
|
|
int r;
|
|
|
|
if (UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)) == 1)
|
|
{
|
|
char intClient[16];
|
|
char intPort[6];
|
|
|
|
#ifndef __WXMSW__
|
|
r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
|
|
port, port, lanaddr, 0, "TCP", 0);
|
|
#else
|
|
r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
|
|
port, port, lanaddr, 0, "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");
|
|
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;
|
|
}
|
|
Sleep(2000);
|
|
}
|
|
} else {
|
|
printf("No valid UPnP IGDs found\n");
|
|
freeUPNPDevlist(devlist); devlist = 0;
|
|
FreeUPNPUrls(&urls);
|
|
loop {
|
|
if (fShutdown)
|
|
return;
|
|
Sleep(2000);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MapPort(bool fMapPort)
|
|
{
|
|
if (fUseUPnP != fMapPort)
|
|
{
|
|
fUseUPnP = fMapPort;
|
|
CWalletDB().WriteSetting("fUseUPnP", fUseUPnP);
|
|
}
|
|
if (fUseUPnP && vnThreadsRunning[5] < 1)
|
|
{
|
|
if (!CreateThread(ThreadMapPort, NULL))
|
|
printf("Error: ThreadMapPort(ThreadMapPort) failed\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const char *strDNSSeed[] = {
|
|
"bitseed.xf2.org",
|
|
};
|
|
|
|
void DNSAddressSeed()
|
|
{
|
|
int found = 0;
|
|
|
|
printf("Loading addresses from DNS seeds (could take a while)\n");
|
|
|
|
for (int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) {
|
|
struct hostent* phostent = gethostbyname(strDNSSeed[seed_idx]);
|
|
if (!phostent)
|
|
continue;
|
|
|
|
for (int host = 0; phostent->h_addr_list[host] != NULL; host++) {
|
|
CAddress addr(*(unsigned int*)phostent->h_addr_list[host],
|
|
GetDefaultPort(), NODE_NETWORK);
|
|
addr.nTime = 0;
|
|
if (addr.IsValid() && addr.GetByte(3) != 127) {
|
|
AddAddress(addr);
|
|
found++;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("%d addresses found from DNS seeds\n", found);
|
|
}
|
|
|
|
|
|
|
|
unsigned int pnSeed[] =
|
|
{
|
|
0x1ddb1032, 0x6242ce40, 0x52d6a445, 0x2dd7a445, 0x8a53cd47, 0x73263750, 0xda23c257, 0xecd4ed57,
|
|
0x0a40ec59, 0x75dce160, 0x7df76791, 0x89370bad, 0xa4f214ad, 0x767700ae, 0x638b0418, 0x868a1018,
|
|
0xcd9f332e, 0x0129653e, 0xcc92dc3e, 0x96671640, 0x56487e40, 0x5b66f440, 0xb1d01f41, 0xf1dc6041,
|
|
0xc1d12b42, 0x86ba1243, 0x6be4df43, 0x6d4cef43, 0xd18e0644, 0x1ab0b344, 0x6584a345, 0xe7c1a445,
|
|
0x58cea445, 0xc5daa445, 0x21dda445, 0x3d3b5346, 0x13e55347, 0x1080d24a, 0x8e611e4b, 0x81518e4b,
|
|
0x6c839e4b, 0xe2ad0a4c, 0xfbbc0a4c, 0x7f5b6e4c, 0x7244224e, 0x1300554e, 0x20690652, 0x5a48b652,
|
|
0x75c5c752, 0x4335cc54, 0x340fd154, 0x87c07455, 0x087b2b56, 0x8a133a57, 0xac23c257, 0x70374959,
|
|
0xfb63d45b, 0xb9a1685c, 0x180d765c, 0x674f645d, 0x04d3495e, 0x1de44b5e, 0x4ee8a362, 0x0ded1b63,
|
|
0xc1b04b6d, 0x8d921581, 0x97b7ea82, 0x1cf83a8e, 0x91490bad, 0x09dc75ae, 0x9a6d79ae, 0xa26d79ae,
|
|
0x0fd08fae, 0x0f3e3fb2, 0x4f944fb2, 0xcca448b8, 0x3ecd6ab8, 0xa9d5a5bc, 0x8d0119c1, 0x045997d5,
|
|
0xca019dd9, 0x0d526c4d, 0xabf1ba44, 0x66b1ab55, 0x1165f462, 0x3ed7cbad, 0xa38fae6e, 0x3bd2cbad,
|
|
0xd36f0547, 0x20df7840, 0x7a337742, 0x549f8e4b, 0x9062365c, 0xd399f562, 0x2b5274a1, 0x8edfa153,
|
|
0x3bffb347, 0x7074bf58, 0xb74fcbad, 0x5b5a795b, 0x02fa29ce, 0x5a6738d4, 0xe8a1d23e, 0xef98c445,
|
|
0x4b0f494c, 0xa2bc1e56, 0x7694ad63, 0xa4a800c3, 0x05fda6cd, 0x9f22175e, 0x364a795b, 0x536285d5,
|
|
0xac44c9d4, 0x0b06254d, 0x150c2fd4, 0x32a50dcc, 0xfd79ce48, 0xf15cfa53, 0x66c01e60, 0x6bc26661,
|
|
0xc03b47ae, 0x4dda1b81, 0x3285a4c1, 0x883ca96d, 0x35d60a4c, 0xdae09744, 0x2e314d61, 0x84e247cf,
|
|
0x6c814552, 0x3a1cc658, 0x98d8f382, 0xe584cb5b, 0x15e86057, 0x7b01504e, 0xd852dd48, 0x56382f56,
|
|
0x0a5df454, 0xa0d18d18, 0x2e89b148, 0xa79c114c, 0xcbdcd054, 0x5523bc43, 0xa9832640, 0x8a066144,
|
|
0x3894c3bc, 0xab76bf58, 0x6a018ac1, 0xfebf4f43, 0x2f26c658, 0x31102f4e, 0x85e929d5, 0x2a1c175e,
|
|
0xfc6c2cd1, 0x27b04b6d, 0xdf024650, 0x161748b8, 0x28be6580, 0x57be6580, 0x1cee677a, 0xaa6bb742,
|
|
0x9a53964b, 0x0a5a2d4d, 0x2434c658, 0x9a494f57, 0x1ebb0e48, 0xf610b85d, 0x077ecf44, 0x085128bc,
|
|
0x5ba17a18, 0x27ca1b42, 0xf8a00b56, 0xfcd4c257, 0xcf2fc15e, 0xd897e052, 0x4cada04f, 0x2f35f6d5,
|
|
0x382ce8c9, 0xe523984b, 0x3f946846, 0x60c8be43, 0x41da6257, 0xde0be142, 0xae8a544b, 0xeff0c254,
|
|
0x1e0f795b, 0xaeb28890, 0xca16acd9, 0x1e47ddd8, 0x8c8c4829, 0xd27dc747, 0xd53b1663, 0x4096b163,
|
|
0x9c8dd958, 0xcb12f860, 0x9e79305c, 0x40c1a445, 0x4a90c2bc, 0x2c3a464d, 0x2727f23c, 0x30b04b6d,
|
|
0x59024cb8, 0xa091e6ad, 0x31b04b6d, 0xc29d46a6, 0x63934fb2, 0xd9224dbe, 0x9f5910d8, 0x7f530a6b,
|
|
0x752e9c95, 0x65453548, 0xa484be46, 0xce5a1b59, 0x710e0718, 0x46a13d18, 0xdaaf5318, 0xc4a8ff53,
|
|
0x87abaa52, 0xb764cf51, 0xb2025d4a, 0x6d351e41, 0xc035c33e, 0xa432c162, 0x61ef34ae, 0xd16fddbc,
|
|
0x0870e8c1, 0x3070e8c1, 0x9c71e8c1, 0xa4992363, 0x85a1f663, 0x4184e559, 0x18d96ed8, 0x17b8dbd5,
|
|
0x60e7cd18, 0xe5ee104c, 0xab17ac62, 0x1e786e1b, 0x5d23b762, 0xf2388fae, 0x88270360, 0x9e5b3d80,
|
|
0x7da518b2, 0xb5613b45, 0x1ad41f3e, 0xd550854a, 0x8617e9a9, 0x925b229c, 0xf2e92542, 0x47af0544,
|
|
0x73b5a843, 0xb9b7a0ad, 0x03a748d0, 0x0a6ff862, 0x6694df62, 0x3bfac948, 0x8e098f4f, 0x746916c3,
|
|
0x02f38e4f, 0x40bb1243, 0x6a54d162, 0x6008414b, 0xa513794c, 0x514aa343, 0x63781747, 0xdbb6795b,
|
|
0xed065058, 0x42d24b46, 0x1518794c, 0x9b271681, 0x73e4ffad, 0x0654784f, 0x438dc945, 0x641846a6,
|
|
0x2d1b0944, 0x94b59148, 0x8d369558, 0xa5a97662, 0x8b705b42, 0xce9204ae, 0x8d584450, 0x2df61555,
|
|
0xeebff943, 0x2e75fb4d, 0x3ef8fc57, 0x9921135e, 0x8e31042e, 0xb5afad43, 0x89ecedd1, 0x9cfcc047,
|
|
0x8fcd0f4c, 0xbe49f5ad, 0x146a8d45, 0x98669ab8, 0x98d9175e, 0xd1a8e46d, 0x839a3ab8, 0x40a0016c,
|
|
0x6d27c257, 0x977fffad, 0x7baa5d5d, 0x1213be43, 0xb167e5a9, 0x640fe8ca, 0xbc9ea655, 0x0f820a4c,
|
|
0x0f097059, 0x69ac957c, 0x366d8453, 0xb1ba2844, 0x8857f081, 0x70b5be63, 0xc545454b, 0xaf36ded1,
|
|
0xb5a4b052, 0x21f062d1, 0x72ab89b2, 0x74a45318, 0x8312e6bc, 0xb916965f, 0x8aa7c858, 0xfe7effad,
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
nMaxOutboundConnections = min(nMaxOutboundConnections, (int)GetArg("-maxconnections", 125));
|
|
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) && !fTestNet)
|
|
{
|
|
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<unsigned int> 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<unsigned char>, CAddress)& item, mapAddresses)
|
|
{
|
|
if (setSeed.count(item.second.ip) && item.second.nTime != 0)
|
|
{
|
|
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<unsigned int> setConnected;
|
|
CRITICAL_BLOCK(cs_vNodes)
|
|
foreach(CNode* pnode, vNodes)
|
|
setConnected.insert(pnode->addr.ip & 0x0000ffff);
|
|
|
|
CRITICAL_BLOCK(cs_mapAddresses)
|
|
{
|
|
foreach(const PAIRTYPE(vector<unsigned char>, CAddress)& item, mapAddresses)
|
|
{
|
|
const CAddress& addr = item.second;
|
|
if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.ip & 0x0000ffff))
|
|
continue;
|
|
int64 nSinceLastSeen = GetAdjustedTime() - addr.nTime;
|
|
int64 nSinceLastTry = GetAdjustedTime() - addr.nLastTry;
|
|
|
|
// 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 != GetDefaultPort())
|
|
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;
|
|
|
|
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<CNode*> 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.
|
|
// Reduce vnThreadsRunning so StopNode has permission to exit while
|
|
// we're sleeping, but we must always check fShutdown after doing this.
|
|
vnThreadsRunning[2]--;
|
|
Sleep(100);
|
|
if (fRequestShutdown)
|
|
Shutdown(NULL);
|
|
vnThreadsRunning[2]++;
|
|
if (fShutdown)
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool BindListenPort(string& strError)
|
|
{
|
|
strError = "";
|
|
int nOne = 1;
|
|
addrLocalHost.port = GetDefaultPort();
|
|
|
|
#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 = GetDefaultPort();
|
|
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], GetDefaultPort(), 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, GetDefaultPort(), 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());
|
|
|
|
if (fUseProxy || mapArgs.count("-connect") || fNoListen)
|
|
{
|
|
// Proxies can't take incoming connections
|
|
addrLocalHost.ip = CAddress("0.0.0.0").ip;
|
|
printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str());
|
|
}
|
|
else
|
|
{
|
|
CreateThread(ThreadGetMyExternalIP, NULL);
|
|
}
|
|
|
|
//
|
|
// Start threads
|
|
//
|
|
|
|
// 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");
|
|
|
|
// 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);
|
|
}
|
|
|
|
bool StopNode()
|
|
{
|
|
printf("StopNode()\n");
|
|
fShutdown = true;
|
|
nTransactionsUpdated++;
|
|
int64 nStart = GetTime();
|
|
while (vnThreadsRunning[0] > 0 || vnThreadsRunning[2] > 0 || vnThreadsRunning[3] > 0 || vnThreadsRunning[4] > 0
|
|
#ifdef USE_UPNP
|
|
|| vnThreadsRunning[5] > 0
|
|
#endif
|
|
)
|
|
{
|
|
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");
|
|
if (fHaveUPnP && vnThreadsRunning[5] > 0) printf("ThreadMapPort 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;
|