diff options
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 251 |
1 files changed, 195 insertions, 56 deletions
diff --git a/src/net.cpp b/src/net.cpp index 3b3d91d65..ab547e2fd 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -28,6 +28,7 @@ #endif #include <boost/filesystem.hpp> +#include <boost/thread.hpp> // Dump addresses to peers.dat every 15 minutes (900s) #define DUMP_ADDRESSES_INTERVAL 900 @@ -47,8 +48,8 @@ #endif #endif -using namespace std; using namespace boost; +using namespace std; namespace { const int MAX_OUTBOUND_CONNECTIONS = 8; @@ -307,12 +308,18 @@ bool IsLocal(const CService& addr) return mapLocalHost.count(addr) > 0; } +/** check whether a given network is one we can probably connect to */ +bool IsReachable(enum Network net) +{ + LOCK(cs_mapLocalHost); + return vfReachable[net] && !vfLimited[net]; +} + /** check whether a given address is in a network we can probably connect to */ bool IsReachable(const CNetAddr& addr) { - LOCK(cs_mapLocalHost); enum Network net = addr.GetNetwork(); - return vfReachable[net] && !vfLimited[net]; + return IsReachable(net); } bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet) @@ -332,7 +339,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha { if (!RecvLine(hSocket, strLine)) { - closesocket(hSocket); + CloseSocket(hSocket); return false; } if (pszKeyword == NULL) @@ -343,7 +350,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha break; } } - closesocket(hSocket); + CloseSocket(hSocket); if (strLine.find("<") != string::npos) strLine = strLine.substr(0, strLine.find("<")); strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r")); @@ -357,7 +364,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha return true; } } - closesocket(hSocket); + CloseSocket(hSocket); return error("GetMyExternalIP() : connection closed"); } @@ -368,7 +375,7 @@ bool GetMyExternalIP(CNetAddr& ipRet) const char* pszKeyword; for (int nLookup = 0; nLookup <= 1; nLookup++) - for (int nHost = 1; nHost <= 2; nHost++) + for (int nHost = 1; nHost <= 1; 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 @@ -393,25 +400,6 @@ bool GetMyExternalIP(CNetAddr& ipRet) pszKeyword = "Address:"; } - else if (nHost == 2) - { - addrConnect = CService("74.208.43.192", 80); // www.showmyip.com - - if (nLookup == 1) - { - CService addrIP("www.showmyip.com", 80, true); - if (addrIP.IsValid()) - addrConnect = addrIP; - } - - pszGet = "GET /simple/ HTTP/1.1\r\n" - "Host: www.showmyip.com\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n" - "Connection: close\r\n" - "\r\n"; - - pszKeyword = NULL; // Returns just IP address - } if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet)) return true; @@ -456,7 +444,7 @@ CNode* FindNode(const CNetAddr& ip) return NULL; } -CNode* FindNode(std::string addrName) +CNode* FindNode(const std::string& addrName) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -500,16 +488,6 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) { addrman.Attempt(addrConnect); - // Set to non-blocking -#ifdef WIN32 - u_long nOne = 1; - if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) - LogPrintf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %s\n", NetworkErrorString(WSAGetLastError())); -#else - if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) - LogPrintf("ConnectSocket() : fcntl non-blocking setting failed, error %s\n", NetworkErrorString(errno)); -#endif - // Add node CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); pnode->AddRef(); @@ -533,8 +511,7 @@ void CNode::CloseSocketDisconnect() if (hSocket != INVALID_SOCKET) { LogPrint("net", "disconnecting peer=%d\n", id); - closesocket(hSocket); - hSocket = INVALID_SOCKET; + CloseSocket(hSocket); } // in case this fails, we'll empty the recv buffer when the CNode is deleted @@ -708,7 +685,7 @@ int CNetMessage::readHeader(const char *pch, unsigned int nBytes) try { hdrbuf >> hdr; } - catch (std::exception &e) { + catch (const std::exception &) { return -1; } @@ -975,12 +952,12 @@ void ThreadSocketHandler() } else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS) { - closesocket(hSocket); + CloseSocket(hSocket); } else if (CNode::IsBanned(addr) && !whitelisted) { LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); - closesocket(hSocket); + CloseSocket(hSocket); } else { @@ -1099,8 +1076,6 @@ void ThreadSocketHandler() BOOST_FOREACH(CNode* pnode, vNodesCopy) pnode->Release(); } - - MilliSleep(10); } } @@ -1228,6 +1203,18 @@ void MapPort(bool) void ThreadDNSAddressSeed() { + // goal: only query DNS seeds if address need is acute + if ((addrman.size() > 0) && + (!GetBoolArg("-forcednsseed", false))) { + MilliSleep(11 * 1000); + + LOCK(cs_vNodes); + if (vNodes.size() >= 2) { + LogPrintf("P2P peers available. Skipped DNS seeding.\n"); + return; + } + } + const vector<CDNSSeedData> &vSeeds = Params().DNSSeeds(); int found = 0; @@ -1479,7 +1466,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu if (!pszDest) { if (IsLocal(addrConnect) || FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || - FindNode(addrConnect.ToStringIPPort().c_str())) + FindNode(addrConnect.ToStringIPPort())) return false; } else if (FindNode(pszDest)) return false; @@ -1643,14 +1630,9 @@ bool BindListenPort(const CService &addrBind, string& strError, bool fWhiteliste setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); #endif -#ifdef WIN32 // Set to non-blocking, 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 %s)", NetworkErrorString(WSAGetLastError())); + if (!SetSocketNonBlocking(hListenSocket, true)) { + strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); return false; } @@ -1679,6 +1661,7 @@ bool BindListenPort(const CService &addrBind, string& strError, bool fWhiteliste else strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr)); LogPrintf("%s\n", strError); + CloseSocket(hListenSocket); return false; } LogPrintf("Bound to %s\n", addrBind.ToString()); @@ -1688,6 +1671,7 @@ bool BindListenPort(const CService &addrBind, string& strError, bool fWhiteliste { strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); + CloseSocket(hListenSocket); return false; } @@ -1801,7 +1785,6 @@ bool StopNode() if (semOutbound) for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++) semOutbound->post(); - MilliSleep(50); DumpAddresses(); return true; @@ -1817,11 +1800,11 @@ public: // Close sockets BOOST_FOREACH(CNode* pnode, vNodes) if (pnode->hSocket != INVALID_SOCKET) - closesocket(pnode->hSocket); + CloseSocket(pnode->hSocket); BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket) if (hListenSocket.socket != INVALID_SOCKET) - if (closesocket(hListenSocket.socket) == SOCKET_ERROR) - LogPrintf("closesocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); + if (!CloseSocket(hListenSocket.socket)) + LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); // clean up some globals (to help leak detection) BOOST_FOREACH(CNode *pnode, vNodes) @@ -2048,3 +2031,159 @@ bool CAddrDB::Read(CAddrMan& addr) return true; } + +unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } +unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } + +CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), setAddrKnown(5000) +{ + nServices = 0; + hSocket = hSocketIn; + nRecvVersion = INIT_PROTO_VERSION; + nLastSend = 0; + nLastRecv = 0; + nSendBytes = 0; + nRecvBytes = 0; + nTimeConnected = GetTime(); + addr = addrIn; + addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; + nVersion = 0; + strSubVer = ""; + fWhitelisted = false; + fOneShot = false; + fClient = false; // set by version message + fInbound = fInboundIn; + fNetworkNode = false; + fSuccessfullyConnected = false; + fDisconnect = false; + nRefCount = 0; + nSendSize = 0; + nSendOffset = 0; + hashContinue = 0; + pindexLastGetBlocksBegin = 0; + hashLastGetBlocksEnd = 0; + nStartingHeight = -1; + fStartSync = false; + fGetAddr = false; + fRelayTxes = false; + setInventoryKnown.max_size(SendBufferSize() / 1000); + pfilter = new CBloomFilter(); + nPingNonceSent = 0; + nPingUsecStart = 0; + nPingUsecTime = 0; + fPingQueued = false; + + { + LOCK(cs_nLastNodeId); + id = nLastNodeId++; + } + + if (fLogIPs) + LogPrint("net", "Added connection to %s peer=%d\n", addrName, id); + else + LogPrint("net", "Added connection peer=%d\n", id); + + // Be shy and don't send version until we hear + if (hSocket != INVALID_SOCKET && !fInbound) + PushVersion(); + + GetNodeSignals().InitializeNode(GetId(), this); +} + +CNode::~CNode() +{ + CloseSocket(hSocket); + + if (pfilter) + delete pfilter; + + GetNodeSignals().FinalizeNode(GetId()); +} + +void CNode::AskFor(const CInv& inv) +{ + if (mapAskFor.size() > MAPASKFOR_MAX_SZ) + return; + // We're using mapAskFor as a priority queue, + // the key is the earliest time the request can be sent + int64_t nRequestTime; + limitedmap<CInv, int64_t>::const_iterator it = mapAlreadyAskedFor.find(inv); + if (it != mapAlreadyAskedFor.end()) + nRequestTime = it->second; + else + nRequestTime = 0; + LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000), id); + + // Make sure not to reuse time indexes to keep things in the same order + int64_t nNow = GetTimeMicros() - 1000000; + static int64_t nLastTime; + ++nLastTime; + nNow = std::max(nNow, nLastTime); + nLastTime = nNow; + + // Each retry is 2 minutes after the last + nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); + if (it != mapAlreadyAskedFor.end()) + mapAlreadyAskedFor.update(it, nRequestTime); + else + mapAlreadyAskedFor.insert(std::make_pair(inv, nRequestTime)); + mapAskFor.insert(std::make_pair(nRequestTime, inv)); +} + +void CNode::BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend) +{ + ENTER_CRITICAL_SECTION(cs_vSend); + assert(ssSend.size() == 0); + ssSend << CMessageHeader(pszCommand, 0); + LogPrint("net", "sending: %s ", pszCommand); +} + +void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend) +{ + ssSend.clear(); + + LEAVE_CRITICAL_SECTION(cs_vSend); + + LogPrint("net", "(aborted)\n"); +} + +void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) +{ + // The -*messagestest options are intentionally not documented in the help message, + // since they are only used during development to debug the networking code and are + // not intended for end-users. + if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0) + { + LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n"); + AbortMessage(); + return; + } + if (mapArgs.count("-fuzzmessagestest")) + Fuzz(GetArg("-fuzzmessagestest", 10)); + + if (ssSend.size() == 0) + return; + + // Set the size + unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; + memcpy((char*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], &nSize, sizeof(nSize)); + + // Set the checksum + uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end()); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); + memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); + + LogPrint("net", "(%d bytes) peer=%d\n", nSize, id); + + std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); + ssSend.GetAndClear(*it); + nSendSize += (*it).size(); + + // If write queue empty, attempt "optimistic write" + if (it == vSendMsg.begin()) + SocketSendData(this); + + LEAVE_CRITICAL_SECTION(cs_vSend); +} |