From 8f10a2889089af1b2ac64802360494b54c8c7ff1 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 11 May 2012 15:28:59 +0200 Subject: [PATCH] Separate listening sockets, -bind= --- src/init.cpp | 36 ++++++++++++++--- src/net.cpp | 86 +++++++++++++++++++++++----------------- src/net.h | 3 +- src/netbase.cpp | 103 ++++++++++++++++++++++++++---------------------- src/netbase.h | 4 +- 5 files changed, 140 insertions(+), 92 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 202d5136..877b1d47 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 @@ -193,6 +205,7 @@ bool AppInit2(int argc, char* argv[]) " -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= \t " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + #ifdef QT_GUI " -lang= \t\t " + _("Set language, for example \"de_DE\" (default: system locale)") + "\n" + #endif @@ -548,7 +561,11 @@ bool AppInit2(int argc, char* argv[]) 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) { @@ -588,14 +605,23 @@ bool AppInit2(int argc, char* argv[]) const char* pszP2SH = "/P2SH/"; COINBASE_FLAGS << std::vector(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/net.cpp b/src/net.cpp index a43b76d7..28667166 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -52,7 +52,7 @@ static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; uint64 nLocalHostNonce = 0; array vnThreadsRunning; -static SOCKET hListenSocket = INVALID_SOCKET; +static std::vector vhListenSocket; CAddrMan addrman; vector vNodes; @@ -719,9 +719,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) @@ -762,12 +763,13 @@ void ThreadSocketHandler2(void* parg) // // Accept new connections // + BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) { #ifdef USE_IPV6 - struct sockaddr_in6 sockaddr; + struct sockaddr_storage sockaddr; #else - struct sockaddr_in sockaddr; + struct sockaddr sockaddr; #endif socklen_t len = sizeof(sockaddr); SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); @@ -775,7 +777,8 @@ void ThreadSocketHandler2(void* parg) 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); @@ -1656,9 +1659,8 @@ void ThreadMessageHandler2(void* parg) -bool BindListenPort(string& strError) +bool BindListenPort(const CService &addrBind, string& strError) { - unsigned short nPort = GetListenPort(); strError = ""; int nOne = 1; @@ -1676,11 +1678,19 @@ bool BindListenPort(string& strError) // Create socket for listening for incoming connections #ifdef USE_IPV6 - int nFamily = AF_INET6; + struct sockaddr_storage sockaddr; #else - int nFamily = AF_INET; + struct sockaddr sockaddr; #endif - hListenSocket = socket(nFamily, SOCK_STREAM, IPPROTO_TCP); + 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()); @@ -1699,6 +1709,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) @@ -1711,38 +1722,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 #ifdef USE_IPV6 - struct sockaddr_in6 sockaddr = sockaddr_in6(); - memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sin6_family = AF_INET6; - sockaddr.sin6_addr = in6addr_any; // bind to all IPs on this computer - sockaddr.sin6_port = htons(nPort); -# 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 -#else - struct sockaddr_in sockaddr = sockaddr_in(); - 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(nPort); + // 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 - if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) +#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."), nPort); + 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, %s)", nPort, nErr, strerror(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", (int)nPort); + printf("Bound to %s\n", addrBind.ToString().c_str()); // Listen for incoming connections if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) @@ -1752,6 +1758,11 @@ bool BindListenPort(string& strError) return false; } + vhListenSocket.push_back(hListenSocket); + + if (addrBind.IsRoutable() && GetBoolArg("-discover", true)) + AddLocal(addrBind, LOCAL_BIND); + return true; } @@ -1915,9 +1926,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 diff --git a/src/net.h b/src/net.h index 398b89dc..d62f5127 100644 --- a/src/net.h +++ b/src/net.h @@ -38,7 +38,7 @@ 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(); @@ -46,6 +46,7 @@ enum { 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 diff --git a/src/netbase.cpp b/src/netbase.cpp index 7e9620d9..ebf823e8 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -183,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; @@ -319,37 +324,18 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe { hSocketRet = INVALID_SOCKET; - struct sockaddr_storage sockaddr; - int nFamily = 0; - size_t nSockAddrLen = 0; - - if (addrConnect.IsIPv4()) - { - // Use IPv4 stack to connect to IPv4 addresses - struct sockaddr_in sockaddr4; - if (!addrConnect.GetSockAddr(&sockaddr4)) - return false; - memcpy(&sockaddr, &sockaddr4, sizeof(sockaddr4)); - nSockAddrLen = sizeof(sockaddr4); - nFamily = AF_INET; - } #ifdef USE_IPV6 - else if (addrConnect.IsIPv6()) - { - struct sockaddr_in6 sockaddr6; - if (!addrConnect.GetSockAddr6(&sockaddr6)) - return false; - memcpy(&sockaddr, &sockaddr6, sizeof(sockaddr6)); - nSockAddrLen = sizeof(sockaddr6); - nFamily = AF_INET6; - } + struct sockaddr_storage sockaddr; +#else + struct sockaddr sockaddr; #endif - else { + 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(nFamily, SOCK_STREAM, IPPROTO_TCP); + SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); if (hSocket == INVALID_SOCKET) return false; #ifdef SO_NOSIGPIPE @@ -369,7 +355,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe return false; } - if (connect(hSocket, (struct sockaddr*)&sockaddr, nSockAddrLen) == 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) @@ -902,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(); @@ -954,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 CService::GetKey() const { diff --git a/src/netbase.h b/src/netbase.h index bd62c42e..514a1ae9 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -105,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); @@ -117,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