diff options
Diffstat (limited to 'src/net.cpp')
-rw-r--r-- | src/net.cpp | 266 |
1 files changed, 177 insertions, 89 deletions
diff --git a/src/net.cpp b/src/net.cpp index 8603514f91..e48b300953 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -35,9 +35,13 @@ void ThreadOpenAddedConnections2(void* parg); void ThreadMapPort2(void* parg); #endif void ThreadDNSAddressSeed2(void* parg); -bool OpenNetworkConnection(const CAddress& addrConnect, const char *strDest = NULL, bool fOneShot = false); +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); +struct LocalServiceInfo { + int nScore; + int nPort; +}; // // Global state variables @@ -45,12 +49,14 @@ bool OpenNetworkConnection(const CAddress& addrConnect, const char *strDest = NU bool fClient = false; static bool fUseUPnP = false; uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); -CCriticalSection cs_mapLocalHost; -map<CNetAddr, int> mapLocalHost; +static CCriticalSection cs_mapLocalHost; +static map<CNetAddr, LocalServiceInfo> mapLocalHost; +static bool vfReachable[NET_MAX] = {}; +static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; uint64 nLocalHostNonce = 0; array<int, THREAD_MAX> vnThreadsRunning; -static SOCKET hListenSocket = INVALID_SOCKET; +static std::vector<SOCKET> vhListenSocket; CAddrMan addrman; vector<CNode*> vNodes; @@ -66,10 +72,7 @@ CCriticalSection cs_vOneShots; set<CNetAddr> setservAddNodeAddresses; CCriticalSection cs_setservAddNodeAddresses; -static CWaitableCriticalSection csOutbound; -static int nOutbound = 0; -static CConditionVariable condOutbound; - +static CSemaphore *semOutbound = NULL; void AddOneShot(string strDest) { @@ -94,39 +97,38 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) } // find 'best' local address for a particular peer -bool GetLocal(CNetAddr& addr, const CNetAddr *paddrPeer) +bool GetLocal(CService& addr, const CNetAddr *paddrPeer) { if (fUseProxy || mapArgs.count("-connect") || fNoListen) return false; - int nBestCount = -1; + int nBestScore = -1; int nBestReachability = -1; { LOCK(cs_mapLocalHost); - for (map<CNetAddr, int>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) + for (map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) { - int nCount = (*it).second; + int nScore = (*it).second.nScore; int nReachability = (*it).first.GetReachabilityFrom(paddrPeer); - if (nReachability > nBestReachability || (nReachability == nBestReachability && nCount > nBestCount)) + if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) { - addr = (*it).first; + addr = CService((*it).first, (*it).second.nPort); nBestReachability = nReachability; - nBestCount = nCount; + nBestScore = nScore; } } } - return nBestCount >= 0; + return nBestScore >= 0; } // get best local address for a particular peer as a CAddress CAddress GetLocalAddress(const CNetAddr *paddrPeer) { CAddress ret(CService("0.0.0.0",0),0); - CNetAddr addr; + CService addr; if (GetLocal(addr, paddrPeer)) { - ret.SetIP(addr); - ret.SetPort(GetListenPort()); + ret = CAddress(addr); ret.nServices = nLocalServices; ret.nTime = GetAdjustedTime(); } @@ -194,7 +196,7 @@ void static AdvertizeLocal() if (pnode->fSuccessfullyConnected) { CAddress addrLocal = GetLocalAddress(&pnode->addr); - if (addrLocal.IsRoutable() && (CNetAddr)addrLocal != (CNetAddr)pnode->addrLocal) + if (addrLocal.IsRoutable() && (CService)addrLocal != (CService)pnode->addrLocal) { pnode->PushAddress(addrLocal); pnode->addrLocal = addrLocal; @@ -204,16 +206,30 @@ void static AdvertizeLocal() } // learn a new local address -bool AddLocal(const CNetAddr& addr, int nScore) +bool AddLocal(const CService& addr, int nScore) { if (!addr.IsRoutable()) return false; + if (!GetBoolArg("-discover", true) && nScore < LOCAL_MANUAL) + return false; + + if (IsLimited(addr)) + return false; + printf("AddLocal(%s,%i)\n", addr.ToString().c_str(), nScore); { LOCK(cs_mapLocalHost); - mapLocalHost[addr] = std::max(nScore, mapLocalHost[addr]) + (mapLocalHost.count(addr) ? 1 : 0); + bool fAlready = mapLocalHost.count(addr) > 0; + LocalServiceInfo &info = mapLocalHost[addr]; + if (!fAlready || nScore >= info.nScore) { + info.nScore = nScore; + info.nPort = addr.GetPort() + (fAlready ? 1 : 0); + } + enum Network net = addr.GetNetwork(); + vfReachable[net] = true; + if (net == NET_IPV6) vfReachable[NET_IPV4] = true; } AdvertizeLocal(); @@ -221,14 +237,39 @@ bool AddLocal(const CNetAddr& addr, int nScore) return true; } -// vote for a local address -bool SeenLocal(const CNetAddr& addr) +bool AddLocal(const CNetAddr &addr, int nScore) +{ + return AddLocal(CService(addr, GetListenPort()), nScore); +} + +/** Make a particular network entirely off-limits (no automatic connects to it) */ +void SetLimited(enum Network net, bool fLimited) +{ + if (net == NET_UNROUTABLE) + return; + LOCK(cs_mapLocalHost); + vfLimited[net] = fLimited; +} + +bool IsLimited(enum Network net) +{ + LOCK(cs_mapLocalHost); + return vfLimited[net]; +} + +bool IsLimited(const CNetAddr &addr) +{ + return IsLimited(addr.GetNetwork()); +} + +/** vote for a local address */ +bool SeenLocal(const CService& addr) { { LOCK(cs_mapLocalHost); if (mapLocalHost.count(addr) == 0) return false; - mapLocalHost[addr]++; + mapLocalHost[addr].nScore++; } AdvertizeLocal(); @@ -236,13 +277,20 @@ bool SeenLocal(const CNetAddr& addr) return true; } -// check whether a given address is potentially local -bool IsLocal(const CNetAddr& addr) +/** check whether a given address is potentially local */ +bool IsLocal(const CService& addr) { LOCK(cs_mapLocalHost); return mapLocalHost.count(addr) > 0; } +/** 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]; +} bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet) { @@ -463,10 +511,6 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, int64 nTimeout) LOCK(cs_vNodes); vNodes.push_back(pnode); } - { - WAITABLE_LOCK(csOutbound); - nOutbound++; - } pnode->nTimeConnected = GetTime(); return pnode; @@ -612,14 +656,8 @@ void ThreadSocketHandler2(void* parg) // remove from vNodes vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); - if (!pnode->fInbound) - { - WAITABLE_LOCK(csOutbound); - nOutbound--; - - // Connection slot(s) were removed, notify connection creator(s) - NOTIFY(condOutbound); - } + // release outbound grant (if any) + pnode->grantOutbound.Release(); // close socket and cleanup pnode->CloseSocketDisconnect(); @@ -688,9 +726,10 @@ void ThreadSocketHandler2(void* parg) FD_ZERO(&fdsetError); SOCKET hSocketMax = 0; - if(hListenSocket != INVALID_SOCKET) + BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) { FD_SET(hListenSocket, &fdsetRecv); - hSocketMax = max(hSocketMax, hListenSocket); + hSocketMax = max(hSocketMax, hListenSocket); + } { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -731,16 +770,22 @@ void ThreadSocketHandler2(void* parg) // // Accept new connections // + BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) { - struct sockaddr_in sockaddr; +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif socklen_t len = sizeof(sockaddr); SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); CAddress addr; int nInbound = 0; if (hSocket != INVALID_SOCKET) - addr = CAddress(sockaddr); + if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) + printf("warning: unknown socket family\n"); { LOCK(cs_vNodes); @@ -1295,8 +1340,11 @@ void static ProcessOneShot() vOneShots.pop_front(); } CAddress addr; - if (!OpenNetworkConnection(addr, strDest.c_str(), true)) - AddOneShot(strDest); + CSemaphoreGrant grant(*semOutbound, true); + if (grant) { + if (!OpenNetworkConnection(addr, &grant, strDest.c_str(), true)) + AddOneShot(strDest); + } } void ThreadOpenConnections2(void* parg) @@ -1312,7 +1360,7 @@ void ThreadOpenConnections2(void* parg) BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) { CAddress addr; - OpenNetworkConnection(addr, strAddr.c_str()); + OpenNetworkConnection(addr, NULL, strAddr.c_str()); for (int i = 0; i < 10 && i < nLoop; i++) { Sleep(500); @@ -1335,13 +1383,9 @@ void ThreadOpenConnections2(void* parg) if (fShutdown) return; - // Limit outbound connections - int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, (int)GetArg("-maxconnections", 125)); + vnThreadsRunning[THREAD_OPENCONNECTIONS]--; - { - WAITABLE_LOCK(csOutbound); - WAIT(condOutbound, fShutdown || nOutbound < nMaxOutbound); - } + CSemaphoreGrant grant(*semOutbound); vnThreadsRunning[THREAD_OPENCONNECTIONS]++; if (fShutdown) return; @@ -1374,11 +1418,15 @@ void ThreadOpenConnections2(void* parg) // Only connect to one address per a.b.?.? range. // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. + int nOutbound = 0; set<vector<unsigned char> > setConnected; { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + BOOST_FOREACH(CNode* pnode, vNodes) { setConnected.insert(pnode->addr.GetGroup()); + if (!pnode->fInbound) + nOutbound++; + } } int64 nANow = GetAdjustedTime(); @@ -1390,11 +1438,14 @@ void ThreadOpenConnections2(void* parg) CAddress addr = addrman.Select(10 + min(nOutbound,8)*10); // if we selected an invalid address, restart - if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) + if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) break; nTries++; + if (IsLimited(addr)) + continue; + // only consider very recently tried nodes after 30 failed attempts if (nANow - addr.nLastTry < 600 && nTries < 30) continue; @@ -1408,7 +1459,7 @@ void ThreadOpenConnections2(void* parg) } if (addrConnect.IsValid()) - OpenNetworkConnection(addrConnect); + OpenNetworkConnection(addrConnect, &grant); } } @@ -1442,7 +1493,8 @@ void ThreadOpenAddedConnections2(void* parg) while(!fShutdown) { BOOST_FOREACH(string& strAddNode, mapMultiArgs["-addnode"]) { CAddress addr; - OpenNetworkConnection(addr, strAddNode.c_str()); + CSemaphoreGrant grant(*semOutbound); + OpenNetworkConnection(addr, &grant, strAddNode.c_str()); Sleep(500); } vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; @@ -1485,7 +1537,8 @@ void ThreadOpenAddedConnections2(void* parg) } BOOST_FOREACH(vector<CService>& vserv, vservConnectAddresses) { - OpenNetworkConnection(CAddress(*(vserv.begin()))); + CSemaphoreGrant grant(*semOutbound); + OpenNetworkConnection(CAddress(*(vserv.begin())), &grant); Sleep(500); if (fShutdown) return; @@ -1500,7 +1553,8 @@ void ThreadOpenAddedConnections2(void* parg) } } -bool OpenNetworkConnection(const CAddress& addrConnect, const char *strDest, bool fOneShot) +// if succesful, this moves the passed grant to the constructed node +bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *strDest, bool fOneShot) { // // Initiate outbound network connection @@ -1522,6 +1576,8 @@ bool OpenNetworkConnection(const CAddress& addrConnect, const char *strDest, boo return false; if (!pnode) return false; + if (grantOutbound) + grantOutbound->MoveTo(pnode->grantOutbound); pnode->fNetworkNode = true; if (fOneShot) pnode->fOneShot = true; @@ -1618,7 +1674,7 @@ void ThreadMessageHandler2(void* parg) -bool BindListenPort(string& strError) +bool BindListenPort(const CService &addrBind, string& strError) { strError = ""; int nOne = 1; @@ -1636,7 +1692,20 @@ bool BindListenPort(string& strError) #endif // Create socket for listening for incoming connections - hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); +#ifdef USE_IPV6 + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; +#endif + socklen_t len = sizeof(sockaddr); + if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) + { + strError = strprintf("Error: bind address family for %s not supported", addrBind.ToString().c_str()); + printf("%s\n", strError.c_str()); + return false; + } + + SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); if (hListenSocket == INVALID_SOCKET) { strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); @@ -1655,6 +1724,7 @@ bool BindListenPort(string& strError) setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); #endif + #ifdef WIN32 // Set to nonblocking, incoming connections will also inherit this if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR) @@ -1667,24 +1737,33 @@ bool BindListenPort(string& strError) 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 = htons(GetListenPort()); - if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) +#ifdef USE_IPV6 + // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option + // and enable it by default or not. Try to enable it, if possible. + if (addrBind.IsIPv6()) { +#ifdef IPV6_V6ONLY + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int)); +#endif +#ifdef WIN32 + int nProtLevel = 10 /* PROTECTION_LEVEL_UNRESTRICTED */; + int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */; + // this call is allowed to fail + setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (const char*)&nProtLevel, sizeof(int)); +#endif + } +#endif + + if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == 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)); + strError = strprintf(_("Unable to bind to %s on this computer. Bitcoin is probably already running."), addrBind.ToString().c_str()); else - strError = strprintf("Error: Unable to bind to port %d on this computer (bind returned error %d)", ntohs(sockaddr.sin_port), nErr); + strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %d, %s)"), addrBind.ToString().c_str(), nErr, strerror(nErr)); printf("%s\n", strError.c_str()); return false; } - printf("Bound to port %d\n", ntohs(sockaddr.sin_port)); + printf("Bound to %s\n", addrBind.ToString().c_str()); // Listen for incoming connections if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) @@ -1694,6 +1773,11 @@ bool BindListenPort(string& strError) return false; } + vhListenSocket.push_back(hListenSocket); + + if (addrBind.IsRoutable() && GetBoolArg("-discover", true)) + AddLocal(addrBind, LOCAL_BIND); + return true; } @@ -1727,28 +1811,22 @@ void static Discover() 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 CNetAddr addr(s4->sin_addr); - AddLocal(addr, LOCAL_IF); + if (AddLocal(addr, LOCAL_IF)) + printf("IPv4 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); } +#ifdef USE_IPV6 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); - -#ifdef USE_IPV6 CNetAddr addr(s6->sin6_addr); - AddLocal(addr, LOCAL_IF); -#endif + if (AddLocal(addr, LOCAL_IF)) + printf("IPv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); } +#endif } freeifaddrs(myaddrs); } @@ -1770,6 +1848,12 @@ void StartNode(void* parg) #endif #endif + if (semOutbound == NULL) { + // initialize semaphore + int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, (int)GetArg("-maxconnections", 125)); + semOutbound = new CSemaphore(nMaxOutbound); + } + if (pnodeLocalHost == NULL) pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); @@ -1823,7 +1907,9 @@ bool StopNode() fShutdown = true; nTransactionsUpdated++; int64 nStart = GetTime(); - NOTIFY_ALL(condOutbound); + if (semOutbound) + for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++) + semOutbound->post(); do { int nThreadsRunning = 0; @@ -1839,12 +1925,13 @@ bool StopNode() if (vnThreadsRunning[THREAD_OPENCONNECTIONS] > 0) printf("ThreadOpenConnections still running\n"); if (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0) printf("ThreadMessageHandler still running\n"); if (vnThreadsRunning[THREAD_MINER] > 0) printf("ThreadBitcoinMiner still running\n"); - if (vnThreadsRunning[THREAD_RPCSERVER] > 0) printf("ThreadRPCServer still running\n"); + if (vnThreadsRunning[THREAD_RPCLISTENER] > 0) printf("ThreadRPCListener still running\n"); + if (vnThreadsRunning[THREAD_RPCHANDLER] > 0) printf("ThreadsRPCServer still running\n"); if (fHaveUPnP && vnThreadsRunning[THREAD_UPNP] > 0) printf("ThreadMapPort still running\n"); if (vnThreadsRunning[THREAD_DNSSEED] > 0) printf("ThreadDNSAddressSeed still running\n"); if (vnThreadsRunning[THREAD_ADDEDCONNECTIONS] > 0) printf("ThreadOpenAddedConnections still running\n"); if (vnThreadsRunning[THREAD_DUMPADDRESS] > 0) printf("ThreadDumpAddresses still running\n"); - while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCSERVER] > 0) + while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCHANDLER] > 0) Sleep(20); Sleep(50); DumpAddresses(); @@ -1863,9 +1950,10 @@ public: BOOST_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()); + BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) + if (hListenSocket != INVALID_SOCKET) + if (closesocket(hListenSocket) == SOCKET_ERROR) + printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); #ifdef WIN32 // Shutdown Windows Sockets |