diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/addrman.h | 1 | ||||
-rw-r--r-- | src/init.cpp | 61 | ||||
-rw-r--r-- | src/irc.cpp | 2 | ||||
-rw-r--r-- | src/keystore.h | 2 | ||||
-rw-r--r-- | src/main.cpp | 12 | ||||
-rw-r--r-- | src/main.h | 1 | ||||
-rw-r--r-- | src/makefile.linux-mingw | 3 | ||||
-rw-r--r-- | src/makefile.mingw | 3 | ||||
-rw-r--r-- | src/makefile.osx | 3 | ||||
-rw-r--r-- | src/makefile.unix | 3 | ||||
-rw-r--r-- | src/net.cpp | 224 | ||||
-rw-r--r-- | src/net.h | 31 | ||||
-rw-r--r-- | src/netbase.cpp | 164 | ||||
-rw-r--r-- | src/netbase.h | 22 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 4 | ||||
-rw-r--r-- | src/qt/csvmodelwriter.cpp | 5 | ||||
-rw-r--r-- | src/qt/forms/sendcoinsdialog.ui | 2 | ||||
-rw-r--r-- | src/qt/messagepage.cpp | 1 | ||||
-rw-r--r-- | src/qt/qtipcserver.cpp | 1 | ||||
-rw-r--r-- | src/sync.cpp | 119 | ||||
-rw-r--r-- | src/sync.h | 216 | ||||
-rw-r--r-- | src/util.cpp | 132 | ||||
-rw-r--r-- | src/util.h | 123 |
23 files changed, 733 insertions, 402 deletions
diff --git a/src/addrman.h b/src/addrman.h index 43b6d35ed8..a1275da2d5 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -7,6 +7,7 @@ #include "netbase.h" #include "protocol.h" #include "util.h" +#include "sync.h" #include <map> diff --git a/src/init.cpp b/src/init.cpp index 60927f20b3..877b1d471f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -119,6 +119,18 @@ bool AppInit(int argc, char* argv[]) return fRet; } +bool static Bind(const CService &addr) { + if (IsLimited(addr)) + return false; + std::string strError; + if (!BindListenPort(addr, strError)) + { + ThreadSafeMessageBox(strError, _("Bitcoin"), wxOK | wxMODAL); + return false; + } + return true; +} + bool AppInit2(int argc, char* argv[]) { #ifdef _MSC_VER @@ -180,6 +192,7 @@ bool AppInit2(int argc, char* argv[]) " -timeout=<n> \t " + _("Specify connection timeout (in milliseconds)") + "\n" + " -proxy=<ip:port> \t " + _("Connect through socks proxy") + "\n" + " -socks=<n> \t " + _("Select the version of socks proxy to use (4 or 5, 5 is default)") + "\n" + + " -noproxy=<net> \t " + _("Do not use proxy for connections to network net (ipv4 or ipv6)") + "\n" + " -dns \t " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" + " -proxydns \t " + _("Pass DNS requests to (SOCKS5) proxy") + "\n" + " -port=<port> \t\t " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n" + @@ -188,9 +201,11 @@ bool AppInit2(int argc, char* argv[]) " -connect=<ip> \t\t " + _("Connect only to the specified node") + "\n" + " -seednode=<ip> \t\t " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" + " -externalip=<ip> \t " + _("Specify your own public address") + "\n" + + " -blocknet=<net> \t " + _("Do not connect to addresses in network net (ipv4, ipv6)") + "\n" + " -discover \t " + _("Try to discover public IP address (default: 1)") + "\n" + " -irc \t " + _("Find peers using internet relay chat (default: 0)") + "\n" + " -listen \t " + _("Accept connections from outside (default: 1)") + "\n" + + " -bind=<addr> \t " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + #ifdef QT_GUI " -lang=<lang> \t\t " + _("Set language, for example \"de_DE\" (default: system locale)") + "\n" + #endif @@ -532,9 +547,25 @@ bool AppInit2(int argc, char* argv[]) } } + if (mapArgs.count("-noproxy")) + { + BOOST_FOREACH(std::string snet, mapMultiArgs["-noproxy"]) { + enum Network net = ParseNetwork(snet); + if (net == NET_UNROUTABLE) { + ThreadSafeMessageBox(_("Unknown network specified in -noproxy"), _("Bitcoin"), wxOK | wxMODAL); + return false; + } + SetNoProxy(net); + } + } + if (mapArgs.count("-connect")) SoftSetBoolArg("-dnsseed", false); - + + // even in Tor mode, if -bind is specified, you really want -listen + if (mapArgs.count("-bind")) + SoftSetBoolArg("-listen", true); + bool fTor = (fUseProxy && addrProxy.GetPort() == 9050); if (fTor) { @@ -547,6 +578,17 @@ bool AppInit2(int argc, char* argv[]) SoftSetBoolArg("-discover", false); } + if (mapArgs.count("-blocknet")) { + BOOST_FOREACH(std::string snet, mapMultiArgs["-blocknet"]) { + enum Network net = ParseNetwork(snet); + if (net == NET_UNROUTABLE) { + ThreadSafeMessageBox(_("Unknown network specified in -blocknet"), _("Bitcoin"), wxOK | wxMODAL); + return false; + } + SetLimited(net); + } + } + fNameLookup = GetBoolArg("-dns"); fProxyNameLookup = GetBoolArg("-proxydns"); if (fProxyNameLookup) @@ -563,14 +605,23 @@ bool AppInit2(int argc, char* argv[]) const char* pszP2SH = "/P2SH/"; COINBASE_FLAGS << std::vector<unsigned char>(pszP2SH, pszP2SH+strlen(pszP2SH)); + bool fBound = false; if (!fNoListen) { std::string strError; - if (!BindListenPort(strError)) - { - ThreadSafeMessageBox(strError, _("Bitcoin"), wxOK | wxMODAL); - return false; + if (mapArgs.count("-bind")) { + BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { + fBound |= Bind(CService(strBind, GetDefaultPort(), false)); + } + } else { + struct in_addr inaddr_any = {s_addr: INADDR_ANY}; + fBound |= Bind(CService(inaddr_any, GetDefaultPort())); +#ifdef USE_IPV6 + fBound |= Bind(CService(in6addr_any, GetDefaultPort())); +#endif } + if (!fBound) + return false; } if (mapArgs.count("-externalip")) diff --git a/src/irc.cpp b/src/irc.cpp index f20152495e..525bd7a8da 100644 --- a/src/irc.cpp +++ b/src/irc.cpp @@ -246,7 +246,7 @@ void ThreadIRCSeed2(void* parg) return; } - CNetAddr addrLocal; + CService addrLocal; string strMyName; if (GetLocal(addrLocal, &addrConnect)) strMyName = EncodeAddress(GetLocalAddress(&addrConnect)); diff --git a/src/keystore.h b/src/keystore.h index 76820e204b..52889b184e 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -6,7 +6,7 @@ #define BITCOIN_KEYSTORE_H #include "crypter.h" -#include "util.h" +#include "sync.h" #include "base58.h" class CScript; diff --git a/src/main.cpp b/src/main.cpp index 98f9b71ef5..20bb56e964 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2419,18 +2419,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } // Store the new addresses + vector<CAddress> vAddrOk; int64 nNow = GetAdjustedTime(); int64 nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) { if (fShutdown) return true; - // ignore IPv6 for now, since it isn't implemented anyway - if (!addr.IsIPv4()) - continue; if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); + bool fReachable = IsReachable(addr); if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes @@ -2455,13 +2454,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) hashKey = Hash(BEGIN(hashKey), END(hashKey)); mapMix.insert(make_pair(hashKey, pnode)); } - int nRelayNodes = 2; + int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) for (multimap<uint256, CNode*>::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) ((*mi).second)->PushAddress(addr); } } + // Do not store addresses outside our network + if (fReachable) + vAddrOk.push_back(addr); } - addrman.Add(vAddr, pfrom->addr, 2 * 60 * 60); + addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom->fGetAddr = false; if (pfrom->fOneShot) diff --git a/src/main.h b/src/main.h index 194d9fdc66..5ac5547a3e 100644 --- a/src/main.h +++ b/src/main.h @@ -6,6 +6,7 @@ #define BITCOIN_MAIN_H #include "bignum.h" +#include "sync.h" #include "net.h" #include "script.h" diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 645f0a16e4..1df1e28f1d 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -27,7 +27,7 @@ LIBS= \ -l ssl \ -l crypto -DEFS=-D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB +DEFS=-D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DUSE_IPV6 DEBUGFLAGS=-g CFLAGS=-O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) @@ -61,6 +61,7 @@ OBJS= \ obj/bitcoinrpc.o \ obj/rpcdump.o \ obj/script.o \ + obj/sync.o \ obj/util.o \ obj/wallet.o \ obj/walletdb.o \ diff --git a/src/makefile.mingw b/src/makefile.mingw index bb6466954f..47bf8d5304 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -23,7 +23,7 @@ LIBS= \ -l ssl \ -l crypto -DEFS=-DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB +DEFS=-DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB -DUSE_IPV6 DEBUGFLAGS=-g CFLAGS=-mthreads -O2 -w -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) @@ -58,6 +58,7 @@ OBJS= \ obj/bitcoinrpc.o \ obj/rpcdump.o \ obj/script.o \ + obj/sync.o \ obj/util.o \ obj/wallet.o \ obj/walletdb.o \ diff --git a/src/makefile.osx b/src/makefile.osx index eb9ae4ba7f..c927330c9c 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -53,7 +53,7 @@ LIBS += \ TESTDEFS += -DBOOST_TEST_DYN_LINK endif -DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 +DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 -DUSE_IPV6 ifdef RELEASE # Compile for maximum compatibility and smallest size. @@ -85,6 +85,7 @@ OBJS= \ obj/bitcoinrpc.o \ obj/rpcdump.o \ obj/script.o \ + obj/sync.o \ obj/util.o \ obj/wallet.o \ obj/walletdb.o \ diff --git a/src/makefile.unix b/src/makefile.unix index 53fb1f0b8d..ad0a82df50 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -4,7 +4,7 @@ USE_UPNP:=0 -DEFS= +DEFS=-DUSE_IPV6 DEFS += $(addprefix -I,$(CURDIR) $(CURDIR)/obj $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH)) LIBS = $(addprefix -L,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH)) @@ -102,6 +102,7 @@ OBJS= \ obj/bitcoinrpc.o \ obj/rpcdump.o \ obj/script.o \ + obj/sync.o \ obj/util.o \ obj/wallet.o \ obj/walletdb.o \ diff --git a/src/net.cpp b/src/net.cpp index a9ac44a08e..f12afb78e6 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -35,7 +35,7 @@ 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); @@ -45,12 +45,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<CService, int> 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 +68,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,7 +93,7 @@ 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; @@ -103,7 +102,7 @@ bool GetLocal(CNetAddr& addr, const CNetAddr *paddrPeer) int nBestReachability = -1; { LOCK(cs_mapLocalHost); - for (map<CNetAddr, int>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) + for (map<CService, int>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) { int nCount = (*it).second; int nReachability = (*it).first.GetReachabilityFrom(paddrPeer); @@ -122,11 +121,10 @@ bool GetLocal(CNetAddr& addr, const CNetAddr *paddrPeer) 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 +192,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,7 +202,7 @@ 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; @@ -214,6 +212,9 @@ bool AddLocal(const CNetAddr& addr, int nScore) { LOCK(cs_mapLocalHost); mapLocalHost[addr] = std::max(nScore, mapLocalHost[addr]) + (mapLocalHost.count(addr) ? 1 : 0); + enum Network net = addr.GetNetwork(); + vfReachable[net] = true; + if (net == NET_IPV6) vfReachable[NET_IPV4] = true; } AdvertizeLocal(); @@ -221,8 +222,28 @@ 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, int port) +{ + if (port == -1) + port = GetListenPort(); + return AddLocal(CService(addr, port), nScore); +} + +/** Make a particular network entirely off-limits (no automatic connects to it) */ +void SetLimited(enum Network net, bool fLimited) +{ + LOCK(cs_mapLocalHost); + vfLimited[net] = fLimited; +} + +bool IsLimited(const CNetAddr& addr) +{ + LOCK(cs_mapLocalHost); + return vfLimited[addr.GetNetwork()]; +} + +/** vote for a local address */ +bool SeenLocal(const CService& addr) { { LOCK(cs_mapLocalHost); @@ -236,13 +257,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 +491,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 +636,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 +706,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 +750,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 +1320,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 +1340,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 +1363,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 +1398,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 +1418,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 +1439,7 @@ void ThreadOpenConnections2(void* parg) } if (addrConnect.IsValid()) - OpenNetworkConnection(addrConnect); + OpenNetworkConnection(addrConnect, &grant); } } @@ -1442,7 +1473,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 +1517,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 +1533,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 +1556,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 +1654,7 @@ void ThreadMessageHandler2(void* parg) -bool BindListenPort(string& strError) +bool BindListenPort(const CService &addrBind, string& strError) { strError = ""; int nOne = 1; @@ -1636,7 +1672,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 +1704,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 +1717,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 +1753,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 +1791,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 +1828,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 +1887,8 @@ bool StopNode() fShutdown = true; nTransactionsUpdated++; int64 nStart = GetTime(); - NOTIFY_ALL(condOutbound); + for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++) + semOutbound->post(); do { int nThreadsRunning = 0; @@ -1864,9 +1929,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 @@ -38,28 +38,34 @@ CNode* FindNode(const CNetAddr& ip); CNode* FindNode(const CService& ip); CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL, int64 nTimeout=0); void MapPort(bool fMapPort); -bool BindListenPort(std::string& strError=REF(std::string())); +bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string())); void StartNode(void* parg); bool StopNode(); enum { - LOCAL_NONE, - LOCAL_IF, - LOCAL_UPNP, - LOCAL_IRC, - LOCAL_HTTP, - LOCAL_MANUAL, + LOCAL_NONE, // unknown + LOCAL_IF, // address a local interface listens on + LOCAL_BIND, // address explicit bound to + LOCAL_UPNP, // address reported by UPnP + LOCAL_IRC, // address reported by IRC (deprecated) + LOCAL_HTTP, // address reported by whatismyip.com and similars + LOCAL_MANUAL, // address explicitly specified (-externalip=) LOCAL_MAX }; -bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); -bool SeenLocal(const CNetAddr& addr); -bool IsLocal(const CNetAddr& addr); -bool GetLocal(CNetAddr &addr, const CNetAddr *paddrPeer = NULL); +void SetLimited(enum Network net, bool fLimited = true); +bool IsLimited(const CNetAddr& addr); +bool AddLocal(const CService& addr, int nScore = LOCAL_NONE); +bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE, int port = -1); +bool SeenLocal(const CService& addr); +bool IsLocal(const CService& addr); +bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL); +bool IsReachable(const CNetAddr &addr); CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); + enum { MSG_TX = 1, @@ -139,7 +145,7 @@ public: unsigned int nMessageStart; CAddress addr; std::string addrName; - CNetAddr addrLocal; + CService addrLocal; int nVersion; std::string strSubVer; bool fOneShot; @@ -148,6 +154,7 @@ public: bool fNetworkNode; bool fSuccessfullyConnected; bool fDisconnect; + CSemaphoreGrant grantOutbound; protected: int nRefCount; diff --git a/src/netbase.cpp b/src/netbase.cpp index 48709dc5c6..ebf823e899 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -21,10 +21,24 @@ bool fProxyNameLookup = false; bool fNameLookup = false; CService addrProxy("127.0.0.1",9050); int nConnectTimeout = 5000; +static bool vfNoProxy[NET_MAX] = {}; static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; +enum Network ParseNetwork(std::string net) { + if (net == "ipv4") return NET_IPV4; + if (net == "ipv6") return NET_IPV6; + if (net == "tor") return NET_TOR; + if (net == "i2p") return NET_I2P; + return NET_UNROUTABLE; +} + +void SetNoProxy(enum Network net, bool fNoProxy) { + assert(net >= 0 && net < NET_MAX); + vfNoProxy[net] = fNoProxy; +} + bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup) { vIP.clear(); @@ -169,7 +183,12 @@ bool static Socks4(const CService &addrDest, SOCKET& hSocket) } char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; struct sockaddr_in addr; - addrDest.GetSockAddr(&addr); + socklen_t len = sizeof(addr); + if (!addrDest.GetSockAddr((struct sockaddr*)&addr, &len) || addr.sin_family != AF_INET) + { + closesocket(hSocket); + return error("Cannot get proxy destination address"); + } memcpy(pszSocks4IP + 2, &addr.sin_port, 2); memcpy(pszSocks4IP + 4, &addr.sin_addr, 4); char* pszSocks4 = pszSocks4IP; @@ -305,7 +324,18 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe { hSocketRet = INVALID_SOCKET; - SOCKET hSocket = 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 (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) { + printf("Cannot connect to %s: unsupported network\n", addrConnect.ToString().c_str()); + return false; + } + + SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); if (hSocket == INVALID_SOCKET) return false; #ifdef SO_NOSIGPIPE @@ -313,13 +343,6 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); #endif - struct sockaddr_in sockaddr; - if (!addrConnect.GetSockAddr(&sockaddr)) - { - closesocket(hSocket); - return false; - } - #ifdef WIN32 u_long fNonblock = 1; if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) @@ -332,7 +355,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe return false; } - if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR) { // WSAEINVAL is here because some legacy version of winsock uses it if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL) @@ -409,7 +432,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout) { SOCKET hSocket = INVALID_SOCKET; - bool fProxy = (fUseProxy && addrDest.IsRoutable()); + bool fProxy = (fUseProxy && addrDest.IsRoutable() && !vfNoProxy[addrDest.GetNetwork()]); if (!ConnectSocketDirectly(fProxy ? addrProxy : addrDest, hSocket, nTimeout)) return false; @@ -531,6 +554,11 @@ bool CNetAddr::IsIPv4() const return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0); } +bool CNetAddr::IsIPv6() const +{ + return (!IsIPv4()); +} + bool CNetAddr::IsRFC1918() const { return IsIPv4() && ( @@ -587,6 +615,18 @@ bool CNetAddr::IsRFC4843() const return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10); } +bool CNetAddr::IsOnionCat() const +{ + static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; + return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0); +} + +bool CNetAddr::IsGarliCat() const +{ + static const unsigned char pchGarliCat[] = {0xFD,0x60,0xDB,0x4D,0xDD,0xB5}; + return (memcmp(ip, pchGarliCat, sizeof(pchGarliCat)) == 0); +} + bool CNetAddr::IsLocal() const { // IPv4 loopback @@ -645,7 +685,24 @@ bool CNetAddr::IsValid() const bool CNetAddr::IsRoutable() const { - return IsValid() && !(IsRFC1918() || IsRFC3927() || IsRFC4862() || IsRFC4193() || IsRFC4843() || IsLocal()); + return IsValid() && !(IsRFC1918() || IsRFC3927() || IsRFC4862() || (IsRFC4193() && !IsOnionCat() && !IsGarliCat()) || IsRFC4843() || IsLocal()); +} + +enum Network CNetAddr::GetNetwork() const +{ + if (!IsRoutable()) + return NET_UNROUTABLE; + + if (IsIPv4()) + return NET_IPV4; + + if (IsOnionCat()) + return NET_TOR; + + if (IsGarliCat()) + return NET_I2P; + + return NET_IPV6; } std::string CNetAddr::ToStringIP() const @@ -701,40 +758,40 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const std::vector<unsigned char> CNetAddr::GetGroup() const { std::vector<unsigned char> vchRet; - int nClass = 0; // 0=IPv6, 1=IPv4, 254=local, 255=unroutable + int nClass = NET_IPV6; int nStartByte = 0; int nBits = 16; // all local addresses belong to the same group if (IsLocal()) { - nClass = 254; + nClass = 255; nBits = 0; } // all unroutable addresses belong to the same group if (!IsRoutable()) { - nClass = 255; + nClass = NET_UNROUTABLE; nBits = 0; } // for IPv4 addresses, '1' + the 16 higher-order bits of the IP // includes mapped IPv4, SIIT translated IPv4, and the well-known prefix else if (IsIPv4() || IsRFC6145() || IsRFC6052()) { - nClass = 1; + nClass = NET_IPV4; nStartByte = 12; } // for 6to4 tunneled addresses, use the encapsulated IPv4 address else if (IsRFC3964()) { - nClass = 1; + nClass = NET_IPV4; nStartByte = 2; } // for Teredo-tunneled IPv6 addresses, use the encapsulated IPv4 address else if (IsRFC4380()) { - vchRet.push_back(1); + vchRet.push_back(NET_IPV4); vchRet.push_back(GetByte(3) ^ 0xFF); vchRet.push_back(GetByte(2) ^ 0xFF); return vchRet; @@ -831,6 +888,22 @@ CService::CService(const struct sockaddr_in6 &addr) : CNetAddr(addr.sin6_addr), } #endif +bool CService::SetSockAddr(const struct sockaddr *paddr) +{ + switch (paddr->sa_family) { + case AF_INET: + *this = CService(*(const struct sockaddr_in*)paddr); + return true; +#ifdef USE_IPV6 + case AF_INET6: + *this = CService(*(const struct sockaddr_in6*)paddr); + return true; +#endif + default: + return false; + } +} + CService::CService(const char *pszIpPort, bool fAllowLookup) { Init(); @@ -883,29 +956,36 @@ bool operator<(const CService& a, const CService& b) return (CNetAddr)a < (CNetAddr)b || ((CNetAddr)a == (CNetAddr)b && a.port < b.port); } -bool CService::GetSockAddr(struct sockaddr_in* paddr) const +bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const { - if (!IsIPv4()) - return false; - memset(paddr, 0, sizeof(struct sockaddr_in)); - if (!GetInAddr(&paddr->sin_addr)) - return false; - paddr->sin_family = AF_INET; - paddr->sin_port = htons(port); - return true; -} - + if (IsIPv4()) { + if (*addrlen < sizeof(struct sockaddr_in)) + return false; + *addrlen = sizeof(struct sockaddr_in); + struct sockaddr_in *paddrin = (struct sockaddr_in*)paddr; + memset(paddrin, 0, *addrlen); + if (!GetInAddr(&paddrin->sin_addr)) + return false; + paddrin->sin_family = AF_INET; + paddrin->sin_port = htons(port); + return true; + } #ifdef USE_IPV6 -bool CService::GetSockAddr6(struct sockaddr_in6* paddr) const -{ - memset(paddr, 0, sizeof(struct sockaddr_in6)); - if (!GetIn6Addr(&paddr->sin6_addr)) - return false; - paddr->sin6_family = AF_INET6; - paddr->sin6_port = htons(port); - return true; -} + if (IsIPv6()) { + if (*addrlen < sizeof(struct sockaddr_in6)) + return false; + *addrlen = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *paddrin6 = (struct sockaddr_in6*)paddr; + memset(paddrin6, 0, *addrlen); + if (!GetIn6Addr(&paddrin6->sin6_addr)) + return false; + paddrin6->sin6_family = AF_INET6; + paddrin6->sin6_port = htons(port); + return true; + } #endif + return false; +} std::vector<unsigned char> CService::GetKey() const { @@ -919,12 +999,16 @@ std::vector<unsigned char> CService::GetKey() const std::string CService::ToStringPort() const { - return strprintf(":%i", port); + return strprintf("%i", port); } std::string CService::ToStringIPPort() const { - return ToStringIP() + ToStringPort(); + if (IsIPv4()) { + return ToStringIP() + ":" + ToStringPort(); + } else { + return "[" + ToStringIP() + "]:" + ToStringPort(); + } } std::string CService::ToString() const diff --git a/src/netbase.h b/src/netbase.h index e5c466e4f0..514a1ae950 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -17,6 +17,20 @@ extern int nConnectTimeout; #undef SetPort #endif +enum Network +{ + NET_UNROUTABLE, + NET_IPV4, + NET_IPV6, + NET_TOR, + NET_I2P, + + NET_MAX +}; + +enum Network ParseNetwork(std::string net); +void SetNoProxy(enum Network net, bool fNoProxy = true); + /** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */ class CNetAddr { @@ -31,6 +45,7 @@ class CNetAddr void Init(); void SetIP(const CNetAddr& ip); bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) + bool IsIPv6() const; // IPv6 address (not IPv4) bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16) @@ -41,10 +56,13 @@ class CNetAddr bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64) bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96) bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) + bool IsOnionCat() const; + bool IsGarliCat() const; bool IsLocal() const; bool IsRoutable() const; bool IsValid() const; bool IsMulticast() const; + enum Network GetNetwork() const; std::string ToString() const; std::string ToStringIP() const; int GetByte(int n) const; @@ -87,7 +105,8 @@ class CService : public CNetAddr void Init(); void SetPort(unsigned short portIn); unsigned short GetPort() const; - bool GetSockAddr(struct sockaddr_in* paddr) const; + bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const; + bool SetSockAddr(const struct sockaddr* paddr); friend bool operator==(const CService& a, const CService& b); friend bool operator!=(const CService& a, const CService& b); friend bool operator<(const CService& a, const CService& b); @@ -99,7 +118,6 @@ class CService : public CNetAddr #ifdef USE_IPV6 CService(const struct in6_addr& ipv6Addr, unsigned short port); - bool GetSockAddr6(struct sockaddr_in6* paddr) const; CService(const struct sockaddr_in6& addr); #endif diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 40ab4acc77..74969fc6f3 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1,8 +1,8 @@ /* * Qt4 bitcoin GUI. * - * W.J. van der Laan 20011-2012 - * The Bitcoin Developers 20011-2012 + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2012 */ #include "bitcoingui.h" #include "transactiontablemodel.h" diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp index 84578b3322..8a50bbab3f 100644 --- a/src/qt/csvmodelwriter.cpp +++ b/src/qt/csvmodelwriter.cpp @@ -27,8 +27,9 @@ void CSVModelWriter::addColumn(const QString &title, int column, int role) static void writeValue(QTextStream &f, const QString &value) { - // TODO: quoting if " or \n in string - f << "\"" << value << "\""; + QString escaped = value; + escaped.replace('"', "\"\""); + f << "\"" << escaped << "\""; } static void writeSep(QTextStream &f) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 49b4580dcf..023a72ac2b 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -84,7 +84,7 @@ <string>Remove all transaction fields</string> </property> <property name="text"> - <string>Clear all</string> + <string>Clear &All</string> </property> <property name="icon"> <iconset resource="../bitcoin.qrc"> diff --git a/src/qt/messagepage.cpp b/src/qt/messagepage.cpp index c04d8b2c78..1f895e28ff 100644 --- a/src/qt/messagepage.cpp +++ b/src/qt/messagepage.cpp @@ -10,7 +10,6 @@ #include "main.h" #include "wallet.h" #include "init.h" -#include "util.h" #include "messagepage.h" #include "ui_messagepage.h" diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp index 102ac0ff4e..06ada5aaca 100644 --- a/src/qt/qtipcserver.cpp +++ b/src/qt/qtipcserver.cpp @@ -8,7 +8,6 @@ #include <boost/date_time/posix_time/posix_time.hpp> #include "ui_interface.h" -#include "util.h" #include "qtipcserver.h" using namespace boost::interprocess; diff --git a/src/sync.cpp b/src/sync.cpp new file mode 100644 index 0000000000..fd9bcb62bc --- /dev/null +++ b/src/sync.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2011-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "sync.h" + + + +#ifdef DEBUG_LOCKORDER +// +// Early deadlock detection. +// Problem being solved: +// Thread 1 locks A, then B, then C +// Thread 2 locks D, then C, then A +// --> may result in deadlock between the two threads, depending on when they run. +// Solution implemented here: +// Keep track of pairs of locks: (A before B), (A before C), etc. +// Complain if any thread trys to lock in a different order. +// + +struct CLockLocation +{ + CLockLocation(const char* pszName, const char* pszFile, int nLine) + { + mutexName = pszName; + sourceFile = pszFile; + sourceLine = nLine; + } + + std::string ToString() const + { + return mutexName+" "+sourceFile+":"+itostr(sourceLine); + } + +private: + std::string mutexName; + std::string sourceFile; + int sourceLine; +}; + +typedef std::vector< std::pair<void*, CLockLocation> > LockStack; + +static boost::interprocess::interprocess_mutex dd_mutex; +static std::map<std::pair<void*, void*>, LockStack> lockorders; +static boost::thread_specific_ptr<LockStack> lockstack; + + +static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2) +{ + printf("POTENTIAL DEADLOCK DETECTED\n"); + printf("Previous lock order was:\n"); + BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s2) + { + if (i.first == mismatch.first) printf(" (1)"); + if (i.first == mismatch.second) printf(" (2)"); + printf(" %s\n", i.second.ToString().c_str()); + } + printf("Current lock order is:\n"); + BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s1) + { + if (i.first == mismatch.first) printf(" (1)"); + if (i.first == mismatch.second) printf(" (2)"); + printf(" %s\n", i.second.ToString().c_str()); + } +} + +static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) +{ + bool fOrderOK = true; + if (lockstack.get() == NULL) + lockstack.reset(new LockStack); + + if (fDebug) printf("Locking: %s\n", locklocation.ToString().c_str()); + dd_mutex.lock(); + + (*lockstack).push_back(std::make_pair(c, locklocation)); + + if (!fTry) BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, (*lockstack)) + { + if (i.first == c) break; + + std::pair<void*, void*> p1 = std::make_pair(i.first, c); + if (lockorders.count(p1)) + continue; + lockorders[p1] = (*lockstack); + + std::pair<void*, void*> p2 = std::make_pair(c, i.first); + if (lockorders.count(p2)) + { + potential_deadlock_detected(p1, lockorders[p2], lockorders[p1]); + break; + } + } + dd_mutex.unlock(); +} + +static void pop_lock() +{ + if (fDebug) + { + const CLockLocation& locklocation = (*lockstack).rbegin()->second; + printf("Unlocked: %s\n", locklocation.ToString().c_str()); + } + dd_mutex.lock(); + (*lockstack).pop_back(); + dd_mutex.unlock(); +} + +void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) +{ + push_lock(cs, CLockLocation(pszName, pszFile, nLine), fTry); +} + +void LeaveCritical() +{ + pop_lock(); +} + +#endif /* DEBUG_LOCKORDER */ diff --git a/src/sync.h b/src/sync.h new file mode 100644 index 0000000000..44d753ac15 --- /dev/null +++ b/src/sync.h @@ -0,0 +1,216 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_SYNC_H +#define BITCOIN_SYNC_H + +#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp> +#include <boost/interprocess/sync/scoped_lock.hpp> +#include <boost/interprocess/sync/interprocess_semaphore.hpp> +#include <boost/interprocess/sync/lock_options.hpp> + + + + +/** Wrapped boost mutex: supports recursive locking, but no waiting */ +typedef boost::interprocess::interprocess_recursive_mutex CCriticalSection; + +/** Wrapped boost mutex: supports waiting but not recursive locking */ +typedef boost::interprocess::interprocess_mutex CWaitableCriticalSection; + +#ifdef DEBUG_LOCKORDER +void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); +void LeaveCritical(); +#else +void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} +void static inline LeaveCritical() {} +#endif + +/** Wrapper around boost::interprocess::scoped_lock */ +template<typename Mutex> +class CMutexLock +{ +private: + boost::interprocess::scoped_lock<Mutex> lock; +public: + + void Enter(const char* pszName, const char* pszFile, int nLine) + { + if (!lock.owns()) + { + EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); +#ifdef DEBUG_LOCKCONTENTION + if (!lock.try_lock()) + { + printf("LOCKCONTENTION: %s\n", pszName); + printf("Locker: %s:%d\n", pszFile, nLine); +#endif + lock.lock(); +#ifdef DEBUG_LOCKCONTENTION + } +#endif + } + } + + void Leave() + { + if (lock.owns()) + { + lock.unlock(); + LeaveCritical(); + } + } + + bool TryEnter(const char* pszName, const char* pszFile, int nLine) + { + if (!lock.owns()) + { + EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); + lock.try_lock(); + if (!lock.owns()) + LeaveCritical(); + } + return lock.owns(); + } + + CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) : lock(mutexIn, boost::interprocess::defer_lock) + { + if (fTry) + TryEnter(pszName, pszFile, nLine); + else + Enter(pszName, pszFile, nLine); + } + + ~CMutexLock() + { + if (lock.owns()) + LeaveCritical(); + } + + operator bool() + { + return lock.owns(); + } + + boost::interprocess::scoped_lock<Mutex> &GetLock() + { + return lock; + } +}; + +typedef CMutexLock<CCriticalSection> CCriticalBlock; + +#define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__) +#define LOCK2(cs1,cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__),criticalblock2(cs2, #cs2, __FILE__, __LINE__) +#define TRY_LOCK(cs,name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) + +#define ENTER_CRITICAL_SECTION(cs) \ + { \ + EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \ + (cs).lock(); \ + } + +#define LEAVE_CRITICAL_SECTION(cs) \ + { \ + (cs).unlock(); \ + LeaveCritical(); \ + } + +#ifdef MAC_OSX +// boost::interprocess::interprocess_semaphore seems to spinlock on OSX; prefer polling instead +class CSemaphore +{ +private: + CCriticalSection cs; + int val; + +public: + CSemaphore(int init) : val(init) {} + + void wait() { + do { + { + LOCK(cs); + if (val>0) { + val--; + return; + } + } + Sleep(100); + } while(1); + } + + bool try_wait() { + LOCK(cs); + if (val>0) { + val--; + return true; + } + return false; + } + + void post() { + LOCK(cs); + val++; + } +}; +#else +typedef boost::interprocess::interprocess_semaphore CSemaphore; +#endif + +/** RAII-style semaphore lock */ +class CSemaphoreGrant +{ +private: + CSemaphore *sem; + bool fHaveGrant; + +public: + void Acquire() { + if (fHaveGrant) + return; + sem->wait(); + fHaveGrant = true; + } + + void Release() { + if (!fHaveGrant) + return; + sem->post(); + fHaveGrant = false; + } + + bool TryAcquire() { + if (!fHaveGrant && sem->try_wait()) + fHaveGrant = true; + return fHaveGrant; + } + + void MoveTo(CSemaphoreGrant &grant) { + grant.Release(); + grant.sem = sem; + grant.fHaveGrant = fHaveGrant; + sem = NULL; + fHaveGrant = false; + } + + CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {} + + CSemaphoreGrant(CSemaphore &sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { + if (fTry) + TryAcquire(); + else + Acquire(); + } + + ~CSemaphoreGrant() { + Release(); + } + + operator bool() { + return fHaveGrant; + } +}; +#endif + diff --git a/src/util.cpp b/src/util.cpp index 1a57cc6621..d2db7cec8f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -4,6 +4,7 @@ // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "util.h" +#include "sync.h" #include "strlcpy.h" #include "version.h" #include "ui_interface.h" @@ -23,8 +24,6 @@ namespace boost { #include <boost/program_options/parsers.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> -#include <boost/interprocess/sync/interprocess_mutex.hpp> -#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp> #include <boost/foreach.hpp> #include <openssl/crypto.h> #include <openssl/rand.h> @@ -71,13 +70,14 @@ bool fLogTimestamps = false; CMedianFilter<int64> vTimeOffsets(200,0); // Init openssl library multithreading support -static boost::interprocess::interprocess_mutex** ppmutexOpenSSL; +static CCriticalSection** ppmutexOpenSSL; void locking_callback(int mode, int i, const char* file, int line) { - if (mode & CRYPTO_LOCK) - ppmutexOpenSSL[i]->lock(); - else - ppmutexOpenSSL[i]->unlock(); + if (mode & CRYPTO_LOCK) { + ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]); + } else { + LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]); + } } // Init @@ -87,9 +87,9 @@ public: CInit() { // Init openssl library multithreading support - ppmutexOpenSSL = (boost::interprocess::interprocess_mutex**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(boost::interprocess::interprocess_mutex*)); + ppmutexOpenSSL = (CCriticalSection**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(CCriticalSection*)); for (int i = 0; i < CRYPTO_num_locks(); i++) - ppmutexOpenSSL[i] = new boost::interprocess::interprocess_mutex(); + ppmutexOpenSSL[i] = new CCriticalSection(); CRYPTO_set_locking_callback(locking_callback); #ifdef WIN32 @@ -1221,117 +1221,3 @@ bool GetStartOnSystemStartup() { return false; } bool SetStartOnSystemStartup(bool fAutoStart) { return false; } #endif - - - -#ifdef DEBUG_LOCKORDER -// -// Early deadlock detection. -// Problem being solved: -// Thread 1 locks A, then B, then C -// Thread 2 locks D, then C, then A -// --> may result in deadlock between the two threads, depending on when they run. -// Solution implemented here: -// Keep track of pairs of locks: (A before B), (A before C), etc. -// Complain if any thread trys to lock in a different order. -// - -struct CLockLocation -{ - CLockLocation(const char* pszName, const char* pszFile, int nLine) - { - mutexName = pszName; - sourceFile = pszFile; - sourceLine = nLine; - } - - std::string ToString() const - { - return mutexName+" "+sourceFile+":"+itostr(sourceLine); - } - -private: - std::string mutexName; - std::string sourceFile; - int sourceLine; -}; - -typedef std::vector< std::pair<void*, CLockLocation> > LockStack; - -static boost::interprocess::interprocess_mutex dd_mutex; -static std::map<std::pair<void*, void*>, LockStack> lockorders; -static boost::thread_specific_ptr<LockStack> lockstack; - - -static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2) -{ - printf("POTENTIAL DEADLOCK DETECTED\n"); - printf("Previous lock order was:\n"); - BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s2) - { - if (i.first == mismatch.first) printf(" (1)"); - if (i.first == mismatch.second) printf(" (2)"); - printf(" %s\n", i.second.ToString().c_str()); - } - printf("Current lock order is:\n"); - BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s1) - { - if (i.first == mismatch.first) printf(" (1)"); - if (i.first == mismatch.second) printf(" (2)"); - printf(" %s\n", i.second.ToString().c_str()); - } -} - -static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) -{ - bool fOrderOK = true; - if (lockstack.get() == NULL) - lockstack.reset(new LockStack); - - if (fDebug) printf("Locking: %s\n", locklocation.ToString().c_str()); - dd_mutex.lock(); - - (*lockstack).push_back(std::make_pair(c, locklocation)); - - if (!fTry) BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, (*lockstack)) - { - if (i.first == c) break; - - std::pair<void*, void*> p1 = std::make_pair(i.first, c); - if (lockorders.count(p1)) - continue; - lockorders[p1] = (*lockstack); - - std::pair<void*, void*> p2 = std::make_pair(c, i.first); - if (lockorders.count(p2)) - { - potential_deadlock_detected(p1, lockorders[p2], lockorders[p1]); - break; - } - } - dd_mutex.unlock(); -} - -static void pop_lock() -{ - if (fDebug) - { - const CLockLocation& locklocation = (*lockstack).rbegin()->second; - printf("Unlocked: %s\n", locklocation.ToString().c_str()); - } - dd_mutex.lock(); - (*lockstack).pop_back(); - dd_mutex.unlock(); -} - -void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) -{ - push_lock(cs, CLockLocation(pszName, pszFile, nLine), fTry); -} - -void LeaveCritical() -{ - pop_lock(); -} - -#endif /* DEBUG_LOCKORDER */ diff --git a/src/util.h b/src/util.h index ebd574f896..1363fbbbf9 100644 --- a/src/util.h +++ b/src/util.h @@ -21,10 +21,6 @@ typedef int pid_t; /* define for windows compatiblity */ #include <boost/thread.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/path.hpp> -#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp> -#include <boost/interprocess/sync/scoped_lock.hpp> -#include <boost/interprocess/sync/interprocess_condition.hpp> -#include <boost/interprocess/sync/lock_options.hpp> #include <boost/date_time/gregorian/gregorian_types.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> @@ -188,125 +184,6 @@ void AddTimeData(const CNetAddr& ip, int64 nTime); -/** Wrapped boost mutex: supports recursive locking, but no waiting */ -typedef boost::interprocess::interprocess_recursive_mutex CCriticalSection; - -/** Wrapped boost mutex: supports waiting but not recursive locking */ -typedef boost::interprocess::interprocess_mutex CWaitableCriticalSection; - -#ifdef DEBUG_LOCKORDER -void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); -void LeaveCritical(); -#else -void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} -void static inline LeaveCritical() {} -#endif - -/** Wrapper around boost::interprocess::scoped_lock */ -template<typename Mutex> -class CMutexLock -{ -private: - boost::interprocess::scoped_lock<Mutex> lock; -public: - - void Enter(const char* pszName, const char* pszFile, int nLine) - { - if (!lock.owns()) - { - EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); -#ifdef DEBUG_LOCKCONTENTION - if (!lock.try_lock()) - { - printf("LOCKCONTENTION: %s\n", pszName); - printf("Locker: %s:%d\n", pszFile, nLine); -#endif - lock.lock(); -#ifdef DEBUG_LOCKCONTENTION - } -#endif - } - } - - void Leave() - { - if (lock.owns()) - { - lock.unlock(); - LeaveCritical(); - } - } - - bool TryEnter(const char* pszName, const char* pszFile, int nLine) - { - if (!lock.owns()) - { - EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); - lock.try_lock(); - if (!lock.owns()) - LeaveCritical(); - } - return lock.owns(); - } - - CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) : lock(mutexIn, boost::interprocess::defer_lock) - { - if (fTry) - TryEnter(pszName, pszFile, nLine); - else - Enter(pszName, pszFile, nLine); - } - - ~CMutexLock() - { - if (lock.owns()) - LeaveCritical(); - } - - operator bool() - { - return lock.owns(); - } - - boost::interprocess::scoped_lock<Mutex> &GetLock() - { - return lock; - } -}; - -typedef CMutexLock<CCriticalSection> CCriticalBlock; -typedef CMutexLock<CWaitableCriticalSection> CWaitableCriticalBlock; -typedef boost::interprocess::interprocess_condition CConditionVariable; - -/** Wait for a given condition inside a WAITABLE_CRITICAL_BLOCK */ -#define WAIT(name,condition) \ - do { while(!(condition)) { (name).wait(waitablecriticalblock.GetLock()); } } while(0) - -/** Notify waiting threads that a condition may hold now */ -#define NOTIFY(name) \ - do { (name).notify_one(); } while(0) - -#define NOTIFY_ALL(name) \ - do { (name).notify_all(); } while(0) - -#define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__) -#define LOCK2(cs1,cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__),criticalblock2(cs2, #cs2, __FILE__, __LINE__) -#define TRY_LOCK(cs,name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) -#define WAITABLE_LOCK(cs) CWaitableCriticalBlock waitablecriticalblock(cs, #cs, __FILE__, __LINE__) - -#define ENTER_CRITICAL_SECTION(cs) \ - { \ - EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \ - (cs).lock(); \ - } - -#define LEAVE_CRITICAL_SECTION(cs) \ - { \ - (cs).unlock(); \ - LeaveCritical(); \ - } - - inline std::string i64tostr(int64 n) { return strprintf("%"PRI64d, n); |