diff --git a/src/main/CommandHandler.cpp b/src/main/CommandHandler.cpp index edf3c3f482..d9dac61833 100644 --- a/src/main/CommandHandler.cpp +++ b/src/main/CommandHandler.cpp @@ -449,9 +449,7 @@ CommandHandler::peers(std::string const&, std::string& retStr) int counter = 0; for (auto peer : mApp.getOverlayManager().getPendingPeers()) { - root["pending_peers"][counter]["ip"] = peer->getIP(); - root["pending_peers"][counter]["port"] = - (int)peer->getRemoteListeningPort(); + root["pending_peers"][counter]["address"] = peer->toString(); counter++; } @@ -460,9 +458,8 @@ CommandHandler::peers(std::string const&, std::string& retStr) counter = 0; for (auto peer : mApp.getOverlayManager().getAuthenticatedPeers()) { - root["authenticated_peers"][counter]["ip"] = peer.second->getIP(); - root["authenticated_peers"][counter]["port"] = - (int)peer.second->getRemoteListeningPort(); + root["authenticated_peers"][counter]["address"] = + peer.second->toString(); root["authenticated_peers"][counter]["ver"] = peer.second->getRemoteVersion(); root["authenticated_peers"][counter]["olver"] = diff --git a/src/overlay/LoopbackPeer.cpp b/src/overlay/LoopbackPeer.cpp index 85e3518ac7..509070edea 100644 --- a/src/overlay/LoopbackPeer.cpp +++ b/src/overlay/LoopbackPeer.cpp @@ -27,6 +27,19 @@ LoopbackPeer::LoopbackPeer(Application& app, PeerRole role) : Peer(app, role) { } +PeerBareAddress +LoopbackPeer::makeAddress(unsigned short remoteListeningPort) const +{ + if (remoteListeningPort <= 0 || remoteListeningPort > UINT16_MAX) + { + return PeerBareAddress{}; + } + else + { + return PeerBareAddress{"127.0.0.1", remoteListeningPort}; + } +} + AuthCert LoopbackPeer::getAuthCert() { @@ -63,12 +76,6 @@ LoopbackPeer::sendMessage(xdr::msg_ptr&& msg) } } -std::string -LoopbackPeer::getIP() -{ - return "127.0.0.1"; -} - void LoopbackPeer::drop(bool) { diff --git a/src/overlay/LoopbackPeer.h b/src/overlay/LoopbackPeer.h index 2496c18a8c..4407d3ce47 100644 --- a/src/overlay/LoopbackPeer.h +++ b/src/overlay/LoopbackPeer.h @@ -53,6 +53,8 @@ class LoopbackPeer : public Peer Stats mStats; void sendMessage(xdr::msg_ptr&& xdrBytes) override; + PeerBareAddress + makeAddress(unsigned short remoteListeningPort) const override; AuthCert getAuthCert() override; void processInQueue(); @@ -63,7 +65,6 @@ class LoopbackPeer : public Peer } LoopbackPeer(Application& app, PeerRole role); void drop(bool force = true) override; - std::string getIP() override; void deliverOne(); void deliverAll(); diff --git a/src/overlay/OverlayManager.h b/src/overlay/OverlayManager.h index 50126845d4..fd36e51389 100644 --- a/src/overlay/OverlayManager.h +++ b/src/overlay/OverlayManager.h @@ -46,8 +46,9 @@ namespace stellar { -class PeerRecord; class PeerAuth; +class PeerBareAddress; +class PeerRecord; class LoadManager; class OverlayManager @@ -78,10 +79,9 @@ class OverlayManager // Return a list of random peers from the set of authenticated peers. virtual std::vector getRandomAuthenticatedPeers() = 0; - // Return an already-connected peer at the given ip address and port; - // returns a `nullptr`-valued pointer if no such connected peer exists. - virtual Peer::pointer getConnectedPeer(std::string const& ip, - unsigned short port) = 0; + // Return an already-connected peer at the given address; returns a + // `nullptr`-valued pointer if no such connected peer exists. + virtual Peer::pointer getConnectedPeer(PeerBareAddress const& address) = 0; // Add a peer to the in-memory set of pending peers. virtual void addPendingPeer(Peer::pointer peer) = 0; @@ -118,7 +118,11 @@ class OverlayManager // a TCP port number. virtual void connectTo(std::string const& addr) = 0; - // Attempt to connect to a peer identified by peer record. + // Attempt to connect to a peer identified by peer address. + virtual void connectTo(PeerBareAddress const& address) = 0; + + // Attempt to connect to a peer identified by peer record. Can modify back + // off value of pr and save it do database. virtual void connectTo(PeerRecord& pr) = 0; // returns the list of peers that sent us the item with hash `h` diff --git a/src/overlay/OverlayManagerImpl.cpp b/src/overlay/OverlayManagerImpl.cpp index e1384e32b2..fe30d8fd26 100644 --- a/src/overlay/OverlayManagerImpl.cpp +++ b/src/overlay/OverlayManagerImpl.cpp @@ -8,6 +8,7 @@ #include "database/Database.h" #include "main/Application.h" #include "main/Config.h" +#include "overlay/PeerBareAddress.h" #include "overlay/PeerRecord.h" #include "overlay/TCPPeer.h" #include "util/Logging.h" @@ -109,8 +110,8 @@ OverlayManagerImpl::connectTo(std::string const& peerStr) { try { - auto pr = PeerRecord::parseIPPort(peerStr, mApp); - connectTo(pr); + auto address = PeerBareAddress::resolve(peerStr, mApp); + connectTo(address); } catch (const std::runtime_error&) { @@ -118,16 +119,23 @@ OverlayManagerImpl::connectTo(std::string const& peerStr) } } +void +OverlayManagerImpl::connectTo(PeerBareAddress const& address) +{ + auto pr = PeerRecord{address, mApp.getClock().now(), 0}; + connectTo(pr); +} + void OverlayManagerImpl::connectTo(PeerRecord& pr) { mConnectionsAttempted.Mark(); - if (!getConnectedPeer(pr.ip(), pr.port())) + if (!getConnectedPeer(pr.getAddress())) { pr.backOff(mApp.getClock()); pr.storePeerRecord(mApp.getDatabase()); - addPendingPeer(TCPPeer::initiate(mApp, pr.ip(), pr.port())); + addPendingPeer(TCPPeer::initiate(mApp, pr.getAddress())); } else { @@ -145,7 +153,8 @@ OverlayManagerImpl::storePeerList(std::vector const& list, { try { - auto pr = PeerRecord::parseIPPort(peerStr, mApp); + auto address = PeerBareAddress::resolve(peerStr, mApp); + auto pr = PeerRecord{address, mApp.getClock().now(), 0}; pr.setPreferred(preferred); if (resetBackOff) { @@ -173,7 +182,7 @@ OverlayManagerImpl::storeConfigPeers() { try { - auto pr = PeerRecord::parseIPPort(s, mApp); + auto pr = PeerBareAddress::resolve(s, mApp); auto r = mPreferredPeers.insert(pr.toString()); if (r.second) { @@ -197,11 +206,10 @@ OverlayManagerImpl::getPreferredPeersFromConfig() std::vector peers; for (auto& pp : mPreferredPeers) { - auto prParsed = PeerRecord::parseIPPort(pp, mApp); - if (!getConnectedPeer(prParsed.ip(), prParsed.port())) + auto address = PeerBareAddress::resolve(pp, mApp); + if (!getConnectedPeer(address)) { - auto pr = PeerRecord::loadPeerRecord( - mApp.getDatabase(), prParsed.ip(), prParsed.port()); + auto pr = PeerRecord::loadPeerRecord(mApp.getDatabase(), address); if (pr && pr->mNextAttempt <= mApp.getClock().now()) { peers.emplace_back(*pr); @@ -223,7 +231,7 @@ OverlayManagerImpl::getPeersToConnectTo(int maxNum) [&](PeerRecord const& pr) { // skip peers that we're already // connected/connecting to - if (!getConnectedPeer(pr.ip(), pr.port())) + if (!getConnectedPeer(pr.getAddress())) { peers.emplace_back(pr); } @@ -293,13 +301,12 @@ OverlayManagerImpl::tick() } Peer::pointer -OverlayManagerImpl::getConnectedPeer(std::string const& ip, unsigned short port) +OverlayManagerImpl::getConnectedPeer(PeerBareAddress const& address) { auto pendingPeerIt = std::find_if(std::begin(mPendingPeers), std::end(mPendingPeers), - [ip, port](Peer::pointer const& peer) { - return peer->getIP() == ip && - peer->getRemoteListeningPort() == port; + [address](Peer::pointer const& peer) { + return peer->getAddress() == address; }); if (pendingPeerIt != std::end(mPendingPeers)) { @@ -308,9 +315,8 @@ OverlayManagerImpl::getConnectedPeer(std::string const& ip, unsigned short port) auto authenticatedPeerIt = std::find_if( std::begin(mAuthenticatedPeers), std::end(mAuthenticatedPeers), - [ip, port](std::pair const& peer) { - return peer.second->getIP() == ip && - peer.second->getRemoteListeningPort() == port; + [address](std::pair const& peer) { + return peer.second->getAddress() == address; }); if (authenticatedPeerIt != std::end(mAuthenticatedPeers)) { diff --git a/src/overlay/OverlayManagerImpl.h b/src/overlay/OverlayManagerImpl.h index 787188b413..1220ea9d97 100644 --- a/src/overlay/OverlayManagerImpl.h +++ b/src/overlay/OverlayManagerImpl.h @@ -74,7 +74,8 @@ class OverlayManagerImpl : public OverlayManager void broadcastMessage(StellarMessage const& msg, bool force = false) override; void connectTo(std::string const& addr) override; - virtual void connectTo(PeerRecord& pr) override; + void connectTo(PeerRecord& pr) override; + void connectTo(PeerBareAddress const& address) override; void addPendingPeer(Peer::pointer peer) override; void dropPeer(Peer* peer) override; @@ -86,9 +87,8 @@ class OverlayManagerImpl : public OverlayManager getAuthenticatedPeers() const override; int getAuthenticatedPeersCount() const override; - // returns NULL if the passed peer isn't found - Peer::pointer getConnectedPeer(std::string const& ip, - unsigned short port) override; + // returns nullptr if the passed peer isn't found + Peer::pointer getConnectedPeer(PeerBareAddress const& address) override; void connectToMorePeers(vector& peers); std::vector getRandomAuthenticatedPeers() override; diff --git a/src/overlay/OverlayManagerTests.cpp b/src/overlay/OverlayManagerTests.cpp index 9eacb2460c..e75a42028d 100644 --- a/src/overlay/OverlayManagerTests.cpp +++ b/src/overlay/OverlayManagerTests.cpp @@ -32,20 +32,22 @@ class PeerStub : public Peer public: int sent = 0; - PeerStub(Application& app, short port) : Peer(app, WE_CALLED_REMOTE) + PeerStub(Application& app, PeerBareAddress const& addres) + : Peer(app, WE_CALLED_REMOTE) { mPeerID = SecretKey::random().getPublicKey(); mState = GOT_AUTH; - mRemoteListeningPort = port; + mAddress = addres; } - virtual void - drop(bool) override + virtual PeerBareAddress + makeAddress(unsigned short) const override { + REQUIRE(false); // should not be called + return {}; } - virtual string - getIP() override + virtual void + drop(bool) override { - return "127.0.0.1"; } virtual void sendMessage(xdr::msg_ptr&& xdrBytes) override @@ -64,12 +66,12 @@ class OverlayManagerStub : public OverlayManagerImpl virtual void connectTo(PeerRecord& pr) override { - if (!getConnectedPeer(pr.ip(), pr.port())) + if (!getConnectedPeer(pr.getAddress())) { pr.backOff(mApp.getClock()); pr.storePeerRecord(mApp.getDatabase()); - auto peerStub = std::make_shared(mApp, pr.port()); + auto peerStub = std::make_shared(mApp, pr.getAddress()); addPendingPeer(peerStub); REQUIRE(acceptAuthenticatedPeer(peerStub)); } diff --git a/src/overlay/Peer.cpp b/src/overlay/Peer.cpp index 4c7b5a67d9..b0128669c5 100644 --- a/src/overlay/Peer.cpp +++ b/src/overlay/Peer.cpp @@ -57,7 +57,6 @@ Peer::Peer(Application& app, PeerRole role) , mRole(role) , mState(role == WE_CALLED_REMOTE ? CONNECTING : CONNECTED) , mRemoteOverlayVersion(0) - , mRemoteListeningPort(0) , mIdleTimer(app) , mLastRead(app.getClock().now()) , mLastWrite(app.getClock().now()) @@ -156,8 +155,8 @@ Peer::Peer(Application& app, PeerRole role) {"overlay", "drop", "recv-hello-ban"}, "drop")) , mDropInRecvHelloNetMeter(app.getMetrics().NewMeter( {"overlay", "drop", "recv-hello-net"}, "drop")) - , mDropInRecvHelloPortMeter(app.getMetrics().NewMeter( - {"overlay", "drop", "recv-hello-port"}, "drop")) + , mDropInRecvHelloAddressMeter(app.getMetrics().NewMeter( + {"overlay", "drop", "recv-hello-address"}, "drop")) , mDropInRecvAuthUnexpectedMeter(app.getMetrics().NewMeter( {"overlay", "drop", "recv-auth-unexpected"}, "drop")) , mDropInRecvAuthRejectMeter(app.getMetrics().NewMeter( @@ -274,9 +273,7 @@ Peer::sendAuth() std::string Peer::toString() { - std::stringstream s; - s << getIP() << ":" << mRemoteListeningPort; - return s.str(); + return mAddress.toString(); } void @@ -383,26 +380,20 @@ Peer::sendPeers() { // send top 50 peers we know about vector peerList; - PeerRecord::loadPeerRecords( - mApp.getDatabase(), 50, mApp.getClock().now(), - [&](PeerRecord const& pr) { - if (!pr.isPrivateAddress() && - !pr.isSelfAddressAndPort(getIP(), mRemoteListeningPort)) - { - peerList.emplace_back(pr); - } - return peerList.size() < 50; - }); + PeerRecord::loadPeerRecords(mApp.getDatabase(), 50, mApp.getClock().now(), + [&](PeerRecord const& pr) { + if (!pr.getAddress().isPrivate() && + pr.getAddress() != mAddress) + { + peerList.emplace_back(pr); + } + return peerList.size() < 50; + }); StellarMessage newMsg; newMsg.type(PEERS); newMsg.peers().reserve(peerList.size()); for (auto const& pr : peerList) { - if (pr.isPrivateAddress() || - pr.isSelfAddressAndPort(getIP(), mRemoteListeningPort)) - { - continue; - } PeerAddress pa; pr.toXdr(pa); newMsg.peers().push_back(pa); @@ -874,17 +865,15 @@ Peer::recvError(StellarMessage const& msg) void Peer::noteHandshakeSuccessInPeerRecord() { - if (getIP().empty() || getRemoteListeningPort() == 0) + if (getAddress().isEmpty()) { - CLOG(ERROR, "Overlay") << "unable to handshake with " << getIP() << ":" - << getRemoteListeningPort(); + CLOG(ERROR, "Overlay") << "unable to handshake with " << toString(); mDropInRecvAuthInvalidPeerMeter.Mark(); drop(); return; } - auto pr = PeerRecord::loadPeerRecord(mApp.getDatabase(), getIP(), - getRemoteListeningPort()); + auto pr = PeerRecord::loadPeerRecord(mApp.getDatabase(), getAddress()); if (pr) { pr->setPreferred(mApp.getOverlayManager().isPreferred(this)); @@ -892,8 +881,7 @@ Peer::noteHandshakeSuccessInPeerRecord() } else { - pr = make_optional(getIP(), mRemoteListeningPort, - mApp.getClock().now()); + pr = make_optional(getAddress(), mApp.getClock().now()); } CLOG(INFO, "Overlay") << "successful handshake with " << mApp.getConfig().toShortString(mPeerID) << "@" @@ -931,7 +919,6 @@ Peer::recvHello(Hello const& elo) return; } - mRemoteListeningPort = static_cast(elo.listeningPort); mRemoteOverlayMinVersion = elo.overlayMinVersion; mRemoteOverlayVersion = elo.overlayVersion; mRemoteVersion = elo.versionStr; @@ -1026,11 +1013,12 @@ Peer::recvHello(Hello const& elo) } } - if (elo.listeningPort <= 0 || elo.listeningPort > UINT16_MAX) + mAddress = makeAddress(elo.listeningPort); + if (mAddress.isEmpty()) { - CLOG(WARNING, "Overlay") << "bad port in recvHello"; - mDropInRecvHelloPortMeter.Mark(); - drop(ERR_CONF, "bad port number"); + CLOG(WARNING, "Overlay") << "bad address in recvHello"; + mDropInRecvHelloAddressMeter.Mark(); + drop(ERR_CONF, "bad address"); return; } @@ -1117,31 +1105,30 @@ Peer::recvPeers(StellarMessage const& msg) mApp.getClock().now() + std::chrono::seconds(std::rand() % NEW_PEER_WINDOW_SECONDS); - stringstream ip; - ip << (int)peer.ip.ipv4()[0] << "." << (int)peer.ip.ipv4()[1] << "." - << (int)peer.ip.ipv4()[2] << "." << (int)peer.ip.ipv4()[3]; - // don't use peer.numFailures here as we may have better luck - // (and we don't want to poison our failure count) - PeerRecord pr{ip.str(), static_cast(peer.port), - defaultNextAttempt, 0}; + assert(peer.ip.type() == IPv4); + auto address = PeerBareAddress{peer}; - if (pr.isPrivateAddress()) + if (address.isPrivate()) { CLOG(WARNING, "Overlay") - << "ignoring received private address " << pr.toString(); + << "ignoring received private address " << address.toString(); } - else if (pr.isSelfAddressAndPort(getIP(), mApp.getConfig().PEER_PORT)) + else if (address == PeerBareAddress{getAddress().getIP(), + mApp.getConfig().PEER_PORT}) { CLOG(WARNING, "Overlay") - << "ignoring received self-address " << pr.toString(); + << "ignoring received self-address " << address.toString(); } - else if (pr.isLocalhost() && + else if (address.isLocalhost() && !mApp.getConfig().ALLOW_LOCALHOST_FOR_TESTING) { CLOG(WARNING, "Overlay") << "ignoring received localhost"; } else { + // don't use peer.numFailures here as we may have better luck + // (and we don't want to poison our failure count) + PeerRecord pr{address, defaultNextAttempt, 0}; pr.insertIfNew(mApp.getDatabase()); } } diff --git a/src/overlay/Peer.h b/src/overlay/Peer.h index fea4f86c19..694f230521 100644 --- a/src/overlay/Peer.h +++ b/src/overlay/Peer.h @@ -6,6 +6,7 @@ #include "util/asio.h" #include "database/Database.h" +#include "overlay/PeerBareAddress.h" #include "overlay/StellarXDR.h" #include "util/NonCopyable.h" #include "util/Timer.h" @@ -70,7 +71,7 @@ class Peer : public std::enable_shared_from_this, std::string mRemoteVersion; uint32_t mRemoteOverlayMinVersion; uint32_t mRemoteOverlayVersion; - unsigned short mRemoteListeningPort; + PeerBareAddress mAddress; VirtualTimer mIdleTimer; VirtualClock::time_point mLastRead; @@ -129,7 +130,7 @@ class Peer : public std::enable_shared_from_this, medida::Meter& mDropInRecvHelloCertMeter; medida::Meter& mDropInRecvHelloBanMeter; medida::Meter& mDropInRecvHelloNetMeter; - medida::Meter& mDropInRecvHelloPortMeter; + medida::Meter& mDropInRecvHelloAddressMeter; medida::Meter& mDropInRecvAuthUnexpectedMeter; medida::Meter& mDropInRecvAuthRejectMeter; medida::Meter& mDropInRecvAuthInvalidPeerMeter; @@ -177,6 +178,8 @@ class Peer : public std::enable_shared_from_this, } virtual AuthCert getAuthCert(); + virtual PeerBareAddress + makeAddress(unsigned short remoteListeningPort) const = 0; void startIdleTimer(); void idleTimerExpired(asio::error_code const& error); @@ -234,11 +237,12 @@ class Peer : public std::enable_shared_from_this, return mRemoteOverlayVersion; } - unsigned short - getRemoteListeningPort() + PeerBareAddress const& + getAddress() { - return mRemoteListeningPort; + return mAddress; } + NodeID getPeerID() { @@ -271,7 +275,6 @@ class Peer : public std::enable_shared_from_this, // If force is true, it will drop immediately without waiting for all // outgoing messages to be sent virtual void drop(bool force = true) = 0; - virtual std::string getIP() = 0; virtual ~Peer() { } diff --git a/src/overlay/PeerBareAddress.cpp b/src/overlay/PeerBareAddress.cpp new file mode 100644 index 0000000000..1f00b264e9 --- /dev/null +++ b/src/overlay/PeerBareAddress.cpp @@ -0,0 +1,210 @@ +// Copyright 2018 Stellar Development Foundation and contributors. Licensed +// under the Apache License, Version 2.0. See the COPYING file at the root +// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 + +#include "util/asio.h" +#include "overlay/PeerBareAddress.h" +#include "main/Application.h" +#include "util/Logging.h" + +#include +#include +#include + +namespace stellar +{ + +namespace +{ + +void +ipToXdr(std::string const& ip, xdr::opaque_array<4U>& ret) +{ + std::stringstream ss(ip); + std::string item; + int n = 0; + while (getline(ss, item, '.') && n < 4) + { + ret[n] = static_cast(atoi(item.c_str())); + n++; + } + if (n != 4) + throw std::runtime_error("ipToXdr: failed on `" + ip + "`"); +} +} + +PeerBareAddress::PeerBareAddress() : mType{Type::EMPTY} +{ +} + +PeerBareAddress::PeerBareAddress(std::string ip, unsigned short port) + : mType{Type::IPv4}, mIP{std::move(ip)}, mPort{port} +{ + if (mIP.empty()) + { + throw std::runtime_error("Cannot create PeerBareAddress with empty ip"); + } + if (mPort == 0) + { + throw std::runtime_error("Cannot create PeerBareAddress with port 0"); + } +} + +PeerBareAddress::PeerBareAddress(PeerAddress const& pa) : mType{Type::IPv4} +{ + assert(pa.ip.type() == IPv4); + + std::stringstream ip; + ip << (int)pa.ip.ipv4()[0] << "." << (int)pa.ip.ipv4()[1] << "." + << (int)pa.ip.ipv4()[2] << "." << (int)pa.ip.ipv4()[3]; + mIP = ip.str(); + mPort = pa.port; +} + +PeerBareAddress +PeerBareAddress::resolve(std::string const& ipPort, Application& app, + unsigned short defaultPort) +{ + static std::regex re( + "^(?:(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|([[:alnum:].-]+))" + "(?:\\:(\\d{1,5}))?$"); + std::smatch m; + + if (!std::regex_search(ipPort, m, re) || m.empty()) + { + throw std::runtime_error( + fmt::format("Cannot parse peer address '{}'", ipPort)); + } + + asio::ip::tcp::resolver::query::flags resolveflags; + std::string toResolve; + if (m[1].matched) + { + resolveflags = asio::ip::tcp::resolver::query::flags::numeric_host; + toResolve = m[1].str(); + } + else + { + resolveflags = asio::ip::tcp::resolver::query::flags::v4_mapped; + toResolve = m[2].str(); + } + + asio::ip::tcp::resolver resolver(app.getWorkerIOService()); + asio::ip::tcp::resolver::query query(toResolve, "", resolveflags); + + asio::error_code ec; + asio::ip::tcp::resolver::iterator i = resolver.resolve(query, ec); + if (ec) + { + LOG(DEBUG) << "Could not resolve '" << ipPort << "' : " << ec.message(); + throw std::runtime_error( + fmt::format("Could not resolve '{}': {}", ipPort, ec.message())); + } + + std::string ip; + while (i != asio::ip::tcp::resolver::iterator()) + { + asio::ip::tcp::endpoint end = *i; + if (end.address().is_v4()) + { + ip = end.address().to_v4().to_string(); + break; + } + i++; + } + if (ip.empty()) + { + throw std::runtime_error( + fmt::format("Could not resolve '{}': {}", ipPort, ec.message())); + } + + unsigned short port = defaultPort; + if (m[3].matched) + { + int parsedPort = atoi(m[3].str().c_str()); + if (parsedPort <= 0 || parsedPort > UINT16_MAX) + { + throw std::runtime_error(fmt::format("Could not resolve '{}': {}", + ipPort, ec.message())); + } + port = static_cast(parsedPort); + } + + assert(!ip.empty()); + assert(port != 0); + + return PeerBareAddress{ip, port}; +} + +std::string +PeerBareAddress::toString() const +{ + switch (mType) + { + case Type::EMPTY: + { + return "(empty)"; + } + case Type::IPv4: + { + return mIP + ":" + std::to_string(mPort); + } + default: + assert(false); + } +} + +bool +PeerBareAddress::isPrivate() const +{ + asio::error_code ec; + asio::ip::address_v4 addr = asio::ip::address_v4::from_string(mIP, ec); + if (ec) + { + return false; + } + unsigned long val = addr.to_ulong(); + if (((val >> 24) == 10) // 10.x.y.z + || ((val >> 20) == 2753) // 172.[16-31].x.y + || ((val >> 16) == 49320)) // 192.168.x.y + { + return true; + } + return false; +} + +bool +PeerBareAddress::isLocalhost() const +{ + return mIP == "127.0.0.1"; +} + +void +PeerBareAddress::toXdr(PeerAddress& ret) const +{ + ret.port = mPort; + ret.ip.type(IPv4); + ipToXdr(mIP, ret.ip.ipv4()); +} + +bool +operator==(PeerBareAddress const& x, PeerBareAddress const& y) +{ + if (x.mIP != y.mIP) + { + return false; + } + if (x.mPort != y.mPort) + { + return false; + } + + return true; +} + +bool +operator!=(PeerBareAddress const& x, PeerBareAddress const& y) +{ + return !(x == y); +} +} diff --git a/src/overlay/PeerBareAddress.h b/src/overlay/PeerBareAddress.h new file mode 100644 index 0000000000..6f95e4db76 --- /dev/null +++ b/src/overlay/PeerBareAddress.h @@ -0,0 +1,70 @@ +#pragma once + +// Copyright 2018 Stellar Development Foundation and contributors. Licensed +// under the Apache License, Version 2.0. See the COPYING file at the root +// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 + +#include "main/Config.h" +#include "xdr/Stellar-overlay.h" + +namespace stellar +{ + +class Application; + +class PeerBareAddress +{ + public: + enum class Type + { + EMPTY, + IPv4 + }; + + PeerBareAddress(); + explicit PeerBareAddress(std::string ip, unsigned short port); + explicit PeerBareAddress(PeerAddress const& pa); + + static PeerBareAddress + resolve(std::string const& ipPort, Application& app, + unsigned short defaultPort = DEFAULT_PEER_PORT); + + bool + isEmpty() const + { + return mType == Type::EMPTY; + } + + Type + getType() const + { + return mType; + } + + std::string const& + getIP() const + { + return mIP; + } + + unsigned short + getPort() const + { + return mPort; + } + + std::string toString() const; + void toXdr(PeerAddress& ret) const; + + bool isPrivate() const; + bool isLocalhost() const; + + friend bool operator==(PeerBareAddress const& x, PeerBareAddress const& y); + friend bool operator!=(PeerBareAddress const& x, PeerBareAddress const& y); + + private: + Type mType; + std::string mIP; + unsigned short mPort; +}; +} diff --git a/src/overlay/PeerRecord.cpp b/src/overlay/PeerRecord.cpp index a622b76cc5..1c62a7814d 100644 --- a/src/overlay/PeerRecord.cpp +++ b/src/overlay/PeerRecord.cpp @@ -31,120 +31,24 @@ static const char* loadPeerRecordSelector = using namespace std; using namespace soci; -PeerRecord::PeerRecord(string const& ip, unsigned short port, +PeerRecord::PeerRecord(PeerBareAddress address, VirtualClock::time_point nextAttempt, int fails) - : mIP(ip) - , mPort(port) + : mAddress(std::move(address)) , mIsPreferred(false) , mNextAttempt(nextAttempt) , mNumFailures(fails) { - if (mIP.empty()) + if (address.isEmpty()) { - throw std::runtime_error("Cannot create PeerRecord with empty ip"); + throw std::runtime_error("Cannot create PeerRecord with empty address"); } - if (mPort == 0) - { - throw std::runtime_error("Cannot create PeerRecord with port 0"); - } -} - -void -PeerRecord::ipToXdr(string ip, xdr::opaque_array<4U>& ret) -{ - stringstream ss(ip); - string item; - int n = 0; - while (getline(ss, item, '.') && n < 4) - { - ret[n] = static_cast(atoi(item.c_str())); - n++; - } - if (n != 4) - throw runtime_error("PeerRecord::ipToXdr: failed on `" + ip + "`"); } void PeerRecord::toXdr(PeerAddress& ret) const { - ret.port = mPort; + mAddress.toXdr(ret); ret.numFailures = mNumFailures; - ipToXdr(mIP, ret.ip.ipv4()); -} - -PeerRecord -PeerRecord::parseIPPort(string const& ipPort, Application& app, - unsigned short defaultPort) -{ - static std::regex re( - "^(?:(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|([[:alnum:].-]+))" - "(?:\\:(\\d{1,5}))?$"); - std::smatch m; - - if (!std::regex_search(ipPort, m, re) || m.empty()) - { - throw std::runtime_error( - fmt::format("Cannot parse peer address '{}'", ipPort)); - } - - asio::ip::tcp::resolver::query::flags resolveflags; - std::string toResolve; - if (m[1].matched) - { - resolveflags = asio::ip::tcp::resolver::query::flags::numeric_host; - toResolve = m[1].str(); - } - else - { - resolveflags = asio::ip::tcp::resolver::query::flags::v4_mapped; - toResolve = m[2].str(); - } - - asio::ip::tcp::resolver resolver(app.getWorkerIOService()); - asio::ip::tcp::resolver::query query(toResolve, "", resolveflags); - - asio::error_code ec; - asio::ip::tcp::resolver::iterator i = resolver.resolve(query, ec); - if (ec) - { - LOG(DEBUG) << "Could not resolve '" << ipPort << "' : " << ec.message(); - throw std::runtime_error( - fmt::format("Could not resolve '{}': {}", ipPort, ec.message())); - } - - string ip; - while (i != asio::ip::tcp::resolver::iterator()) - { - asio::ip::tcp::endpoint end = *i; - if (end.address().is_v4()) - { - ip = end.address().to_v4().to_string(); - break; - } - i++; - } - if (ip.empty()) - { - throw std::runtime_error( - fmt::format("Could not resolve '{}': {}", ipPort, ec.message())); - } - - unsigned short port = defaultPort; - if (m[3].matched) - { - int parsedPort = atoi(m[3].str().c_str()); - if (parsedPort <= 0 || parsedPort > UINT16_MAX) - { - throw std::runtime_error(fmt::format("Could not resolve '{}': {}", - ipPort, ec.message())); - } - port = static_cast(parsedPort); - } - - assert(!ip.empty()); - assert(port != 0); - - return PeerRecord{ip, port, app.getClock().now(), 0}; } // peerRecordProcessor returns false if we should stop processing entries @@ -174,9 +78,10 @@ PeerRecord::loadPeerRecords( { if (!ip.empty() && lport > 0) { - auto pr = - PeerRecord{ip, static_cast(lport), - VirtualClock::tmToPoint(nextAttempt), numFailures}; + auto address = + PeerBareAddress{ip, static_cast(lport)}; + auto pr = PeerRecord{address, VirtualClock::tmToPoint(nextAttempt), + numFailures}; pr.setPreferred((flags & PEER_RECORD_FLAGS_PREFERRED) != 0); if (!peerRecordProcessor(pr)) @@ -189,21 +94,17 @@ PeerRecord::loadPeerRecords( } optional -PeerRecord::loadPeerRecord(Database& db, string ip, unsigned short port) +PeerRecord::loadPeerRecord(Database& db, PeerBareAddress const& address) { - if (ip.empty() || port == 0) - { - return nullopt(); - } - std::string sql = loadPeerRecordSelector; sql += "WHERE ip = :v1 AND port = :v2"; auto prep = db.getPreparedStatement(sql); auto& st = prep.statement(); + auto ip = address.getIP(); st.exchange(use(ip)); - int port32(port); + int port32(address.getPort()); st.exchange(use(port32)); optional r; @@ -255,49 +156,6 @@ PeerRecord::loadPeerRecords(Database& db, int batchSize, } } -bool -PeerRecord::isSelfAddressAndPort(std::string const& ip, - unsigned short port) const -{ - asio::error_code ec; - asio::ip::address_v4 addr = asio::ip::address_v4::from_string(mIP, ec); - if (ec) - { - return false; - } - asio::ip::address_v4 otherAddr = asio::ip::address_v4::from_string(ip, ec); - if (ec) - { - return false; - } - return (addr == otherAddr && port == mPort); -} - -bool -PeerRecord::isPrivateAddress() const -{ - asio::error_code ec; - asio::ip::address_v4 addr = asio::ip::address_v4::from_string(mIP, ec); - if (ec) - { - return false; - } - unsigned long val = addr.to_ulong(); - if (((val >> 24) == 10) // 10.x.y.z - || ((val >> 20) == 2753) // 172.[16-31].x.y - || ((val >> 16) == 49320)) // 192.168.x.y - { - return true; - } - return false; -} - -bool -PeerRecord::isLocalhost() const -{ - return mIP == "127.0.0.1"; -} - bool PeerRecord::isPreferred() const { @@ -315,7 +173,7 @@ PeerRecord::insertIfNew(Database& db) { auto tm = VirtualClock::pointToTm(mNextAttempt); - auto other = loadPeerRecord(db, mIP, mPort); + auto other = loadPeerRecord(db, mAddress); if (other) { @@ -328,8 +186,9 @@ PeerRecord::insertIfNew(Database& db) "( ip, port, nextattempt, numfailures, flags) VALUES " "(:v1, :v2, :v3, :v4, :v5)"); auto& st = prep.statement(); - st.exchange(use(mIP)); - int port = mPort; + auto ip = mAddress.getIP(); + st.exchange(use(ip)); + int port = mAddress.getPort(); st.exchange(use(port)); st.exchange(use(tm)); st.exchange(use(mNumFailures)); @@ -361,8 +220,9 @@ PeerRecord::storePeerRecord(Database& db) st.exchange(use(mNumFailures)); int flags = (mIsPreferred ? PEER_RECORD_FLAGS_PREFERRED : 0); st.exchange(use(flags)); - st.exchange(use(mIP)); - int port = mPort; + auto ip = mAddress.getIP(); + st.exchange(use(ip)); + int port = mAddress.getPort(); st.exchange(use(port)); st.define_and_bind(); { @@ -408,10 +268,10 @@ PeerRecord::computeBackoff(VirtualClock& clock) return nsecs; } -string -PeerRecord::toString() +std::string +PeerRecord::toString() const { - return mIP + ":" + to_string(mPort); + return mAddress.toString(); } void diff --git a/src/overlay/PeerRecord.h b/src/overlay/PeerRecord.h index a2682edff7..09e32d7707 100644 --- a/src/overlay/PeerRecord.h +++ b/src/overlay/PeerRecord.h @@ -6,6 +6,7 @@ #include "database/Database.h" #include "main/Config.h" +#include "overlay/PeerBareAddress.h" #include "util/Timer.h" #include "util/optional.h" #include @@ -17,8 +18,7 @@ using namespace std; class PeerRecord { private: - std::string mIP; - unsigned short mPort; + PeerBareAddress mAddress; bool mIsPreferred; public: @@ -32,49 +32,37 @@ class PeerRecord * @pre: !ip.empty() * @pre: port > 0 */ - PeerRecord(string const& ip, unsigned short port, - VirtualClock::time_point nextAttempt, int fails = 0); + PeerRecord(PeerBareAddress address, VirtualClock::time_point nextAttempt, + int fails = 0); bool operator==(PeerRecord& other) { - return mIP == other.mIP && mPort == other.mPort && + return mAddress == other.mAddress && mNextAttempt == other.mNextAttempt && mNumFailures == other.mNumFailures; } - static PeerRecord - parseIPPort(std::string const& ipPort, Application& app, - unsigned short defaultPort = DEFAULT_PEER_PORT); - /** * Load PeerRecord from database. If preconditions are not met - exception * is thrown from PeerRecord constructor. * If given PeerRecord is not found, nullopt is returned. * If @p ip is empty or @ip is set to 0 nullopt is returned. */ - static optional loadPeerRecord(Database& db, std::string ip, - unsigned short port); + static optional loadPeerRecord(Database& db, + PeerBareAddress const& address); // pred returns false if we should stop processing entries static void loadPeerRecords(Database& db, int batchSize, VirtualClock::time_point nextAttemptCutoff, std::function pred); - const std::string& - ip() const - { - return mIP; - }; - unsigned short - port() const + + PeerBareAddress const& + getAddress() const { - return mPort; + return mAddress; }; - bool isSelfAddressAndPort(std::string const& ip, unsigned short port) const; - bool isPrivateAddress() const; - bool isLocalhost() const; - void setPreferred(bool p); bool isPreferred() const; @@ -91,7 +79,7 @@ class PeerRecord void toXdr(PeerAddress& ret) const; static void dropAll(Database& db); - std::string toString(); + std::string toString() const; private: // peerRecordProcessor returns false if we should stop processing entries @@ -99,7 +87,6 @@ class PeerRecord loadPeerRecords(Database& db, StatementContext& prep, std::function peerRecordProcessor); std::chrono::seconds computeBackoff(VirtualClock& clock); - static void ipToXdr(std::string ip, xdr::opaque_array<4U>& ret); static const char* kSQLCreateStatement; }; } diff --git a/src/overlay/PeerRecordTests.cpp b/src/overlay/PeerRecordTests.cpp index 3952561c58..7bcadeae77 100644 --- a/src/overlay/PeerRecordTests.cpp +++ b/src/overlay/PeerRecordTests.cpp @@ -21,13 +21,14 @@ TEST_CASE("toXdr", "[overlay][PeerRecord]") { VirtualClock clock; Application::pointer app = createTestApplication(clock, getTestConfig()); - auto pr = PeerRecord::parseIPPort("1.25.50.200:256", *app); + auto address = PeerBareAddress::resolve("1.25.50.200:256", *app); + auto pr = PeerRecord{address, app->getClock().now(), 0}; pr.mNumFailures = 2; SECTION("fromIPPort and toXdr") { - REQUIRE(pr.ip() == "1.25.50.200"); - REQUIRE(pr.port() == 256); + REQUIRE(address.getIP() == "1.25.50.200"); + REQUIRE(address.getPort() == 256); PeerAddress xdr; pr.toXdr(xdr); @@ -49,68 +50,67 @@ TEST_CASE("toXdr", "[overlay][PeerRecord]") pr2.mNumFailures++; REQUIRE(!pr2.insertIfNew(app->getDatabase())); - auto actualPR = PeerRecord::loadPeerRecord(app->getDatabase(), - pr.ip(), pr.port()); + auto actualPR = + PeerRecord::loadPeerRecord(app->getDatabase(), pr.getAddress()); REQUIRE(*actualPR == pr); } - PeerRecord other("1.2.3.4", 15, clock.now()); + PeerRecord other(PeerBareAddress{"1.2.3.4", 15}, clock.now()); other.storePeerRecord(app->getDatabase()); pr.mNextAttempt = pr.mNextAttempt + chrono::seconds(12); pr.storePeerRecord(app->getDatabase()); auto actual1 = - PeerRecord::loadPeerRecord(app->getDatabase(), pr.ip(), pr.port()); + PeerRecord::loadPeerRecord(app->getDatabase(), pr.getAddress()); REQUIRE(*actual1 == pr); - auto actual2 = - PeerRecord::loadPeerRecord(app->getDatabase(), "1.2.3.4", 15); + auto actual2 = PeerRecord::loadPeerRecord( + app->getDatabase(), PeerBareAddress{"1.2.3.4", 15}); REQUIRE(*actual2 == other); } } TEST_CASE("private addresses", "[overlay][PeerRecord]") { - VirtualClock clock; - PeerRecord pr("1.2.3.4", 15, clock.now()); - CHECK(!pr.isPrivateAddress()); - pr = PeerRecord("10.1.2.3", 15, clock.now()); - CHECK(pr.isPrivateAddress()); - pr = PeerRecord("172.17.1.2", 15, clock.now()); - CHECK(pr.isPrivateAddress()); - pr = PeerRecord("192.168.1.2", 15, clock.now()); - CHECK(pr.isPrivateAddress()); + PeerBareAddress pa("1.2.3.4", 15); + CHECK(!pa.isPrivate()); + pa = PeerBareAddress("10.1.2.3", 15); + CHECK(pa.isPrivate()); + pa = PeerBareAddress("172.17.1.2", 15); + CHECK(pa.isPrivate()); + pa = PeerBareAddress("192.168.1.2", 15); + CHECK(pa.isPrivate()); } TEST_CASE("create peer rercord", "[overlay][PeerRecord]") { SECTION("empty") { - REQUIRE_THROWS_AS(PeerRecord("", 0, {}), std::runtime_error); + REQUIRE_THROWS_AS(PeerBareAddress("", 0), std::runtime_error); } SECTION("empty ip") { - REQUIRE_THROWS_AS(PeerRecord("", 80, {}), std::runtime_error); + REQUIRE_THROWS_AS(PeerBareAddress("", 80), std::runtime_error); } SECTION("zero port") { - REQUIRE_THROWS_AS(PeerRecord("127.0.0.1", 0, {}), std::runtime_error); + REQUIRE_THROWS_AS(PeerBareAddress("127.0.0.1", 0), std::runtime_error); } - SECTION("random string") // PeerRecord does not validate IP format + SECTION("random string") // PeerBareAddress does not validate IP format { - auto pr = PeerRecord("random string", 80, {}); - REQUIRE(pr.ip() == "random string"); - REQUIRE(pr.port() == 80); + auto pa = PeerBareAddress("random string", 80); + REQUIRE(pa.getIP() == "random string"); + REQUIRE(pa.getPort() == 80); } SECTION("valid data") { - auto pr = PeerRecord("127.0.0.1", 80, {}); - REQUIRE(pr.ip() == "127.0.0.1"); - REQUIRE(pr.port() == 80); + auto pa = PeerBareAddress("127.0.0.1", 80); + REQUIRE(pa.getIP() == "127.0.0.1"); + REQUIRE(pa.getPort() == 80); } } @@ -121,39 +121,40 @@ TEST_CASE("parse peer rercord", "[overlay][PeerRecord]") SECTION("empty") { - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("", *app), std::runtime_error); } SECTION("random string") { - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("random string", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("random string", *app), std::runtime_error); } SECTION("invalid ipv4") { - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("127.0.0.256", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("127.0.0.256", *app), std::runtime_error); - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("256.256.256.256", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("256.256.256.256", *app), std::runtime_error); } SECTION("ipv4 mask instead of address") { - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("127.0.0.1/8", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("127.0.0.1/8", *app), std::runtime_error); - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("127.0.0.1/16", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("127.0.0.1/16", *app), std::runtime_error); - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("127.0.0.1/24", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("127.0.0.1/24", *app), std::runtime_error); } SECTION("valid ipv6") { - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("2001:db8:a0b:12f0::1", *app), - std::runtime_error); - REQUIRE_THROWS_AS(PeerRecord::parseIPPort( + REQUIRE_THROWS_AS( + PeerBareAddress::resolve("2001:db8:a0b:12f0::1", *app), + std::runtime_error); + REQUIRE_THROWS_AS(PeerBareAddress::resolve( "2001:0db8:0a0b:12f0:0000:0000:0000:0001", *app), std::runtime_error); } @@ -161,9 +162,9 @@ TEST_CASE("parse peer rercord", "[overlay][PeerRecord]") SECTION("invalid ipv6") { REQUIRE_THROWS_AS( - PeerRecord::parseIPPort("10000:db8:a0b:12f0::1", *app), + PeerBareAddress::resolve("10000:db8:a0b:12f0::1", *app), std::runtime_error); - REQUIRE_THROWS_AS(PeerRecord::parseIPPort( + REQUIRE_THROWS_AS(PeerBareAddress::resolve( "2001:0db8:0a0b:12f0:0000:10000:0000:0001", *app), std::runtime_error); } @@ -171,73 +172,73 @@ TEST_CASE("parse peer rercord", "[overlay][PeerRecord]") SECTION("ipv6 mask instead of address") { REQUIRE_THROWS_AS( - PeerRecord::parseIPPort("2001:db8:a0b:12f0::1/16", *app), + PeerBareAddress::resolve("2001:db8:a0b:12f0::1/16", *app), std::runtime_error); REQUIRE_THROWS_AS( - PeerRecord::parseIPPort("2001:db8:a0b:12f0::1/32", *app), + PeerBareAddress::resolve("2001:db8:a0b:12f0::1/32", *app), std::runtime_error); REQUIRE_THROWS_AS( - PeerRecord::parseIPPort("2001:db8:a0b:12f0::1/64", *app), + PeerBareAddress::resolve("2001:db8:a0b:12f0::1/64", *app), std::runtime_error); } SECTION("valid ipv4 with empty port") { - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("127.0.0.2:", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("127.0.0.2:", *app), std::runtime_error); } SECTION("valid ipv4 with invalid port") { - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("127.0.0.2:-1", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("127.0.0.2:-1", *app), std::runtime_error); - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("127.0.0.2:0", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("127.0.0.2:0", *app), std::runtime_error); - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("127.0.0.2:65536", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("127.0.0.2:65536", *app), std::runtime_error); - REQUIRE_THROWS_AS(PeerRecord::parseIPPort("127.0.0.2:65537", *app), + REQUIRE_THROWS_AS(PeerBareAddress::resolve("127.0.0.2:65537", *app), std::runtime_error); } SECTION("valid ipv4 with default port") { - auto pr = PeerRecord::parseIPPort("127.0.0.2", *app); - REQUIRE(pr.ip() == "127.0.0.2"); - REQUIRE(pr.port() == DEFAULT_PEER_PORT); + auto pr = PeerBareAddress::resolve("127.0.0.2", *app); + REQUIRE(pr.getIP() == "127.0.0.2"); + REQUIRE(pr.getPort() == DEFAULT_PEER_PORT); - pr = PeerRecord::parseIPPort("8.8.8.8", *app); - REQUIRE(pr.ip() == "8.8.8.8"); - REQUIRE(pr.port() == DEFAULT_PEER_PORT); + pr = PeerBareAddress::resolve("8.8.8.8", *app); + REQUIRE(pr.getIP() == "8.8.8.8"); + REQUIRE(pr.getPort() == DEFAULT_PEER_PORT); } SECTION("valid ipv4 with different default port") { - auto pr = PeerRecord::parseIPPort("127.0.0.2", *app, 10); - REQUIRE(pr.ip() == "127.0.0.2"); - REQUIRE(pr.port() == 10); + auto pr = PeerBareAddress::resolve("127.0.0.2", *app, 10); + REQUIRE(pr.getIP() == "127.0.0.2"); + REQUIRE(pr.getPort() == 10); - pr = PeerRecord::parseIPPort("8.8.8.8", *app, 10); - REQUIRE(pr.ip() == "8.8.8.8"); - REQUIRE(pr.port() == 10); + pr = PeerBareAddress::resolve("8.8.8.8", *app, 10); + REQUIRE(pr.getIP() == "8.8.8.8"); + REQUIRE(pr.getPort() == 10); } SECTION("valid ipv4 with valid port") { - auto pr = PeerRecord::parseIPPort("127.0.0.2:1", *app); - REQUIRE(pr.ip() == "127.0.0.2"); - REQUIRE(pr.port() == 1); + auto pr = PeerBareAddress::resolve("127.0.0.2:1", *app); + REQUIRE(pr.getIP() == "127.0.0.2"); + REQUIRE(pr.getPort() == 1); - pr = PeerRecord::parseIPPort("127.0.0.2:1234", *app); - REQUIRE(pr.ip() == "127.0.0.2"); - REQUIRE(pr.port() == 1234); + pr = PeerBareAddress::resolve("127.0.0.2:1234", *app); + REQUIRE(pr.getIP() == "127.0.0.2"); + REQUIRE(pr.getPort() == 1234); - pr = PeerRecord::parseIPPort("127.0.0.2:65534", *app); - REQUIRE(pr.ip() == "127.0.0.2"); - REQUIRE(pr.port() == 65534); + pr = PeerBareAddress::resolve("127.0.0.2:65534", *app); + REQUIRE(pr.getIP() == "127.0.0.2"); + REQUIRE(pr.getPort() == 65534); - pr = PeerRecord::parseIPPort("127.0.0.2:65535", *app); - REQUIRE(pr.ip() == "127.0.0.2"); - REQUIRE(pr.port() == 65535); + pr = PeerBareAddress::resolve("127.0.0.2:65535", *app); + REQUIRE(pr.getIP() == "127.0.0.2"); + REQUIRE(pr.getPort() == 65535); } } } diff --git a/src/overlay/TCPPeer.cpp b/src/overlay/TCPPeer.cpp index 81fcc3ca77..ca1392d617 100644 --- a/src/overlay/TCPPeer.cpp +++ b/src/overlay/TCPPeer.cpp @@ -34,17 +34,19 @@ TCPPeer::TCPPeer(Application& app, Peer::PeerRole role, } TCPPeer::pointer -TCPPeer::initiate(Application& app, std::string const& ip, unsigned short port) +TCPPeer::initiate(Application& app, PeerBareAddress const& address) { + assert(address.getType() == PeerBareAddress::Type::IPv4); + CLOG(DEBUG, "Overlay") << "TCPPeer:initiate" - << " to " << ip << ":" << port; + << " to " << address.toString(); assertThreadIsMain(); auto socket = make_shared(app.getClock().getIOService()); auto result = make_shared(app, WE_CALLED_REMOTE, socket); - result->mIP = ip; - result->mRemoteListeningPort = port; + result->mAddress = address; result->startIdleTimer(); - asio::ip::tcp::endpoint endpoint(asio::ip::address::from_string(ip), port); + asio::ip::tcp::endpoint endpoint( + asio::ip::address::from_string(address.getIP()), address.getPort()); socket->next_layer().async_connect( endpoint, [result](asio::error_code const& error) { asio::error_code ec; @@ -69,19 +71,15 @@ TCPPeer::accept(Application& app, shared_ptr socket) assertThreadIsMain(); shared_ptr result; asio::error_code ec; - auto ep = socket->next_layer().remote_endpoint(ec); - if (!ec) - { - asio::ip::tcp::no_delay nodelay(true); - socket->next_layer().set_option(nodelay, ec); - } + + asio::ip::tcp::no_delay nodelay(true); + socket->next_layer().set_option(nodelay, ec); if (!ec) { CLOG(DEBUG, "Overlay") << "TCPPeer:accept" << "@" << app.getConfig().PEER_PORT; result = make_shared(app, REMOTE_CALLED_US, socket); - result->mIP = ep.address().to_string(); result->startIdleTimer(); result->startRead(); } @@ -114,10 +112,19 @@ TCPPeer::~TCPPeer() } } -std::string -TCPPeer::getIP() +PeerBareAddress +TCPPeer::makeAddress(unsigned short remoteListeningPort) const { - return mIP; + asio::error_code ec; + auto ep = mSocket->next_layer().remote_endpoint(ec); + if (ec || remoteListeningPort <= 0 || remoteListeningPort > UINT16_MAX) + { + return PeerBareAddress{}; + } + else + { + return PeerBareAddress{ep.address().to_string(), remoteListeningPort}; + } } void diff --git a/src/overlay/TCPPeer.h b/src/overlay/TCPPeer.h index 73b7637d29..62d9f75720 100644 --- a/src/overlay/TCPPeer.h +++ b/src/overlay/TCPPeer.h @@ -26,7 +26,6 @@ class TCPPeer : public Peer typedef asio::buffered_stream SocketType; private: - std::string mIP; std::shared_ptr mSocket; std::vector mIncomingHeader; std::vector mIncomingBody; @@ -36,6 +35,9 @@ class TCPPeer : public Peer bool mDelayedShutdown{false}; bool mShutdownScheduled{false}; + PeerBareAddress + makeAddress(unsigned short remoteListeningPort) const override; + void recvMessage(); void sendMessage(xdr::msg_ptr&& xdrBytes) override; @@ -62,13 +64,11 @@ class TCPPeer : public Peer // `initiate` or // `accept` instead - static pointer initiate(Application& app, std::string const& ip, - unsigned short port); + static pointer initiate(Application& app, PeerBareAddress const& address); static pointer accept(Application& app, std::shared_ptr socket); virtual ~TCPPeer(); virtual void drop(bool force = true) override; - virtual std::string getIP() override; }; } diff --git a/src/overlay/TCPPeerTests.cpp b/src/overlay/TCPPeerTests.cpp index b033bea267..24c277c152 100644 --- a/src/overlay/TCPPeerTests.cpp +++ b/src/overlay/TCPPeerTests.cpp @@ -7,6 +7,7 @@ #include "main/Application.h" #include "main/Config.h" #include "overlay/OverlayManager.h" +#include "overlay/PeerBareAddress.h" #include "overlay/PeerDoor.h" #include "simulation/Simulation.h" #include "test/test.h" @@ -41,10 +42,10 @@ TEST_CASE("TCPPeer can communicate", "[overlay]") s->crankForAtLeast(std::chrono::seconds(1), false); auto p0 = n0->getOverlayManager().getConnectedPeer( - "127.0.0.1", n1->getConfig().PEER_PORT); + PeerBareAddress{"127.0.0.1", n1->getConfig().PEER_PORT}); auto p1 = n1->getOverlayManager().getConnectedPeer( - "127.0.0.1", n0->getConfig().PEER_PORT); + PeerBareAddress{"127.0.0.1", n0->getConfig().PEER_PORT}); REQUIRE(p0); REQUIRE(p1); diff --git a/src/simulation/Simulation.cpp b/src/simulation/Simulation.cpp index a9341ac512..4bfb8c8278 100644 --- a/src/simulation/Simulation.cpp +++ b/src/simulation/Simulation.cpp @@ -175,7 +175,7 @@ Simulation::dropConnection(NodeID initiator, NodeID acceptor) auto& cAcceptor = mNodes[acceptor].mApp->getConfig(); auto peer = iApp->getOverlayManager().getConnectedPeer( - "127.0.0.1", cAcceptor.PEER_PORT); + PeerBareAddress{"127.0.0.1", cAcceptor.PEER_PORT}); if (peer) { peer->drop(true); @@ -229,9 +229,8 @@ Simulation::addTCPConnection(NodeID initiator, NodeID acceptor) { throw runtime_error("PEER_PORT cannot be set to 0"); } - PeerRecord pr{"127.0.0.1", to->getConfig().PEER_PORT, - from->getClock().now()}; - from->getOverlayManager().connectTo(pr); + auto address = PeerBareAddress{"127.0.0.1", to->getConfig().PEER_PORT}; + from->getOverlayManager().connectTo(address); } void