From 7871a4f4dd3155575e21fd226007eef7ab6b5234 Mon Sep 17 00:00:00 2001 From: study-faraphel Date: Sun, 17 Nov 2024 14:21:49 +0100 Subject: [PATCH] simplified some constructor syntax, made the "id" value of a peer not required for the program to work correctly (id will be removed later) --- CMakeLists.txt | 5 ++ source/Context.hpp | 21 ++++--- source/Manager.cpp | 29 ++++----- source/Peer.cpp | 21 +++++++ source/Peer.hpp | 30 ++++++++++ source/RemotePeer.cpp | 15 +++++ source/RemotePeer.hpp | 31 ++++------ source/behavior/events/info/InfoEvent.cpp | 27 +++++---- .../tasks/undefined/UndefinedTask.cpp | 24 ++++---- source/utils/network.cpp | 60 +++++++++++++++++++ source/utils/network.hpp | 19 ++++++ 11 files changed, 215 insertions(+), 67 deletions(-) create mode 100644 source/Peer.cpp create mode 100644 source/Peer.hpp create mode 100644 source/RemotePeer.cpp create mode 100644 source/utils/network.cpp create mode 100644 source/utils/network.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 17b4dd1..afb3f96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,11 @@ add_executable(M2-PT-DRP source/packets/info/InfoPacketData.hpp source/utils/time/Chrony.cpp source/utils/time/Chrony.hpp + source/Peer.hpp + source/utils/network.cpp + source/utils/network.hpp + source/Peer.cpp + source/RemotePeer.cpp ) target_include_directories(M2-PT-DRP PRIVATE source diff --git a/source/Context.hpp b/source/Context.hpp index 691a566..b44cf94 100644 --- a/source/Context.hpp +++ b/source/Context.hpp @@ -1,7 +1,7 @@ #pragma once +#include #include -#include #include #include "RemotePeer.hpp" @@ -13,14 +13,19 @@ */ class Context { public: - int socket = -1; /// current socket file descriptor, used to communicate - addrinfo* broadcastAddressInfo = nullptr; /// address used to broadcast messages - std::shared_ptr server = nullptr; /// peer currently used as the server + explicit Context() { + this->socket = -1; + this->broadcastAddressInfo = nullptr; + this->server = nullptr; + + this->latestPeerDiscovery = std::chrono::high_resolution_clock::now(); + } + + int socket; /// current socket file descriptor, used to communicate + addrinfo* broadcastAddressInfo; /// address used to broadcast messages + std::shared_ptr server; /// peer currently used as the server Peer me; /// information about our own machine - std::map< - std::uint32_t, - std::shared_ptr - > remotePeers {}; /// information about other machines + std::list> remotePeers {}; /// information about other machines std::chrono::high_resolution_clock::time_point latestPeerDiscovery; /// time of the latest discovered machine }; diff --git a/source/Manager.cpp b/source/Manager.cpp index bb53854..1f52ef8 100644 --- a/source/Manager.cpp +++ b/source/Manager.cpp @@ -57,7 +57,7 @@ Manager::Manager(const std::string& address, const std::string& port, const bool throw std::runtime_error("Could not create the socket: " + std::string(strerror(errno))); // allow IPv6 multicast loopback so that we can receive our own messages. - int socketLoopback = 1; + const int socketLoopback = 1; if (setsockopt( context.socket, IPPROTO_IPV6, @@ -102,21 +102,8 @@ Manager::Manager(const std::string& address, const std::string& port, const bool ) < 0) throw std::runtime_error("Could not bind to the address: " + std::string(strerror(errno))); - // generate a random identifier for ourselves - std::random_device randomDevice; - std::mt19937 randomGenerator(randomDevice()); - - std::uniform_int_distribution distribution( - 1, - std::numeric_limits::max() - ); - this->context.me.id = distribution(randomGenerator); - // TODO(Faraphel): should only be enabled in specific case. this->context.me.serverEnabled = true; - - // define the time of the latest discovery - this->context.latestPeerDiscovery = std::chrono::high_resolution_clock::now(); } @@ -177,8 +164,22 @@ void Manager::loopReceiver() { continue; // decrypt the packet + // TODO(Faraphel): handle exception ? packetContent = packet.getContent(); + // look for a saved peer with the same address + auto remotePeer = std::ranges::find_if( + this->context.remotePeers, + [&](const std::shared_ptr& remotePeer) { return + remotePeer->addressLength == fromAddressLength and + std::memcmp(&fromAddress, &remotePeer->address, fromAddressLength) == 0; + } + ); + // if found, update the latest connection date + if (remotePeer != this->context.remotePeers.end()) { + (*remotePeer)->latestConnection = std::chrono::high_resolution_clock::now(); + } + // get the corresponding event class std::shared_ptr event; try { diff --git a/source/Peer.cpp b/source/Peer.cpp new file mode 100644 index 0000000..88aef33 --- /dev/null +++ b/source/Peer.cpp @@ -0,0 +1,21 @@ +#include "Peer.hpp" + + +Peer::Peer() { + this->id = randomDistribution(randomGenerator); + this->channel = 0; + this->serverEnabled = false; + this->status = drp::task::TaskType::UNDEFINED; + + this->latencyAverage = std::chrono::high_resolution_clock::duration::max(); +} + + +std::random_device Peer::randomDevice = std::random_device(); + +std::mt19937 Peer::randomGenerator = std::mt19937(randomDevice()); + +std::uniform_int_distribution Peer::randomDistribution = std::uniform_int_distribution( + 1, + std::numeric_limits::max() +); \ No newline at end of file diff --git a/source/Peer.hpp b/source/Peer.hpp new file mode 100644 index 0000000..615e84a --- /dev/null +++ b/source/Peer.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +#include "behavior/tasks/types.hpp" + + +/** + * Contains common information about a certain peer. + */ +class Peer { +public: + explicit Peer(); + + std::uint32_t id; + + bool serverEnabled; + drp::task::TaskType status; + std::uint8_t channel; + + std::chrono::high_resolution_clock::duration latencyAverage {}; + +private: + // random + static std::random_device randomDevice; + static std::mt19937 randomGenerator; + static std::uniform_int_distribution randomDistribution; +}; diff --git a/source/RemotePeer.cpp b/source/RemotePeer.cpp new file mode 100644 index 0000000..9bc32f2 --- /dev/null +++ b/source/RemotePeer.cpp @@ -0,0 +1,15 @@ +#include "RemotePeer.hpp" + + +RemotePeer::RemotePeer(const sockaddr_storage& address, const socklen_t addressLength, const Peer& peer) { + this->address = address; + this->addressLength = addressLength; + this->information = peer; + + this->latestConnection = std::chrono::system_clock::now(); + this->latency = std::chrono::high_resolution_clock::duration::max(); +} + +void RemotePeer::update(const Peer& peer) { + this->information = peer; +} diff --git a/source/RemotePeer.hpp b/source/RemotePeer.hpp index 8a39364..8ca9252 100644 --- a/source/RemotePeer.hpp +++ b/source/RemotePeer.hpp @@ -3,35 +3,24 @@ #include #include -#include "behavior/tasks/types.hpp" - - -// TODO(Faraphel): should be split in multiple files. - - -/** - * Contains common information about a certain peer. - */ -struct Peer { - std::uint32_t id = 0; - - bool serverEnabled = false; - drp::task::TaskType status = drp::task::TaskType::UNDEFINED; - std::uint8_t channel = 0; - - std::chrono::high_resolution_clock::duration latencyAverage = std::chrono::high_resolution_clock::duration::max(); -}; +#include "Peer.hpp" /** * Contains information about a distant peer. */ -struct RemotePeer { +class RemotePeer { +public: + explicit RemotePeer(const sockaddr_storage& address, socklen_t addressLength, const Peer& peer); + + void update(const Peer& peer); + // communication sockaddr_storage address {}; - socklen_t addressLength = 0; + socklen_t addressLength; - std::chrono::high_resolution_clock::duration latency = std::chrono::high_resolution_clock::duration::max(); + std::chrono::high_resolution_clock::time_point latestConnection; + std::chrono::high_resolution_clock::duration latency{}; // information Peer information; diff --git a/source/behavior/events/info/InfoEvent.cpp b/source/behavior/events/info/InfoEvent.cpp index 5291fee..05d1264 100644 --- a/source/behavior/events/info/InfoEvent.cpp +++ b/source/behavior/events/info/InfoEvent.cpp @@ -19,29 +19,32 @@ void InfoEvent::handle( // get the peer information const auto packetData = packet::info::InfoPacketData::fromGeneric(content); - const Peer peer = packetData.peer; + const Peer packetPeer = packetData.peer; // check if the peer address is already in the map - const auto iterator = context.remotePeers.find(peer.id); + const auto itRemotePeer = std::ranges::find_if( + context.remotePeers, + [&](const auto& remotePeer) { + return remotePeer->information.id == packetPeer.id; + } + ); - std::shared_ptr remotePeer; - if (iterator == context.remotePeers.end()) { + // if not found, create a new remote peer. + if (itRemotePeer == context.remotePeers.end()) { // if not found, create a new peer - remotePeer = std::make_shared(); - remotePeer->address = fromAddress; - remotePeer->addressLength = fromAddressLength; + const auto remotePeer = std::make_shared(fromAddress, fromAddressLength, packetPeer); + // register it in the peer list + context.remotePeers.push_back(remotePeer); // update the latest discovery time context.latestPeerDiscovery = std::chrono::high_resolution_clock::now(); } else { // get the peer - remotePeer = iterator->second; + const auto& remotePeer = *itRemotePeer; + // update the peer information + remotePeer->update(packetPeer); } // TODO(Faraphel): interpret the timestamp and calculate average ping - - // save it in the peers list - remotePeer->information = peer; - context.remotePeers[peer.id] = remotePeer; } diff --git a/source/behavior/tasks/undefined/UndefinedTask.cpp b/source/behavior/tasks/undefined/UndefinedTask.cpp index c5d4fda..8f6c613 100644 --- a/source/behavior/tasks/undefined/UndefinedTask.cpp +++ b/source/behavior/tasks/undefined/UndefinedTask.cpp @@ -4,15 +4,16 @@ #include #include #include -#include #include #include #include +#include #include "Context.hpp" #include "packets/base/Packet.hpp" #include "packets/base/SecurityMode.hpp" #include "packets/search/SearchPacketData.hpp" +#include "utils/network.hpp" namespace drp::task { @@ -20,22 +21,21 @@ namespace drp::task { void UndefinedTask::handle(Context& context) { std::cout << "[Task - Undefined] List of peers: " << std::endl; - for (const auto& peer : context.remotePeers) + for (const auto& remotePeer : context.remotePeers) std::cout << - "\tPeer(id=" << peer.second->information.id << ", " << - "status=" << std::to_string(static_cast(peer.second->information.status)) << ")" << + "\tPeer(id=" << remotePeer->information.id << ", " << + "status=" << std::to_string(static_cast(remotePeer->information.status)) << ")" << std::endl; // search if a server is available among the peer. const auto& server = std::ranges::find_if( context.remotePeers, - [&](const auto& peer) { return peer.second->information.status == TaskType::SERVER; } + [&](const auto& peer) { return peer->information.status == TaskType::SERVER; } ); - // if a server have been found, use it if (server != context.remotePeers.end()) { // if a server have been found, use it - context.server = server->second; + context.server = *server; context.me.status = TaskType::CLIENT; return; } @@ -51,21 +51,21 @@ void UndefinedTask::handle(Context& context) { // TODO(Faraphel): should use the machine with the lowest average ping if (context.me.serverEnabled) { // find the remote peer with the highest id that can be a server - const auto serverCandidate = std::ranges::max_element( + std::shared_ptr serverCandidate = *std::ranges::max_element( context.remotePeers, [&](auto& remotePeer1, auto& remotePeer2) { return ( - (remotePeer1.second->information.serverEnabled ? remotePeer1.first : 0) < - (remotePeer2.second->information.serverEnabled ? remotePeer2.first : 0) + (remotePeer1->information.serverEnabled ? remotePeer1->information.id : 0) < + (remotePeer2->information.serverEnabled ? remotePeer2->information.id : 0) ); } ); // check if we are this peer - if (context.me.id == serverCandidate->first) { + if (util::network::is_localhost(serverCandidate->address, serverCandidate->addressLength)) { std::cout << "[Task - Undefined] Becoming server..." << std::endl; // set ourselves as the server - context.server = serverCandidate->second; + context.server = serverCandidate; context.me.status = TaskType::SERVER; return; } diff --git a/source/utils/network.cpp b/source/utils/network.cpp new file mode 100644 index 0000000..1b7a3fb --- /dev/null +++ b/source/utils/network.cpp @@ -0,0 +1,60 @@ +#include "network.hpp" + +#include +#include +#include +#include + + +namespace drp::util::network { + + +bool is_localhost(const sockaddr_storage& address, const socklen_t addressLength) { + // iterate through all the interfaces + ifaddrs* interfaces; + if (getifaddrs(&interfaces) == -1) + throw std::runtime_error("Could not get network interfaces."); + + bool result = false; + for (const ifaddrs *interface = interfaces; interface; interface = interface->ifa_next) { + // if the interface family does not correspond to the server candidate, ignore it + if (interface->ifa_addr->sa_family != address.ss_family) + continue; + + switch (address.ss_family) { + case AF_INET: { + const auto interfaceIpv4 = reinterpret_cast(interface->ifa_addr); + const auto addressIpv4 = reinterpret_cast(&address); + + // the family have already been checked. + if (std::memcmp(&interfaceIpv4->sin_addr, &addressIpv4->sin_addr, sizeof(in_addr)) == 0) + result = true; + + break; + } + + case AF_INET6: { + const auto interfaceIpv6 = reinterpret_cast(interface->ifa_addr); + const auto addressIpv6 = reinterpret_cast(&address); + + // the interface and the family have already been checked. + if (std::memcmp(&interfaceIpv6->sin6_addr, &addressIpv6->sin6_addr, sizeof(in6_addr)) == 0) + result = true; + + break; + } + + default: + throw std::runtime_error("Unknown address family."); + } + + if (result == true) + break; + } + + freeifaddrs(interfaces); + return result; +} + + +} diff --git a/source/utils/network.hpp b/source/utils/network.hpp new file mode 100644 index 0000000..3ca213f --- /dev/null +++ b/source/utils/network.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + + +namespace drp::util::network { + + +/** + * Indicate if an address refer to the local machine. + * @param address the address to check. + * @param addressLength the length of the address. + * @return True if the address refer to ourselves, False otherwise. + */ +bool is_localhost(const sockaddr_storage& address, socklen_t addressLength); + + +}