diff --git a/src/net.h b/src/net.h --- a/src/net.h +++ b/src/net.h @@ -515,6 +515,8 @@ std::string addrLocal; // Address of this peer CAddress addr; + // Bind address of our side of the connection + CAddress addrBind; }; class CNetMessage { @@ -603,7 +605,10 @@ std::atomic nLastRecv; const int64_t nTimeConnected; std::atomic nTimeOffset; + // Address of this peer const CAddress addr; + // Bind address of our side of the connection + const CAddress addrBind; std::atomic nVersion; // strSubVer is whatever byte array we read from the wire. However, this // field is intended to be printed out, displayed to humans in various forms @@ -702,8 +707,8 @@ CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, - uint64_t nLocalHostNonceIn, const std::string &addrNameIn = "", - bool fInboundIn = false); + uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, + const std::string &addrNameIn = "", bool fInboundIn = false); ~CNode(); private: @@ -722,6 +727,7 @@ mutable CCriticalSection cs_addrName; std::string addrName; + // Our address, as reported by the peer CService addrLocal; mutable CCriticalSection cs_addrLocal; diff --git a/src/net.cpp b/src/net.cpp --- a/src/net.cpp +++ b/src/net.cpp @@ -326,6 +326,22 @@ return true; } +/** Get the bind address for a socket as CAddress */ +static CAddress GetBindAddress(SOCKET sock) { + CAddress addr_bind; + struct sockaddr_storage sockaddr_bind; + socklen_t sockaddr_bind_len = sizeof(sockaddr_bind); + if (sock != INVALID_SOCKET) { + if (!getsockname(sock, (struct sockaddr *)&sockaddr_bind, + &sockaddr_bind_len)) { + addr_bind.SetSockAddr((const struct sockaddr *)&sockaddr_bind); + } else { + LogPrint(BCLog::NET, "Warning: getsockname failed\n"); + } + } + return addr_bind; +} + CNode *CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) { if (pszDest == nullptr) { @@ -387,9 +403,10 @@ GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE) .Write(id) .Finalize(); + CAddress addr_bind = GetBindAddress(hSocket); CNode *pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, - CalculateKeyedNetGroup(addrConnect), nonce, + CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false); pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices); @@ -638,6 +655,7 @@ stats.nodeid = this->GetId(); stats.nServices = nServices; stats.addr = addr; + stats.addrBind = addrBind; { LOCK(cs_filter); stats.fRelayTxes = fRelayTxes; @@ -1192,9 +1210,11 @@ uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE) .Write(id) .Finalize(); + CAddress addr_bind = GetBindAddress(hSocket); - CNode *pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, - CalculateKeyedNetGroup(addr), nonce, "", true); + CNode *pnode = + new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, + CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true); pnode->AddRef(); pnode->fWhitelisted = whitelisted; @@ -2811,10 +2831,12 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, - const std::string &addrNameIn, bool fInboundIn) + const CAddress &addrBindIn, const std::string &addrNameIn, + bool fInboundIn) : nTimeConnected(GetSystemTimeInSeconds()), addr(addrIn), - fInbound(fInboundIn), nKeyedNetGroup(nKeyedNetGroupIn), - addrKnown(5000, 0.001), filterInventoryKnown(50000, 0.000001), id(idIn), + addrBind(addrBindIn), fInbound(fInboundIn), + nKeyedNetGroup(nKeyedNetGroupIn), addrKnown(5000, 0.001), + filterInventoryKnown(50000, 0.000001), id(idIn), nLocalHostNonce(nLocalHostNonceIn), nLocalServices(nLocalServicesIn), nMyStartingHeight(nMyStartingHeightIn), nSendVersion(0) { nServices = NODE_NONE; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -78,7 +78,10 @@ " \"id\": n, (numeric) Peer index\n" " \"addr\":\"host:port\", (string) The ip address and port " "of the peer\n" - " \"addrlocal\":\"ip:port\", (string) local address\n" + " \"addrbind\":\"ip:port\", (string) Bind address of the " + "connection to the peer\n" + " \"addrlocal\":\"ip:port\", (string) Local address as " + "reported by the peer\n" " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services " "offered\n" " \"relaytxes\":true|false, (boolean) Whether peer has asked " @@ -160,6 +163,9 @@ if (!(stats.addrLocal.empty())) { obj.push_back(Pair("addrlocal", stats.addrLocal)); } + if (stats.addrBind.IsValid()) { + obj.push_back(Pair("addrbind", stats.addrBind.ToString())); + } obj.push_back(Pair("services", strprintf("%016x", stats.nServices))); obj.push_back(Pair("relaytxes", stats.fRelayTxes)); obj.push_back(Pair("lastsend", stats.nLastSend)); diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -49,8 +49,8 @@ connman->ClearBanned(); CAddress addr1(ip(0xa0b0c001), NODE_NONE); - CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, "", - true); + CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, + CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); GetNodeSignals().InitializeNode(config, &dummyNode1, connman); dummyNode1.nVersion = 1; @@ -63,8 +63,8 @@ BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001 | 0x0000ff00))); CAddress addr2(ip(0xa0b0c002), NODE_NONE); - CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, "", - true); + CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, + CAddress(), "", true); dummyNode2.SetSendVersion(PROTOCOL_VERSION); GetNodeSignals().InitializeNode(config, &dummyNode2, connman); dummyNode2.nVersion = 1; @@ -88,8 +88,8 @@ // because 11 is my favorite number. gArgs.ForceSetArg("-banscore", "111"); CAddress addr1(ip(0xa0b0c001), NODE_NONE); - CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, "", - true); + CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, + CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); GetNodeSignals().InitializeNode(config, &dummyNode1, connman); dummyNode1.nVersion = 1; @@ -116,8 +116,8 @@ SetMockTime(nStartTime); CAddress addr(ip(0xa0b0c001), NODE_NONE); - CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, "", - true); + CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, + CAddress(), "", true); dummyNode.SetSendVersion(PROTOCOL_VERSION); GetNodeSignals().InitializeNode(config, &dummyNode, connman); dummyNode.nVersion = 1; diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -158,13 +158,15 @@ // Test that fFeeler is false by default. std::unique_ptr pnode1(new CNode(id++, NODE_NETWORK, height, hSocket, - addr, 0, 0, pszDest, fInboundIn)); + addr, 0, 0, CAddress(), pszDest, + fInboundIn)); BOOST_CHECK(pnode1->fInbound == false); BOOST_CHECK(pnode1->fFeeler == false); fInboundIn = true; std::unique_ptr pnode2(new CNode(id++, NODE_NETWORK, height, hSocket, - addr, 1, 1, pszDest, fInboundIn)); + addr, 1, 1, CAddress(), pszDest, + fInboundIn)); BOOST_CHECK(pnode2->fInbound == true); BOOST_CHECK(pnode2->fFeeler == false); } diff --git a/test/functional/net.py b/test/functional/net.py --- a/test/functional/net.py +++ b/test/functional/net.py @@ -28,6 +28,7 @@ self._test_getnettotals() self._test_getnetworkinginfo() self._test_getaddednodeinfo() + self._test_getpeerinfo() def _test_connection_count(self): # connect_nodes_bi connects each node to the other @@ -91,6 +92,13 @@ assert_raises_rpc_error(-24, "Node has not been added", self.nodes[0].getaddednodeinfo, '1.1.1.1') + def _test_getpeerinfo(self): + peer_info = [x.getpeerinfo() for x in self.nodes] + # check both sides of bidirectional connection between nodes + # the address bound to on one side will be the source address for the other node + assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr']) + assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr']) + if __name__ == '__main__': NetTest().main()