From 23aa78c405f82257e8578afb3d5d244aa27dcd74 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 31 Mar 2012 17:58:25 +0200 Subject: [PATCH 1/8] IPv6 node support This will make bitcoin relay valid routable IPv6 addresses, and when USE_IPV6 is enabled, listen on IPv6 interfaces and attempt connections to IPv6 addresses. --- bitcoin-qt.pro | 2 +- doc/build-osx.txt | 2 +- doc/build-unix.txt | 5 +++- src/main.cpp | 3 --- src/makefile.linux-mingw | 2 +- src/makefile.mingw | 2 +- src/makefile.osx | 2 +- src/makefile.unix | 2 +- src/net.cpp | 56 ++++++++++++++++++++++++++-------------- src/netbase.cpp | 54 ++++++++++++++++++++++++++++++-------- src/netbase.h | 1 + 11 files changed, 91 insertions(+), 40 deletions(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 112e8e938..c9bbc873b 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -2,7 +2,7 @@ TEMPLATE = app TARGET = VERSION = 0.6.99 INCLUDEPATH += src src/json src/qt -DEFINES += QT_GUI BOOST_THREAD_USE_LIB +DEFINES += QT_GUI BOOST_THREAD_USE_LIB USE_IPV6 CONFIG += no_include_pwd # for boost 1.37, add -mt to the boost libraries diff --git a/doc/build-osx.txt b/doc/build-osx.txt index 2ae77067f..256614f7a 100644 --- a/doc/build-osx.txt +++ b/doc/build-osx.txt @@ -44,7 +44,7 @@ sudo port install qrencode 4. Now you should be able to build bitcoind: cd bitcoin/src -make -f makefile.osx +make -f makefile.osx USE_IPV6=1 Run: ./bitcoind --help # for a list of command-line options. diff --git a/doc/build-unix.txt b/doc/build-unix.txt index c5b420508..eb5973681 100644 --- a/doc/build-unix.txt +++ b/doc/build-unix.txt @@ -43,6 +43,9 @@ your package manager. Set USE_QRCODE to control this: USE_QRCODE=0 (the default) No QRCode support - libarcode not required USE_QRCODE=1 QRCode support enabled +IPv6 support may be enabled by setting + USE_IPV6=1 Enable IPv6 support + Licenses of statically linked libraries: Berkeley DB New BSD license with additional requirement that linked software must be free open source @@ -80,7 +83,7 @@ emerge -av1 --noreplace boost glib openssl sys-libs/db:4.8 Take the following steps to build (no UPnP support): cd ${BITCOIN_DIR}/src - make -f makefile.unix USE_UPNP= BDB_INCLUDE_PATH='/usr/include/db4.8' + make -f makefile.unix USE_UPNP= USE_IPV6=1 BDB_INCLUDE_PATH='/usr/include/db4.8' strip bitcoind diff --git a/src/main.cpp b/src/main.cpp index b0ed28aa1..f9691a5d5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2424,9 +2424,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { 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); diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 645f0a16e..373b49ab0 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) diff --git a/src/makefile.mingw b/src/makefile.mingw index bb6466954..156eb17cc 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) diff --git a/src/makefile.osx b/src/makefile.osx index eb9ae4ba7..9c05d500a 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. diff --git a/src/makefile.unix b/src/makefile.unix index 53fb1f0b8..ce8b55cdf 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)) diff --git a/src/net.cpp b/src/net.cpp index 8603514f9..75c8bbaba 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -733,7 +733,11 @@ void ThreadSocketHandler2(void* parg) // if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) { +#ifdef USE_IPV6 + struct sockaddr_in6 sockaddr; +#else struct sockaddr_in sockaddr; +#endif socklen_t len = sizeof(sockaddr); SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); CAddress addr; @@ -1390,7 +1394,7 @@ 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++; @@ -1620,6 +1624,7 @@ void ThreadMessageHandler2(void* parg) bool BindListenPort(string& strError) { + unsigned short nPort = GetListenPort(); strError = ""; int nOne = 1; @@ -1636,7 +1641,12 @@ bool BindListenPort(string& strError) #endif // Create socket for listening for incoming connections - hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); +#ifdef USE_IPV6 + int nFamily = AF_INET6; +#else + int nFamily = AF_INET; +#endif + hListenSocket = socket(nFamily, SOCK_STREAM, IPPROTO_TCP); if (hListenSocket == INVALID_SOCKET) { strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); @@ -1669,22 +1679,36 @@ bool BindListenPort(string& strError) // The sockaddr_in structure specifies the address family, // IP address, and port for the socket that is being bound - struct sockaddr_in sockaddr; +#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(GetListenPort()); + sockaddr.sin_port = htons(nPort); +#endif if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == 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 port %d on this computer. Bitcoin is probably already running."), nPort); else - strError = strprintf("Error: Unable to bind to port %d on this computer (bind returned error %d)", ntohs(sockaddr.sin_port), nErr); + strError = strprintf("Error: Unable to bind to port %d on this computer (bind returned error %d, %s)", nPort, nErr, strerror(nErr)); printf("%s\n", strError.c_str()); return false; } - printf("Bound to port %d\n", ntohs(sockaddr.sin_port)); + printf("Bound to port %d\n", (int)nPort); // Listen for incoming connections if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) @@ -1727,28 +1751,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); } diff --git a/src/netbase.cpp b/src/netbase.cpp index 48709dc5c..a22d42a96 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -305,7 +305,37 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe { hSocketRet = INVALID_SOCKET; - SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + 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; + } +#endif + else { + printf("Cannot connect to %s: unsupported network\n", addrConnect.ToString().c_str()); + return false; + } + + SOCKET hSocket = socket(nFamily, 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, nSockAddrLen) == SOCKET_ERROR) { // WSAEINVAL is here because some legacy version of winsock uses it if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINVAL) @@ -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() && ( @@ -919,12 +947,16 @@ std::vector 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 e5c466e4f..acbcc36d1 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -31,6 +31,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) From d32148567f5866a7cd2a77a2f44f846134011c9c Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 2 Apr 2012 17:06:11 +0200 Subject: [PATCH 2/8] Preliminary support for Tor/I2P hidden services There are plans to let Bitcoin function as Tor/I2P hidden service. To do so, we could use the established encoding provided by OnionCat and GarliCat (without actually using those tools) to embed Tor/I2P addresses in IPv6. This patch makes these addresses considered routable, so they can travel over the Bitcoin network in 'addr' messages. This will hopefully make it easier to deploy real hidden service support later. --- src/netbase.cpp | 14 +++++++++++++- src/netbase.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/netbase.cpp b/src/netbase.cpp index a22d42a96..37e6120e7 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -615,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 @@ -673,7 +685,7 @@ 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()); } std::string CNetAddr::ToStringIP() const diff --git a/src/netbase.h b/src/netbase.h index acbcc36d1..1b6d8d59b 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -42,6 +42,8 @@ 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; From 090e5b40f1b3ac9ac6209f8996da4d686686a2ac Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 10 Apr 2012 20:22:04 +0200 Subject: [PATCH 3/8] Limited relaying/storing of foreign addresses Introduce a boolean variable for each "network" (ipv4, ipv6, tor, i2p), and track whether we are likely to able to connect to it. Addresses in "addr" messages outside of our network get limited relaying and are not stored in addrman. --- src/main.cpp | 9 +++++++-- src/net.cpp | 14 ++++++++++++-- src/net.h | 2 ++ src/netbase.cpp | 25 +++++++++++++++++++++++++ src/netbase.h | 14 ++++++++++++++ 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f9691a5d5..5f16378b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2418,6 +2418,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } // Store the new addresses + vector vAddrOk; int64 nNow = GetAdjustedTime(); int64 nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) @@ -2427,6 +2428,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) 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 @@ -2451,13 +2453,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::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/net.cpp b/src/net.cpp index 75c8bbaba..d407e6642 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -45,8 +45,9 @@ 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 mapLocalHost; +static CCriticalSection cs_mapLocalHost; +static map mapLocalHost; +static bool vfReachable[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; uint64 nLocalHostNonce = 0; array vnThreadsRunning; @@ -214,6 +215,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(); @@ -243,6 +247,12 @@ bool IsLocal(const CNetAddr& addr) 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); + return vfReachable[addr.GetNetwork()]; +} bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet) { diff --git a/src/net.h b/src/net.h index a00dd1b8c..63f871204 100644 --- a/src/net.h +++ b/src/net.h @@ -58,8 +58,10 @@ 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); +bool IsReachable(const CNetAddr &addr); CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); + enum { MSG_TX = 1, diff --git a/src/netbase.cpp b/src/netbase.cpp index 37e6120e7..4c852f5ee 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -25,6 +25,14 @@ int nConnectTimeout = 5000; 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; +} + bool static LookupIntern(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions, bool fAllowLookup) { vIP.clear(); @@ -688,6 +696,23 @@ bool CNetAddr::IsRoutable() const 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 { if (IsIPv4()) diff --git a/src/netbase.h b/src/netbase.h index 1b6d8d59b..998f8eaf1 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -17,6 +17,19 @@ 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); + /** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */ class CNetAddr { @@ -48,6 +61,7 @@ class CNetAddr 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; From 623b987813acfc985ecca591e96ac0b84f5333e3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 4 May 2012 16:55:19 +0200 Subject: [PATCH 4/8] Add -noproxy to circumvent proxy for some network --- src/init.cpp | 13 +++++++++++++ src/netbase.cpp | 8 +++++++- src/netbase.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 60927f20b..03b47b3ef 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -180,6 +180,7 @@ bool AppInit2(int argc, char* argv[]) " -timeout= \t " + _("Specify connection timeout (in milliseconds)") + "\n" + " -proxy= \t " + _("Connect through socks proxy") + "\n" + " -socks= \t " + _("Select the version of socks proxy to use (4 or 5, 5 is default)") + "\n" + + " -noproxy= \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= \t\t " + _("Listen for connections on (default: 8333 or testnet: 18333)") + "\n" + @@ -532,6 +533,18 @@ 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); diff --git a/src/netbase.cpp b/src/netbase.cpp index 4c852f5ee..2c821c7ac 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -21,6 +21,7 @@ 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 }; @@ -33,6 +34,11 @@ enum Network ParseNetwork(std::string net) { 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& vIP, unsigned int nMaxSolutions, bool fAllowLookup) { vIP.clear(); @@ -440,7 +446,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; diff --git a/src/netbase.h b/src/netbase.h index 998f8eaf1..bd62c42e3 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -29,6 +29,7 @@ enum Network }; 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 From c5b3ffd8d5d61b0adb69882efd72f2d4e89f62d6 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 29 Apr 2012 02:19:23 +0200 Subject: [PATCH 5/8] Use NET_ identifiers in CNetAddr::GetGroup() --- src/netbase.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/netbase.cpp b/src/netbase.cpp index 2c821c7ac..7e9620d9f 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -772,40 +772,40 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const std::vector CNetAddr::GetGroup() const { std::vector 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; From 457754d2c24f7e53c55f4b68155a5fa702552327 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 4 May 2012 16:46:22 +0200 Subject: [PATCH 6/8] Add -blocknet to prevent connections to a given network --- src/init.cpp | 12 ++++++++++++ src/net.cpp | 26 ++++++++++++++++++++++---- src/net.h | 2 ++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 03b47b3ef..202d51367 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -189,6 +189,7 @@ bool AppInit2(int argc, char* argv[]) " -connect= \t\t " + _("Connect only to the specified node") + "\n" + " -seednode= \t\t " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" + " -externalip= \t " + _("Specify your own public address") + "\n" + + " -blocknet= \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" + @@ -560,6 +561,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) diff --git a/src/net.cpp b/src/net.cpp index d407e6642..79d0a8ddb 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -48,6 +48,7 @@ uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); static CCriticalSection cs_mapLocalHost; static map mapLocalHost; static bool vfReachable[NET_MAX] = {}; +static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; uint64 nLocalHostNonce = 0; array vnThreadsRunning; @@ -225,7 +226,20 @@ bool AddLocal(const CNetAddr& addr, int nScore) return true; } -// vote for a local address +/** 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 CNetAddr& addr) { { @@ -240,18 +254,19 @@ bool SeenLocal(const CNetAddr& addr) return true; } -// check whether a given address is potentially local +/** check whether a given address is potentially local */ bool IsLocal(const CNetAddr& addr) { LOCK(cs_mapLocalHost); return mapLocalHost.count(addr) > 0; } -// check whether a given address is in a network we can probably connect to +/** check whether a given address is in a network we can probably connect to */ bool IsReachable(const CNetAddr& addr) { LOCK(cs_mapLocalHost); - return vfReachable[addr.GetNetwork()]; + enum Network net = addr.GetNetwork(); + return vfReachable[net] && !vfLimited[net]; } bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet) @@ -1409,6 +1424,9 @@ void ThreadOpenConnections2(void* parg) nTries++; + if (IsLimited(addr)) + continue; + // only consider very recently tried nodes after 30 failed attempts if (nANow - addr.nLastTry < 600 && nTries < 30) continue; diff --git a/src/net.h b/src/net.h index 63f871204..be167b001 100644 --- a/src/net.h +++ b/src/net.h @@ -54,6 +54,8 @@ enum LOCAL_MAX }; +void SetLimited(enum Network net, bool fLimited = true); +bool IsLimited(const CNetAddr& addr); bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); bool SeenLocal(const CNetAddr& addr); bool IsLocal(const CNetAddr& addr); From 7fa4443f77a659031e277337770b506fcf954d69 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 10 May 2012 20:35:13 +0200 Subject: [PATCH 7/8] Keep port information for local addresses --- src/irc.cpp | 2 +- src/net.cpp | 26 ++++++++++++++++---------- src/net.h | 23 ++++++++++++----------- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/irc.cpp b/src/irc.cpp index f20152495..525bd7a8d 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/net.cpp b/src/net.cpp index 79d0a8ddb..a43b76d79 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -46,7 +46,7 @@ bool fClient = false; static bool fUseUPnP = false; uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); static CCriticalSection cs_mapLocalHost; -static map mapLocalHost; +static map mapLocalHost; static bool vfReachable[NET_MAX] = {}; static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; @@ -96,7 +96,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; @@ -105,7 +105,7 @@ bool GetLocal(CNetAddr& addr, const CNetAddr *paddrPeer) int nBestReachability = -1; { LOCK(cs_mapLocalHost); - for (map::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) + for (map::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) { int nCount = (*it).second; int nReachability = (*it).first.GetReachabilityFrom(paddrPeer); @@ -124,11 +124,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(); } @@ -196,7 +195,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; @@ -206,7 +205,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; @@ -226,6 +225,13 @@ bool AddLocal(const CNetAddr& addr, int nScore) return true; } +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) { @@ -240,7 +246,7 @@ bool IsLimited(const CNetAddr& addr) } /** vote for a local address */ -bool SeenLocal(const CNetAddr& addr) +bool SeenLocal(const CService& addr) { { LOCK(cs_mapLocalHost); @@ -255,7 +261,7 @@ bool SeenLocal(const CNetAddr& addr) } /** check whether a given address is potentially local */ -bool IsLocal(const CNetAddr& addr) +bool IsLocal(const CService& addr) { LOCK(cs_mapLocalHost); return mapLocalHost.count(addr) > 0; diff --git a/src/net.h b/src/net.h index be167b001..398b89dcf 100644 --- a/src/net.h +++ b/src/net.h @@ -44,22 +44,23 @@ 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_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 }; void SetLimited(enum Network net, bool fLimited = true); bool IsLimited(const CNetAddr& addr); -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); +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); @@ -142,7 +143,7 @@ public: unsigned int nMessageStart; CAddress addr; std::string addrName; - CNetAddr addrLocal; + CService addrLocal; int nVersion; std::string strSubVer; bool fOneShot; From 8f10a2889089af1b2ac64802360494b54c8c7ff1 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 11 May 2012 15:28:59 +0200 Subject: [PATCH 8/8] 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 202d51367..877b1d471 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 a43b76d79..28667166e 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 398b89dcf..d62f51271 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 7e9620d9f..ebf823e89 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 bd62c42e3..514a1ae95 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