Skip to content

Commit 4953f30

Browse files
Igor EgorovxDimon
Igor Egorov
andauthored
Dnsaddr support (#105)
Signed-off-by: Igor Egorov <[email protected]> Co-authored-by: Dmitriy Khaustov aka xDimon <[email protected]>
1 parent 4a5963f commit 4953f30

23 files changed

+755
-7
lines changed

cmake/dependencies.cmake

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ find_package(OpenSSL REQUIRED)
2525
hunter_add_package(Protobuf)
2626
find_package(Protobuf CONFIG REQUIRED)
2727

28+
find_package(Threads)
29+
30+
hunter_add_package(c-ares)
31+
find_package(c-ares CONFIG REQUIRED)
32+
2833
# https://docs.hunter.sh/en/latest/packages/pkg/spdlog.html
2934
hunter_add_package(spdlog)
3035
find_package(spdlog CONFIG REQUIRED)

example/04-dnstxt/CMakeLists.txt

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#
2+
# Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
6+
add_executable(example_ares_resolver
7+
ares_resolver.cpp
8+
)
9+
target_link_libraries(example_ares_resolver
10+
Boost::Boost.DI
11+
p2p_basic_host
12+
p2p_peer_repository
13+
p2p_inmem_address_repository
14+
p2p_inmem_key_repository
15+
p2p_inmem_protocol_repository
16+
p2p_default_network
17+
p2p_cares
18+
)

example/04-dnstxt/ares_resolver.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include <iostream>
7+
#include <memory>
8+
9+
#include <libp2p/common/logger.hpp>
10+
#include <libp2p/injector/host_injector.hpp>
11+
#include <libp2p/network/cares/cares.hpp>
12+
#include <libp2p/outcome/outcome.hpp>
13+
14+
int main(int argc, char *argv[]) {
15+
libp2p::network::c_ares::Ares ares;
16+
spdlog::set_level(spdlog::level::trace);
17+
18+
// create a default Host via an injector
19+
auto injector = libp2p::injector::makeHostInjector();
20+
21+
// create io_context - in fact, thing, which allows us to execute async
22+
// operations
23+
auto context = injector.create<std::shared_ptr<boost::asio::io_context>>();
24+
// the guard to preserve context's running state when tasks queue is empty
25+
auto work_guard = boost::asio::make_work_guard(*context);
26+
context->post([&] {
27+
ares.resolveTxt(
28+
"_dnsaddr.bootstrap.libp2p.io", context,
29+
[&](libp2p::outcome::result<std::vector<std::string>> result) -> void {
30+
if (result.has_error()) {
31+
std::cout << result.error().message() << std::endl;
32+
} else {
33+
auto &&val = result.value();
34+
for (auto &record : val) {
35+
std::cout << record << std::endl;
36+
}
37+
}
38+
// work guard is used only for example purposes.
39+
// normal conditions for libp2p imply the main context is running
40+
// and serving listeners or something at the moment
41+
work_guard.reset();
42+
});
43+
});
44+
// run the IO context
45+
try {
46+
context->run();
47+
} catch (const boost::system::error_code &ec) {
48+
std::cout << "Example cannot run: " << ec.message() << std::endl;
49+
} catch (...) {
50+
std::cout << "Unknown error happened" << std::endl;
51+
}
52+
}

example/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
add_subdirectory(01-echo)
77
add_subdirectory(02-kademlia)
88
add_subdirectory(03-gossip)
9+
add_subdirectory(04-dnstxt)

include/libp2p/injector/network_injector.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <libp2p/muxer/yamux.hpp>
2424
#include <libp2p/network/impl/connection_manager_impl.hpp>
2525
#include <libp2p/network/impl/dialer_impl.hpp>
26+
#include <libp2p/network/impl/dnsaddr_resolver_impl.hpp>
2627
#include <libp2p/network/impl/listener_manager_impl.hpp>
2728
#include <libp2p/network/impl/network_impl.hpp>
2829
#include <libp2p/network/impl/router_impl.hpp>
@@ -273,6 +274,7 @@ namespace libp2p::injector {
273274
di::bind<security::secio::ExchangeMessageMarshaller>().template to<security::secio::ExchangeMessageMarshallerImpl>(),
274275

275276
// internal
277+
di::bind<network::DnsaddrResolver>().template to <network::DnsaddrResolverImpl>(),
276278
di::bind<network::Router>().template to<network::RouterImpl>(),
277279
di::bind<network::ConnectionManager>().template to<network::ConnectionManagerImpl>(),
278280
di::bind<network::ListenerManager>().template to<network::ListenerManagerImpl>(),
+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef LIBP2P_INCLUDE_LIBP2P_NETWORK_CARES_CARES_HPP
7+
#define LIBP2P_INCLUDE_LIBP2P_NETWORK_CARES_CARES_HPP
8+
9+
#include <atomic>
10+
#include <functional>
11+
#include <list>
12+
#include <map>
13+
#include <memory>
14+
#include <string>
15+
#include <vector>
16+
17+
#include <ares.h>
18+
#include <arpa/nameser.h>
19+
#include <sys/select.h>
20+
#include <boost/asio.hpp>
21+
#include <boost/optional.hpp>
22+
#include <libp2p/common/logger.hpp>
23+
#include <libp2p/outcome/outcome.hpp>
24+
25+
namespace libp2p::network::c_ares {
26+
27+
/**
28+
*
29+
* Only one instance is allowed to exist.
30+
* Has to be initialized prior any threads spawn.
31+
* Designed for use only via Boost injector passing by a reference.
32+
*/
33+
class Ares final {
34+
public:
35+
using TxtCallback =
36+
std::function<void(outcome::result<std::vector<std::string>>)>;
37+
38+
enum class Error {
39+
NOT_INITIALIZED = 1,
40+
CHANNEL_INIT_FAILURE,
41+
THREAD_FAILED,
42+
// the following are the codes returned to callback by ::ares_query
43+
E_NO_DATA,
44+
E_BAD_QUERY,
45+
E_SERVER_FAIL,
46+
E_NOT_FOUND,
47+
E_SERVER_NOTIMP,
48+
E_REFUSED,
49+
E_BAD_NAME,
50+
E_QUERY_TIMEOUT,
51+
E_NS_CONN_REFUSED,
52+
E_NO_MEM,
53+
E_CANCELLED,
54+
E_CHANNEL_DESTROYED,
55+
E_BAD_RESPONSE,
56+
};
57+
58+
static inline const std::map<int, Ares::Error> kQueryErrors = {
59+
{ARES_ENODATA, Error::E_NO_DATA},
60+
{ARES_EFORMERR, Error::E_BAD_QUERY},
61+
{ARES_ESERVFAIL, Error::E_SERVER_FAIL},
62+
{ARES_ENOTFOUND, Error::E_NOT_FOUND},
63+
{ARES_ENOTIMP, Error::E_SERVER_NOTIMP},
64+
{ARES_EREFUSED, Error::E_REFUSED},
65+
{ARES_EBADNAME, Error::E_BAD_NAME},
66+
{ARES_ETIMEOUT, Error::E_QUERY_TIMEOUT},
67+
{ARES_ECONNREFUSED, Error::E_NS_CONN_REFUSED},
68+
{ARES_ENOMEM, Error::E_NO_MEM},
69+
{ARES_ECANCELLED, Error::E_CANCELLED},
70+
{ARES_EDESTRUCTION, Error::E_CHANNEL_DESTROYED},
71+
{ARES_EBADRESP, Error::E_BAD_RESPONSE},
72+
};
73+
74+
Ares();
75+
~Ares();
76+
77+
// make it non-copyable
78+
Ares(const Ares &) = delete;
79+
Ares(Ares &&) = delete;
80+
void operator=(const Ares &) = delete;
81+
void operator=(Ares &&) = delete;
82+
83+
static void resolveTxt(
84+
const std::string &uri,
85+
const std::weak_ptr<boost::asio::io_context> &io_context,
86+
TxtCallback callback);
87+
88+
private:
89+
struct RequestContext {
90+
std::weak_ptr<boost::asio::io_context> io_context;
91+
std::string uri;
92+
TxtCallback callback;
93+
94+
RequestContext(std::weak_ptr<boost::asio::io_context> io_context_,
95+
std::string uri_, TxtCallback callback_)
96+
: io_context{std::move(io_context_)},
97+
uri{std::move(uri_)},
98+
callback{std::move(callback_)} {}
99+
};
100+
101+
/// schedules to user's io_context the call of callback with specified error
102+
static void reportError(
103+
const std::weak_ptr<boost::asio::io_context> &io_context,
104+
TxtCallback callback, Error error);
105+
106+
static void txtCallback(void *arg, int status, int timeouts,
107+
unsigned char *abuf, int alen);
108+
109+
/// does ares sockets processing for the channel, to be in a separate thread
110+
static void waitAresChannel(::ares_channel channel);
111+
112+
static void removeRequest(RequestContext *request_ptr);
113+
114+
static std::atomic_bool initialized_;
115+
static std::list<std::shared_ptr<RequestContext>> requests_;
116+
static common::Logger log_;
117+
};
118+
119+
} // namespace libp2p::network::c_ares
120+
121+
OUTCOME_HPP_DECLARE_ERROR(libp2p::network::c_ares, Ares::Error);
122+
123+
#endif // LIBP2P_INCLUDE_LIBP2P_NETWORK_CARES_CARES_HPP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef LIBP2P_INCLUDE_LIBP2P_NETWORK_DNSADDR_RESOLVER_HPP
7+
#define LIBP2P_INCLUDE_LIBP2P_NETWORK_DNSADDR_RESOLVER_HPP
8+
9+
#include <functional>
10+
#include <vector>
11+
12+
#include <libp2p/multi/multiaddress.hpp>
13+
#include <libp2p/outcome/outcome.hpp>
14+
#include <libp2p/peer/peer_info.hpp>
15+
16+
namespace libp2p::network {
17+
18+
/**
19+
* Utility for peers discovering through bootstrap addresses like
20+
* /dnsaddr/<hostname>
21+
*/
22+
class DnsaddrResolver {
23+
public:
24+
virtual ~DnsaddrResolver() = default;
25+
26+
/**
27+
* Callback to dnsaddr resolution request.
28+
* @param a set of discovered addresses
29+
*/
30+
using AddressesCallback =
31+
std::function<void(outcome::result<std::vector<multi::Multiaddress>>)>;
32+
33+
/**
34+
* Makes a query for peers info to a specified bootstrap address via
35+
* /dnsaddr
36+
* @param address - bootstrap address to query for peers
37+
* @param cb - a callback to fed the discovered peers to
38+
*/
39+
virtual void load(multi::Multiaddress address, AddressesCallback callback) = 0;
40+
};
41+
} // namespace libp2p::network
42+
43+
#endif // LIBP2P_INCLUDE_LIBP2P_NETWORK_DNSADDR_RESOLVER_HPP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef LIBP2P_INCLUDE_LIBP2P_NETWORK_IMPL_DNSADDR_RESOLVER_IMPL_HPP
7+
#define LIBP2P_INCLUDE_LIBP2P_NETWORK_IMPL_DNSADDR_RESOLVER_IMPL_HPP
8+
9+
#include <libp2p/network/dnsaddr_resolver.hpp>
10+
11+
#include <memory>
12+
#include <string>
13+
14+
#include <boost/asio.hpp>
15+
#include <libp2p/network/cares/cares.hpp>
16+
17+
namespace libp2p::network {
18+
19+
class DnsaddrResolverImpl : public DnsaddrResolver {
20+
public:
21+
static constexpr auto kDnsaddr = multi::Protocol::Code::DNS_ADDR;
22+
23+
enum class Error {
24+
INVALID_DNSADDR = 1,
25+
MALFORMED_RESPONSE,
26+
BAD_ADDR_IN_RESPONSE,
27+
};
28+
29+
DnsaddrResolverImpl(std::shared_ptr<boost::asio::io_context> io_context,
30+
const c_ares::Ares &cares);
31+
32+
void load(multi::Multiaddress address, AddressesCallback callback) override;
33+
34+
private:
35+
/// Convert multiaddr "/dnsaddr/hostname" to string "_dnsaddr.hostname"
36+
static outcome::result<std::string> dnsaddrUriFromMultiaddr(
37+
const multi::Multiaddress &address);
38+
39+
std::shared_ptr<boost::asio::io_context> io_context_;
40+
// captured by reference intentionally to force DI use the single instance
41+
const c_ares::Ares &cares_;
42+
};
43+
44+
} // namespace libp2p::network
45+
46+
OUTCOME_HPP_DECLARE_ERROR(libp2p::network, DnsaddrResolverImpl::Error);
47+
48+
#endif // LIBP2P_INCLUDE_LIBP2P_NETWORK_IMPL_DNSADDR_RESOLVER_IMPL_HPP

include/libp2p/peer/address_repository.hpp

+29
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
namespace libp2p::peer {
2020

21+
constexpr std::string_view kBootstrapAddress = "/dnsaddr/bootstrap.libp2p.io";
22+
2123
namespace ttl {
2224

2325
/// permanent addresses, for example, bootstrap nodes
@@ -50,9 +52,36 @@ namespace libp2p::peer {
5052

5153
public:
5254
using AddressCallback = void(const PeerId &, const multi::Multiaddress &);
55+
using BootstrapCallback = void(outcome::result<void>);
5356

5457
~AddressRepository() override = default;
5558

59+
/**
60+
* @brief Populate repository with peer infos discovered through the default
61+
* libp2p bootstrap address
62+
* @param cb - callback to accept the number of discovered peers or an error
63+
* of any
64+
*/
65+
virtual void bootstrap(std::function<BootstrapCallback> cb) {
66+
auto ma_res = multi::Multiaddress::create(kBootstrapAddress);
67+
if (ma_res.has_error()) {
68+
cb(ma_res.error());
69+
return;
70+
}
71+
bootstrap(ma_res.value(), cb);
72+
}
73+
74+
/**
75+
* @brief Populate repository with peer infos discovered through the
76+
* specified bootstrap address
77+
* @param ma - bootstrap node multiaddress, address format is
78+
* "/dnsaddr/<hostname>"
79+
* @param cb - callback to accept the number of discovered peers or an error
80+
* if any
81+
*/
82+
virtual void bootstrap(const multi::Multiaddress &ma,
83+
std::function<BootstrapCallback> cb) = 0;
84+
5685
/**
5786
* @brief Add addresses to a given peer {@param p}
5887
* @param p peer

0 commit comments

Comments
 (0)