diff --git a/globalcoin-qt.pro b/globalcoin-qt.pro index 6109f4a..f1751d2 100644 --- a/globalcoin-qt.pro +++ b/globalcoin-qt.pro @@ -37,9 +37,21 @@ contains(RELEASE, 1) { } } -# qmake "USE_UPNP=-" (not supported) +# use: qmake "USE_UPNP=1" ( enabled by default; default) +# or: qmake "USE_UPNP=0" (disabled by default) +# or: qmake "USE_UPNP=-" (not supported) +# miniupnpc (http://miniupnp.free.fr/files/) must be installed for support contains(USE_UPNP, -) { message(Building without UPNP support) +} else { + message(Building with UPNP support) + count(USE_UPNP, 0) { + USE_UPNP=1 + } + DEFINES += USE_UPNP=$$USE_UPNP STATICLIB + INCLUDEPATH += $$MINIUPNPC_INCLUDE_PATH + LIBS += $$join(MINIUPNPC_LIB_PATH,,-L,) -lminiupnpc + win32:LIBS += -liphlpapi } # use: qmake "USE_QRCODE=1" diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 8d29131..a5c699d 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -2831,7 +2831,11 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + " -maxreceivebuffer= " + _("Maximum per-connection receive buffer, *1000 bytes (default: 5000)") + "\n" + " -maxsendbuffer= " + _("Maximum per-connection send buffer, *1000 bytes (default: 1000)") + "\n" + +#ifdef USE_UPNP +#if USE_UPNP + " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n" + +#else + " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n" + +#endif +#endif " -detachdb " + _("Detach block and address databases. Increases shutdown time (default: 0)") + "\n" + " -paytxfee= " + _("Fee per KB to add to transactions you send") + "\n" + @@ -526,7 +533,9 @@ bool AppInit2() fNoListen = !GetBoolArg("-listen", true); fDiscover = GetBoolArg("-discover", true); fNameLookup = GetBoolArg("-dns", true); - +#ifdef USE_UPNP + fUseUPnP = GetBoolArg("-upnp", USE_UPNP); +#endif bool fBound = false; if (!fNoListen) @@ -765,11 +774,11 @@ bool AppInit2() printf("mapWallet.size() = %d\n", pwalletMain->mapWallet.size()); printf("mapAddressBook.size() = %d\n", pwalletMain->mapAddressBook.size()); - if (!CreateThread(StartNode, NULL)) + if (!NewThread(StartNode, NULL)) InitError(_("Error: could not start node")); if (fServer) - CreateThread(ThreadRPCServer, NULL); + NewThread(ThreadRPCServer, NULL); // ********************************************************* Step 11: finished diff --git a/src/net.cpp b/src/net.cpp index bfbb090..de6e8cd 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -16,7 +16,12 @@ #ifdef WIN32 #include #endif - +#ifdef USE_UPNP +#include +#include +#include +#include +#endif using namespace std; using namespace boost; @@ -27,7 +32,9 @@ void ThreadMessageHandler2(void* parg); void ThreadSocketHandler2(void* parg); void ThreadOpenConnections2(void* parg); void ThreadOpenAddedConnections2(void* parg); - +#ifdef USE_UPNP +void ThreadMapPort2(void* parg); +#endif void ThreadDNSAddressSeed2(void* parg); bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); @@ -42,7 +49,7 @@ struct LocalServiceInfo { // bool fClient = false; bool fDiscover = true; - +bool fUseUPnP = false; uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); static CCriticalSection cs_mapLocalHost; static map mapLocalHost; @@ -978,6 +985,147 @@ void ThreadSocketHandler2(void* parg) } +#ifdef USE_UPNP +void ThreadMapPort(void* parg) +{ + // Make this thread recognisable as the UPnP thread + RenameThread("PayCon-UPnP"); + + try + { + vnThreadsRunning[THREAD_UPNP]++; + ThreadMapPort2(parg); + vnThreadsRunning[THREAD_UPNP]--; + } + catch (std::exception& e) { + vnThreadsRunning[THREAD_UPNP]--; + PrintException(&e, "ThreadMapPort()"); + } catch (...) { + vnThreadsRunning[THREAD_UPNP]--; + PrintException(NULL, "ThreadMapPort()"); + } + printf("ThreadMapPort exited\n"); +} + +void ThreadMapPort2(void* parg) +{ + printf("ThreadMapPort started\n"); + + std::string port = strprintf("%u", GetListenPort()); + const char * multicastif = 0; + const char * minissdpdpath = 0; + struct UPNPDev * devlist = 0; + char lanaddr[64]; + +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0); +#else + /* miniupnpc 1.6 */ + int error = 0; + devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); +#endif + + struct UPNPUrls urls; + struct IGDdatas data; + int r; + + r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); + if (r == 1) + { + if (fDiscover) { + char externalIPAddress[40]; + r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); + if(r != UPNPCOMMAND_SUCCESS) + printf("UPnP: GetExternalIPAddress() returned %d\n", r); + else + { + if(externalIPAddress[0]) + { + printf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); + AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP); + } + else + printf("UPnP: GetExternalIPAddress failed.\n"); + } + } + + string strDesc = "PayCon " + FormatFullVersion(); +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); +#else + /* miniupnpc 1.6 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); +#endif + + if(r!=UPNPCOMMAND_SUCCESS) + printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + port.c_str(), port.c_str(), lanaddr, r, strupnperror(r)); + else + printf("UPnP Port Mapping successful.\n"); + int i = 1; + while (true) + { + if (fShutdown || !fUseUPnP) + { + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + printf("UPNP_DeletePortMapping() returned : %d\n", r); + freeUPNPDevlist(devlist); devlist = 0; + FreeUPNPUrls(&urls); + return; + } + if (i % 600 == 0) // Refresh every 20 minutes + { +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); +#else + /* miniupnpc 1.6 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); +#endif + + if(r!=UPNPCOMMAND_SUCCESS) + printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + port.c_str(), port.c_str(), lanaddr, r, strupnperror(r)); + else + printf("UPnP Port Mapping successful.\n");; + } + Sleep(2000); + i++; + } + } else { + printf("No valid UPnP IGDs found\n"); + freeUPNPDevlist(devlist); devlist = 0; + if (r != 0) + FreeUPNPUrls(&urls); + while (true) + { + if (fShutdown || !fUseUPnP) + return; + Sleep(2000); + } + } +} + +void MapPort() +{ + if (fUseUPnP && vnThreadsRunning[THREAD_UPNP] < 1) + { + if (!NewThread(ThreadMapPort, NULL)) + printf("Error: ThreadMapPort(ThreadMapPort) failed\n"); + } +} +#else +void MapPort() +{ + // Intentionally left blank. +} +#endif @@ -1650,7 +1798,7 @@ void static Discover() } #endif - CreateThread(ThreadGetMyExternalIP, NULL); + NewThread(ThreadGetMyExternalIP, NULL); } void StartNode(void* parg) @@ -1676,32 +1824,34 @@ void StartNode(void* parg) if (!GetBoolArg("-dnsseed", true)) printf("DNS seeding disabled\n"); else - if (!CreateThread(ThreadDNSAddressSeed, NULL)) - printf("Error: CreateThread(ThreadDNSAddressSeed) failed\n"); - + if (!NewThread(ThreadDNSAddressSeed, NULL)) + printf("Error: NewThread(ThreadDNSAddressSeed) failed\n"); + // Map ports with UPnP + if (fUseUPnP) + MapPort(); // Get addresses from IRC and advertise ours - if (!CreateThread(ThreadIRCSeed, NULL)) - printf("Error: CreateThread(ThreadIRCSeed) failed\n"); + if (!NewThread(ThreadIRCSeed, NULL)) + printf("Error: NewThread(ThreadIRCSeed) failed\n"); // Send and receive from sockets, accept connections - if (!CreateThread(ThreadSocketHandler, NULL)) - printf("Error: CreateThread(ThreadSocketHandler) failed\n"); + if (!NewThread(ThreadSocketHandler, NULL)) + printf("Error: NewThread(ThreadSocketHandler) failed\n"); // Initiate outbound connections from -addnode - if (!CreateThread(ThreadOpenAddedConnections, NULL)) - printf("Error: CreateThread(ThreadOpenAddedConnections) failed\n"); + if (!NewThread(ThreadOpenAddedConnections, NULL)) + printf("Error: NewThread(ThreadOpenAddedConnections) failed\n"); // Initiate outbound connections - if (!CreateThread(ThreadOpenConnections, NULL)) - printf("Error: CreateThread(ThreadOpenConnections) failed\n"); + if (!NewThread(ThreadOpenConnections, NULL)) + printf("Error: NewThread(ThreadOpenConnections) failed\n"); // Process messages - if (!CreateThread(ThreadMessageHandler, NULL)) - printf("Error: CreateThread(ThreadMessageHandler) failed\n"); + if (!NewThread(ThreadMessageHandler, NULL)) + printf("Error: NewThread(ThreadMessageHandler) failed\n"); // Dump network addresses - if (!CreateThread(ThreadDumpAddress, NULL)) + if (!NewThread(ThreadDumpAddress, NULL)) printf("Error; CreateThread(ThreadDumpAddress) failed\n"); // Generate coins in the background @@ -1734,7 +1884,9 @@ bool StopNode() if (vnThreadsRunning[THREAD_MINER] > 0) printf("ThreadBitcoinMiner still running\n"); if (vnThreadsRunning[THREAD_RPCLISTENER] > 0) printf("ThreadRPCListener still running\n"); if (vnThreadsRunning[THREAD_RPCHANDLER] > 0) printf("ThreadsRPCServer still running\n"); - +#ifdef USE_UPNP + if (vnThreadsRunning[THREAD_UPNP] > 0) printf("ThreadMapPort still running\n"); +#endif if (vnThreadsRunning[THREAD_DNSSEED] > 0) printf("ThreadDNSAddressSeed still running\n"); if (vnThreadsRunning[THREAD_ADDEDCONNECTIONS] > 0) printf("ThreadOpenAddedConnections still running\n"); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index ae4c1dc..7fb9409 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -50,6 +50,8 @@ void OptionsModel::Init() // These are shared with core Bitcoin; we want // command-line options to override the GUI settings: + if (settings.contains("fUseUPnP")) + SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()); if (settings.contains("addrProxy") && settings.value("fUseProxy").toBool()) SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()); if (settings.contains("nSocksVersion") && settings.value("fUseProxy").toBool()) @@ -84,7 +86,7 @@ bool OptionsModel::Upgrade() } } QList boolOptions; - boolOptions << "bDisplayAddresses" << "fMinimizeToTray" << "fMinimizeOnClose" << "fUseProxy" ; +boolOptions << "bDisplayAddresses" << "fMinimizeToTray" << "fMinimizeOnClose" << "fUseProxy" << "fUseUPnP"; foreach(QString key, boolOptions) { bool value = false; @@ -136,6 +138,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return QVariant(GUIUtil::GetStartOnSystemStartup()); case MinimizeToTray: return QVariant(fMinimizeToTray); + case MapPortUPnP: + return settings.value("fUseUPnP", GetBoolArg("-upnp", true)); case MinimizeOnClose: return QVariant(fMinimizeOnClose); case ProxyUse: @@ -187,6 +191,11 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in fMinimizeToTray = value.toBool(); settings.setValue("fMinimizeToTray", fMinimizeToTray); break; + case MapPortUPnP: + fUseUPnP = value.toBool(); + settings.setValue("fUseUPnP", fUseUPnP); + MapPort(); + break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); settings.setValue("fMinimizeOnClose", fMinimizeOnClose); diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp index d4e0c34..97e1eb8 100644 --- a/src/qt/qtipcserver.cpp +++ b/src/qt/qtipcserver.cpp @@ -108,7 +108,7 @@ void ipcInit() return; } - if (!CreateThread(ipcThread, mq)) + if (!NewThread(ipcThread, mq)) { delete mq; return; diff --git a/src/util.cpp b/src/util.cpp index 3d9c385..b21f012 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1296,3 +1296,14 @@ void RenameThread(const char* name) (void)name; #endif } +bool NewThread(void(*pfn)(void*), void* parg) +{ + try + { + boost::thread(pfn, parg); // thread detaches when out of scope + } catch(boost::thread_resource_error &e) { + printf("Error creating thread: %s\n", e.what()); + return false; + } + return true; +} \ No newline at end of file diff --git a/src/util.h b/src/util.h index 276012c..3571654 100644 --- a/src/util.h +++ b/src/util.h @@ -544,7 +544,7 @@ template class CMedianFilter - +bool NewThread(void(*pfn)(void*), void* parg); diff --git a/src/wallet.cpp b/src/wallet.cpp index c19b18e..b5e1476 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1328,7 +1328,7 @@ int CWallet::LoadWallet(bool& fFirstRunRet) return nLoadWalletRet; fFirstRunRet = !vchDefaultKey.IsValid(); - CreateThread(ThreadFlushWalletDB, &strWalletFile); + NewThread(ThreadFlushWalletDB, &strWalletFile); return DB_LOAD_OK; }