diff --git a/CMakeLists.txt b/CMakeLists.txt index b3a402180cadc90d3bc2b8c186e0fd2ae4afa319..df83e7a07ebff06ba53a6b274b1c29fffc6c3131 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,7 @@ list (APPEND opendht_SOURCES src/securedht.cpp src/dhtrunner.cpp src/log.cpp + src/peer_discovery.cpp ) list (APPEND opendht_HEADERS @@ -172,6 +173,7 @@ list (APPEND opendht_HEADERS include/opendht/securedht.h include/opendht/log.h include/opendht/log_enable.h + include/opendht/peer_discovery.h include/opendht.h ) @@ -314,6 +316,8 @@ if (OPENDHT_TESTS) tests/cryptotester.cpp tests/dhtrunnertester.h tests/dhtrunnertester.cpp + tests/peerdiscoverytester.h + tests/peerdiscoverytester.cpp ) if (OPENDHT_PROXY_SERVER AND OPENDHT_PROXY_CLIENT) list (APPEND test_FILES diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index a377c5c326ed34d22e33867db3fab0cd56b24485..5b942760aadd825d1c09fae71bee55cf301db12a 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -40,6 +40,7 @@ namespace dht { struct Node; class SecureDht; +class PeerDiscovery; struct SecureDhtConfig; /** @@ -58,6 +59,8 @@ public: bool threaded; std::string proxy_server; std::string push_node_id; + bool peer_discovery; + bool peer_publish; }; DhtRunner(); @@ -259,6 +262,12 @@ public: */ void bootstrap(const std::string& host, const std::string& service); + /** + * Insert known nodes to the routing table, without necessarly ping them. + * Usefull to restart a node and get things running fast without putting load on the network. + */ + void bootstrap(const InfoHash& id, const SockAddr& address); + /** * Clear the list of bootstrap added using bootstrap(const std::string&, const std::string&). */ @@ -361,7 +370,9 @@ public: }, /*.threaded = */threaded, /*.proxy_server = */"", - /*.push_node_id = */"" + /*.push_node_id = */"", + /*.peer_discovery = */true, + /*.peer_publish = */true, }); } void run(in_port_t port, const Config& config); @@ -526,6 +537,12 @@ private: /** Push notification token */ std::string pushToken_; + + /** PeerDiscovery Parameters */ + std::unique_ptr<PeerDiscovery> peerDiscovery4_; + std::unique_ptr<PeerDiscovery> peerDiscovery6_; + const in_port_t port_multicast = 8888; + }; } diff --git a/include/opendht/peer_discovery.h b/include/opendht/peer_discovery.h new file mode 100644 index 0000000000000000000000000000000000000000..862f9c5fbacd36b125de87cd85238c3ae3731444 --- /dev/null +++ b/include/opendht/peer_discovery.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2014-2019 Savoir-faire Linux Inc. + * Author(s) : Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "sockaddr.h" +#include "infohash.h" + +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +#include <thread> +#include <mutex> +#include <condition_variable> + +namespace dht { + +class OPENDHT_PUBLIC PeerDiscovery +{ +public: + + using PeerDiscoveredCallback = std::function<void(const InfoHash&, const SockAddr&)>; + + PeerDiscovery(sa_family_t domain, in_port_t port); + ~PeerDiscovery(); + + /** + * startDiscovery - Keep Listening data from the sender until node is joinned or stop is called + */ + void startDiscovery(PeerDiscoveredCallback callback); + + /** + * startPublish - Keeping sending data until node is joinned or stop is called + */ + void startPublish(const dht::InfoHash &nodeId, in_port_t port_to_send); + + /** + * Thread Stopper + */ + void stop(); + + /** + * Configure the sockopt to be able to listen multicast group + */ + static void socketJoinMulticast(int sockfd, sa_family_t family); + + /** + * Join the threads + */ + void join(){ + + if(running_listen.joinable()){ running_listen.join(); }; + if(running_send.joinable()){ running_send.join(); }; + + } + +private: + std::mutex mtx_; + std::condition_variable cv_; + bool running_ {true}; + sa_family_t domain_ {AF_UNSPEC}; + int port_; + int sockfd_ {-1}; + int stop_writefd_ {-1}; + + SockAddr sockAddrSend_; + std::array<uint8_t,dht::InfoHash::size() + sizeof(in_port_t)> data_send_; + + //Thread export to be joined + std::thread running_listen; + std::thread running_send; + dht::InfoHash nodeId_; + + /** + * Multicast Socket Initialization, accept IPV4, IPV6 + */ + static int initialize_socket(sa_family_t domain); + + /** + * Send messages + */ + void sendTo(uint8_t *buf,size_t buf_size); + + /** + * Receive messages + */ + SockAddr recvFrom(uint8_t *buf, size_t &buf_size); + + /** + * Send thread loop + */ + void sender_thread(); + + /** + * Listener thread loop + */ + void listener_thread(PeerDiscoveredCallback callback); + + /** + * Listener Parameters Setup + */ + void listener_setup(); + + /** + * Sender Parameters Setup + */ + void sender_setup(const dht::InfoHash& nodeId, in_port_t port_to_send); + + /** + * Binary Converters + */ + static void inttolitend(uint32_t x, uint8_t *lit_int) { + lit_int[0] = (uint8_t)(x >> 0); + lit_int[1] = (uint8_t)(x >> 8); + } + + static uint16_t litendtoint(uint8_t *lit_int) { + return (uint32_t)lit_int[0] << 0 + | (uint32_t)lit_int[1] << 8; + } + +#ifdef _WIN32 + WSADATA wsaData; +#endif + +}; + +} diff --git a/include/opendht/sockaddr.h b/include/opendht/sockaddr.h index 7cfa5575bcfd5a722f0dcd67dccfc5584b284f6c..684c4a03d03b913dc991f91af3cdd9cbd9110450 100644 --- a/include/opendht/sockaddr.h +++ b/include/opendht/sockaddr.h @@ -23,6 +23,7 @@ #ifndef _WIN32 #include <sys/socket.h> #include <netinet/in.h> +#include <arpa/inet.h> #ifdef __ANDROID__ typedef uint16_t in_port_t; #endif @@ -90,6 +91,8 @@ public: static std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {}); + static SockAddr parse(sa_family_t family, const char* address); + bool operator<(const SockAddr& o) const { if (len != o.len) return len < o.len; @@ -146,6 +149,21 @@ public: addr->sa_family = af; } + /** + * Set Network Interface to any + */ + void setAny() { + auto family = getFamily(); + switch(family) { + case AF_INET: + getIPv4().sin_addr.s_addr = htonl(INADDR_ANY); + break; + case AF_INET6: + getIPv6().sin6_addr = in6addr_any; + break; + } + } + /** * Retreive the port (in host byte order) or 0 if the address is not * of a supported family. diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp index 8d24555aee72885eb2586616c74e5aa7c60bce7e..ae68fdf53b1503d07c1908ddaab5907a71277b69 100644 --- a/src/dhtrunner.cpp +++ b/src/dhtrunner.cpp @@ -20,6 +20,7 @@ #include "dhtrunner.h" #include "securedht.h" +#include "peer_discovery.h" #ifdef OPENDHT_PROXY_CLIENT #include "dht_proxy_client.h" @@ -147,6 +148,36 @@ DhtRunner::run(const SockAddr& local4, const SockAddr& local6, const DhtRunner:: cv.wait_until(lk, wakeup, hasJobToDo); } }); + + if (config.peer_discovery or config.peer_publish) { + peerDiscovery4_.reset(new PeerDiscovery(AF_INET, port_multicast)); + peerDiscovery6_.reset(new PeerDiscovery(AF_INET6, port_multicast)); + } + if (config.peer_discovery) { + using sig = void (DhtRunner::*)(const InfoHash&, const SockAddr&); + peerDiscovery4_->startDiscovery(std::bind(static_cast<sig>(&DhtRunner::bootstrap), + this, + std::placeholders::_1,std::placeholders::_2)); + peerDiscovery6_->startDiscovery(std::bind(static_cast<sig>(&DhtRunner::bootstrap), + this, + std::placeholders::_1,std::placeholders::_2)); + } + if (config.peer_publish) { + peerDiscovery4_->startPublish(getNodeId(),getBoundPort()); + peerDiscovery6_->startPublish(getNodeId(),getBoundPort()); + } +} + +void DhtRunner::bootstrap(const InfoHash& id, const SockAddr& address) +{ + { + std::unique_lock<std::mutex> lck(storage_mtx); + pending_ops_prio.emplace([id, address](SecureDht& dht) mutable { + std::cout<<address.getPort()<<std::endl; + dht.insertNode(id, address); + }); + } + cv.notify_all(); } void @@ -169,12 +200,21 @@ DhtRunner::join() running = false; cv.notify_all(); bootstrap_cv.notify_all(); + if (peerDiscovery4_) + peerDiscovery4_->stop(); + if (peerDiscovery6_) + peerDiscovery6_->stop(); + if (dht_thread.joinable()) dht_thread.join(); if (bootstrap_thread.joinable()) bootstrap_thread.join(); if (rcv_thread.joinable()) rcv_thread.join(); + if (peerDiscovery4_) + peerDiscovery4_->join(); + if (peerDiscovery6_) + peerDiscovery6_->join(); { std::lock_guard<std::mutex> lck(storage_mtx); @@ -592,7 +632,8 @@ DhtRunner::startNetwork(const SockAddr sin4, const SockAddr sin6) rc = recvfrom(s6, (char*)buf.data(), buf.size(), 0, (sockaddr*)&from, &from_len); else continue; - if (rc > 0) { + + if (rc > 0 && rc != static_cast<int>(getNodeId().size() + 2)) { { std::lock_guard<std::mutex> lck(sock_mtx); if (rcv.size() >= RX_QUEUE_MAX_SIZE) { @@ -974,6 +1015,8 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const std::shared_p void DhtRunner::resetDht() { + peerDiscovery4_.reset(); + peerDiscovery6_.reset(); #ifdef OPENDHT_PROXY_CLIENT listeners_.clear(); dht_via_proxy_.reset(); diff --git a/src/peer_discovery.cpp b/src/peer_discovery.cpp new file mode 100644 index 0000000000000000000000000000000000000000..972f265dfd3d857fbc08d49f1cbc17e94a48ffa8 --- /dev/null +++ b/src/peer_discovery.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2014-2019 Savoir-faire Linux Inc. + * Author(s) : Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "peer_discovery.h" + +#ifdef _WIN32 +#include <Ws2tcpip.h> // needed for ip_mreq definition for multicast +#include <Windows.h> +#else +#include <sys/types.h> +#endif +#include <fcntl.h> + +namespace dht { + +constexpr char MULTICAST_ADDRESS_IPV4[10] = "224.0.0.1"; +constexpr char MULTICAST_ADDRESS_IPV6[8] = "ff02::1"; + +#ifdef _WIN32 + +static bool +set_nonblocking(int fd, int nonblocking) +{ + unsigned long mode = !!nonblocking; + int rc = ioctlsocket(fd, FIONBIO, &mode); + return rc == 0; +} + +extern const char *inet_ntop(int, const void *, char *, socklen_t); + +#else + +static bool +set_nonblocking(int fd, int nonblocking) +{ + int rc = fcntl(fd, F_GETFL, 0); + if (rc < 0) + return false; + rc = fcntl(fd, F_SETFL, nonblocking?(rc | O_NONBLOCK):(rc & ~O_NONBLOCK)); + return rc >= 0; +} + +#endif + +PeerDiscovery::PeerDiscovery(sa_family_t domain, in_port_t port) + : domain_(domain), port_(port), sockfd_(initialize_socket(domain)) +{ + socketJoinMulticast(sockfd_, domain); +} + +int +PeerDiscovery::initialize_socket(sa_family_t domain) +{ + +#ifdef _WIN32 + // Initialize Windows Socket API with given VERSION. + if (WSAStartup(0x0101, &wsaData)) { + perror("WSAStartup"); + throw std::runtime_error(std::string("Socket Creation Error_initialize_socket ") + strerror(errno)); + } +#endif + + int sockfd = socket(domain, SOCK_DGRAM, 0); + if (sockfd < 0) { + throw std::runtime_error(std::string("Socket Creation Error: ") + strerror(errno)); + } + set_nonblocking(sockfd, 1); + return sockfd; +} + +void +PeerDiscovery::listener_setup() +{ + SockAddr sockAddrListen_; + sockAddrListen_.setFamily(domain_); + sockAddrListen_.setPort(port_); + sockAddrListen_.setAny(); + + unsigned int opt = 1; + if (setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, (char*) &opt, sizeof(opt)) < 0){ + throw std::runtime_error(std::string("Reusing ADDR failed: ") + strerror(errno)); + } + + // bind to receive address + if (bind(sockfd_, sockAddrListen_.get(), sockAddrListen_.getLength()) < 0){ + throw std::runtime_error(std::string("Bind Socket For Listener Error: ") + strerror(errno)); + } +} + +void +PeerDiscovery::socketJoinMulticast(int sockfd, sa_family_t family) +{ + switch (family) + { + case AF_INET:{ + + ip_mreq config_ipv4; + + //This option can be used to set the interface for sending outbound + //multicast datagrams from the sockets application. + config_ipv4.imr_interface.s_addr = htonl(INADDR_ANY); + if( setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &config_ipv4.imr_interface, sizeof( struct in_addr )) < 0 ) { + throw std::runtime_error(std::string("Bound Network Interface IPV4 Error: ") + strerror(errno)); + } + + //The IP_MULTICAST_TTL socket option allows the application to primarily + //limit the lifetime of the packet in the Internet and prevent it from circulating indefinitely + unsigned char ttl4 = 20; + if( setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl4, sizeof( ttl4 )) < 0 ) { + throw std::runtime_error(std::string(" TTL Sockopt Error: ") + strerror(errno)); + } + + // config the listener to be interested in joining in the multicast group + config_ipv4.imr_multiaddr.s_addr = inet_addr(MULTICAST_ADDRESS_IPV4); + config_ipv4.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&config_ipv4, sizeof(config_ipv4)) < 0){ + throw std::runtime_error(std::string(" Member Addition IPV4 Error: ") + strerror(errno)); + } + + break; + } + + case AF_INET6:{ + + ipv6_mreq config_ipv6; + + unsigned int outif = 0; + if( setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &outif, sizeof( outif )) < 0 ) { + throw std::runtime_error(std::string("Bound Network Interface IPV6 Error: ") + strerror(errno)); + } + + unsigned int ttl6 = 20; + if( setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl6, sizeof( ttl6 )) < 0 ) { + throw std::runtime_error(std::string("Hop Count Set Error: ") + strerror(errno)); + } + + config_ipv6.ipv6mr_interface = 0; + inet_pton(AF_INET6, MULTICAST_ADDRESS_IPV6, &config_ipv6.ipv6mr_multiaddr); + if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &config_ipv6, sizeof(config_ipv6)) < 0){ + throw std::runtime_error(std::string("Member Addition IPV6 Error: ") + strerror(errno)); + } + + break; + } + + } +} + +void +PeerDiscovery::sendTo(uint8_t *buf, size_t buf_size) +{ + ssize_t nbytes = sendto( + sockfd_, + buf, + buf_size, + 0, + sockAddrSend_.get(), + sockAddrSend_.getLength() + ); + if (nbytes < 0) { + throw std::runtime_error(std::string("Error sending packet: ") + strerror(errno)); + } +} + +SockAddr +PeerDiscovery::recvFrom(uint8_t *buf, size_t& buf_size) +{ + sockaddr_storage storeage_recv; + socklen_t sa_len = sizeof(storeage_recv); + + ssize_t nbytes = recvfrom( + sockfd_, + buf, + buf_size, + 0, + (sockaddr*)&storeage_recv, + &sa_len + ); + if (nbytes < 0) { + throw std::runtime_error(std::string("Error receiving packet: ") + strerror(errno)); + } + + buf_size = nbytes; + SockAddr ret {storeage_recv, sa_len}; + return ret; +} + +void +PeerDiscovery::sender_setup(const dht::InfoHash& nodeId, in_port_t port_to_send) +{ + nodeId_ = nodeId; + //Set up for Sender + sockAddrSend_ = SockAddr::parse(domain_, domain_ == AF_INET ? MULTICAST_ADDRESS_IPV4 : MULTICAST_ADDRESS_IPV6); + sockAddrSend_.setPort(port_); + + //Setup for send data + int port_node = port_to_send; + uint8_t port_node_binary[2]; + PeerDiscovery::inttolitend(port_node,port_node_binary); + + //Copy Node id and node port + memcpy (data_send_.data(), nodeId.data(), nodeId.size()); + data_send_[InfoHash::size()] = port_node_binary[0]; + data_send_[InfoHash::size() + 1] = port_node_binary[1]; +} + +void +PeerDiscovery::sender_thread() +{ + while(true) { + sendTo(data_send_.data(), data_send_.size()); + { + std::unique_lock<std::mutex> lck(mtx_); + if (cv_.wait_for(lck,std::chrono::seconds(3),[&]{ return !running_; })) + break; + } + } +} + +void +PeerDiscovery::listener_thread(PeerDiscoveredCallback callback) +{ + int stopfds_pipe[2]; +#ifndef _WIN32 + auto status = pipe(stopfds_pipe); + if (status == -1) { + throw std::runtime_error(std::string("Can't open pipe: ") + strerror(errno)); + } +#else + udpPipe(stopfds_pipe); +#endif + int stop_readfd = stopfds_pipe[0]; + stop_writefd_ = stopfds_pipe[1]; + + while(true) { + fd_set readfds; + + FD_ZERO(&readfds); + FD_SET(stop_readfd, &readfds); + FD_SET(sockfd_, &readfds); + + int data_coming = select(sockfd_ > stop_readfd ? sockfd_ + 1 : stop_readfd + 1, &readfds, nullptr, nullptr, nullptr); + + { + std::unique_lock<std::mutex> lck(mtx_); + if (not running_) + break; + } + + if (data_coming < 0) { + if(errno != EINTR) { + perror("Select Error"); + std::this_thread::sleep_for( std::chrono::seconds(1) ); + } + } + + if (data_coming > 0) { + + if(FD_ISSET(stop_readfd, &readfds)){ break; } + + std::array<uint8_t,dht::InfoHash::size() + sizeof(in_port_t)> data_receive; + size_t data_receive_size = data_receive.size(); + auto from = recvFrom(data_receive.data(), data_receive_size); + + //Data_receive_size as a value-result member will hlep to filter packs + if(data_receive_size != data_receive.size()){ + perror("Data Received Unmatch"); + continue; + } + + std::array<uint8_t,dht::InfoHash::size()> data_infohash; + uint8_t data_port[2]; + + memcpy (data_infohash.data(), data_receive.data(), dht::InfoHash::size()); + data_port[0] = data_receive[dht::InfoHash::size()]; + data_port[1] = data_receive[dht::InfoHash::size() + 1]; + + auto port = PeerDiscovery::litendtoint(data_port); + auto nodeId = dht::InfoHash(data_infohash.data(), dht::InfoHash::size()); + + if (nodeId != nodeId_){ + from.setPort(port); + callback(nodeId, from); + } + } + } + if (stop_readfd != -1) + close(stop_readfd); + if (stop_writefd_ != -1) { + close(stop_writefd_); + stop_writefd_ = -1; + } +} + +void +PeerDiscovery::startDiscovery(PeerDiscoveredCallback callback) +{ + listener_setup(); + running_listen = std::thread(&PeerDiscovery::listener_thread, this, callback); +} + +void +PeerDiscovery::startPublish(const dht::InfoHash &nodeId, in_port_t port_to_send) +{ + sender_setup(nodeId, port_to_send); + running_send = std::thread(&PeerDiscovery::sender_thread, this); +} + +void +PeerDiscovery::stop() +{ + { + std::unique_lock<std::mutex> lck(mtx_); + running_ = false; + } + cv_.notify_one(); + if (stop_writefd_ != -1) { + + if (write(stop_writefd_, "\0", 1) == -1) { + perror("write"); + } + } +} + +PeerDiscovery::~PeerDiscovery() +{ + if (sockfd_ != -1) + close(sockfd_); + +#ifdef _WIN32 + WSACleanup(); +#endif +} + +} diff --git a/src/utils.cpp b/src/utils.cpp index 0b75ac48d28e3a834118f15cc0012e37af6666b8..d5c55049ba88ab831232b09353e55d911cfb3501 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -74,6 +74,18 @@ SockAddr::resolve(const std::string& host, const std::string& service) } +SockAddr +SockAddr::parse(sa_family_t family, const char* address) +{ + SockAddr addr; + addr.setFamily(family); + if (inet_pton(family, address, family == AF_INET ? (void*)&addr.getIPv4().sin_addr.s_addr : (void*)&addr.getIPv6().sin6_addr) <= 0){ + throw std::runtime_error("SockAddr::parse inet_pton"); + } + return addr; +} + + std::string print_addr(const sockaddr* sa, socklen_t slen) { diff --git a/tests/peerdiscoverytester.cpp b/tests/peerdiscoverytester.cpp new file mode 100644 index 0000000000000000000000000000000000000000..42e4ad28a183fb396ea8712103244df5c91dcfba --- /dev/null +++ b/tests/peerdiscoverytester.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 Savoir-faire Linux Inc. + * + * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "peerdiscoverytester.h" + +namespace test { + +CPPUNIT_TEST_SUITE_REGISTRATION(PeerDiscoveryTester); + +void PeerDiscoveryTester::setUp(){} + +void PeerDiscoveryTester::testTransmission_ipv4(){ + + // Node for getnode id + dht::InfoHash data_n = dht::InfoHash::get("applepin"); + int port = 2222; + in_port_t port_n = 50000;; + + dht::PeerDiscovery test_n(AF_INET, port); + dht::PeerDiscovery test_s(AF_INET, port); + + test_s.startDiscovery([&](const dht::InfoHash& node, const dht::SockAddr& addr){ + CPPUNIT_ASSERT_EQUAL(data_n, node); + CPPUNIT_ASSERT_EQUAL(port_n, addr.getPort()); + }); + + test_n.startPublish(data_n, port_n); + + sleep(5); + test_n.stop(); + test_s.stop(); + test_n.join(); + test_s.join(); +} + +/*void PeerDiscoveryTester::testTransmission_ipv6(){ + + // Node for getnode id + dht::InfoHash data_n = dht::InfoHash::get("applepin"); + int port = 2222; + int port_n = 50000;; + + dht::PeerDiscovery test_n(AF_INET6,port); + dht::PeerDiscovery test_s(AF_INET6,port); + + test_s.startDiscovery([&](const dht::InfoHash& node, const dht::SockAddr& addr){ + + CPPUNIT_ASSERT_MESSAGE("Data Receive Incorrect", memcmp(node.data(),data_n.data(),dht::InfoHash::size()) == 0 ); + CPPUNIT_ASSERT_MESSAGE("Port Receive Incorrect", addr.getPort() == 50000); + + }); + + test_n.startPublish(data_n,port_n); + + sleep(5); + test_n.stop(); + test_s.stop(); + test_n.join(); + test_s.join(); + +}*/ + +void PeerDiscoveryTester::tearDown(){} + +} // namespace test \ No newline at end of file diff --git a/tests/peerdiscoverytester.h b/tests/peerdiscoverytester.h new file mode 100644 index 0000000000000000000000000000000000000000..3d5438cc453cb16c58f258116297143c8c6ed9bd --- /dev/null +++ b/tests/peerdiscoverytester.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 Savoir-faire Linux Inc. + * + * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "opendht/peer_discovery.h" + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +namespace test { + +class PeerDiscoveryTester : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE(PeerDiscoveryTester); + CPPUNIT_TEST(testTransmission_ipv4); + // CPPUNIT_TEST(testTransmission_ipv6); + CPPUNIT_TEST_SUITE_END(); + + public: + /** + * Method automatically called before each test by CppUnit + */ + void setUp(); + /** + * Method automatically called after each test CppUnit + */ + void tearDown(); + /** + * Test Multicast Transmission Ipv4 + */ + void testTransmission_ipv4(); + /** + * Test Multicast Transmission Ipv6 + */ + //void testTransmission_ipv6(); + +}; + +} // namespace test