080-resolver.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. // SPDX-FileCopyrightText: 2019-2024 Ivan Baidakou
  3. #include <catch2/catch_all.hpp>
  4. #include <rotor.hpp>
  5. #include <rotor/asio.hpp>
  6. #include "test-utils.h"
  7. #include "utils/error_code.h"
  8. #include "net/resolver_actor.h"
  9. #include "utils/format.hpp"
  10. using namespace syncspirit;
  11. using namespace syncspirit::test;
  12. using namespace syncspirit::net;
  13. using namespace std::chrono_literals;
  14. namespace asio = boost::asio;
  15. namespace sys = boost::system;
  16. namespace r = rotor;
  17. namespace ra = r::asio;
  18. using configure_callback_t = std::function<void(r::plugin::plugin_base_t &)>;
  19. using response_ptr_t = r::intrusive_ptr_t<message::resolve_response_t>;
  20. using Catch::Matchers::StartsWith;
  21. auto timeout = r::pt::time_duration{r::pt::millisec{1000}};
  22. struct my_supervisor_t : ra::supervisor_asio_t {
  23. using parent_t = ra::supervisor_asio_t;
  24. using parent_t::parent_t;
  25. using responses_t = std::vector<response_ptr_t>;
  26. void configure(r::plugin::plugin_base_t &plugin) noexcept override {
  27. ra::supervisor_asio_t::configure(plugin);
  28. plugin.with_casted<r::plugin::starter_plugin_t>(
  29. [&](auto &p) { p.subscribe_actor(&my_supervisor_t::on_resolve); });
  30. if (configure_callback) {
  31. configure_callback(plugin);
  32. }
  33. }
  34. void on_resolve(message::resolve_response_t &res) noexcept { responses.emplace_back(&res); }
  35. responses_t responses;
  36. configure_callback_t configure_callback;
  37. };
  38. using supervisor_ptr_t = r::intrusive_ptr_t<my_supervisor_t>;
  39. using actor_ptr_t = r::intrusive_ptr_t<resolver_actor_t>;
  40. struct fixture_t {
  41. fixture_t() : ctx(io_ctx), root_path{bfs::unique_path()}, path_quard{root_path}, remote_resolver{io_ctx} {
  42. utils::set_default("trace");
  43. log = utils::get_logger("fixture");
  44. rx_buff.resize(1500);
  45. }
  46. virtual void main() noexcept = 0;
  47. void run() {
  48. auto strand = std::make_shared<asio::io_context::strand>(io_ctx);
  49. sup = ctx.create_supervisor<my_supervisor_t>().strand(strand).timeout(timeout).create_registry().finish();
  50. sup->start();
  51. sup->do_process();
  52. hosts_path = root_path / "hosts";
  53. auto ep = asio::ip::udp::endpoint(asio::ip::make_address("127.0.0.1"), 0);
  54. remote_resolver.open(ep.protocol());
  55. remote_resolver.bind(ep);
  56. auto local_ep = remote_resolver.local_endpoint();
  57. log->info("remote resolver: {}", local_ep);
  58. main();
  59. sup->do_shutdown();
  60. sup->do_process();
  61. io_ctx.run();
  62. }
  63. asio::io_context io_ctx{1};
  64. ra::system_context_asio_t ctx;
  65. bfs::path root_path;
  66. bfs::path hosts_path;
  67. path_guard_t path_quard;
  68. utils::logger_t log;
  69. supervisor_ptr_t sup;
  70. actor_ptr_t resolver;
  71. udp_socket_t remote_resolver;
  72. udp::endpoint resolver_endpoint;
  73. fmt::memory_buffer rx_buff;
  74. };
  75. void test_local_resolver() {
  76. struct F : fixture_t {
  77. void main() noexcept override {
  78. write_file(hosts_path, "127.0.0.2 lclhst.localdomain lclhst\n");
  79. resolver = sup->create_actor<resolver_actor_t>()
  80. .resolve_timeout(timeout / 2)
  81. .hosts_path(hosts_path.c_str())
  82. .server_addresses("127.0.0.1:1234")
  83. .timeout(timeout)
  84. .finish();
  85. sup->do_process();
  86. sup->request<payload::address_request_t>(resolver->get_address(), "lclhst", 123).send(timeout);
  87. sup->do_process();
  88. REQUIRE(sup->responses.size() == 1);
  89. auto results = sup->responses.at(0)->payload.res->results;
  90. REQUIRE(results.size() == 1);
  91. REQUIRE(results.at(0) == asio::ip::make_address("127.0.0.2"));
  92. // cache hit
  93. sup->request<payload::address_request_t>(resolver->get_address(), "lclhst", 123).send(timeout);
  94. sup->do_process();
  95. REQUIRE(sup->responses.size() == 2);
  96. results = sup->responses.at(1)->payload.res->results;
  97. REQUIRE(results.size() == 1);
  98. REQUIRE(results.at(0) == asio::ip::make_address("127.0.0.2"));
  99. }
  100. };
  101. F().run();
  102. }
  103. void test_success_resolver() {
  104. struct F : fixture_t {
  105. void main() noexcept override {
  106. auto local_port = remote_resolver.local_endpoint().port();
  107. write_file(hosts_path, "");
  108. auto buff = asio::buffer(rx_buff.data(), rx_buff.size());
  109. remote_resolver.async_receive_from(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) -> void {
  110. log->info("received {} bytes from resolver", bytes);
  111. const unsigned char reply[] = {0x0e, 0x51, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
  112. 0x00, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f,
  113. 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00,
  114. 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x04, 0x8e, 0xfa, 0xcb, 0x8e};
  115. auto reply_str = std::string_view(reinterpret_cast<const char *>(reply), sizeof(reply));
  116. auto buff = asio::buffer(reply_str.data(), reply_str.size());
  117. remote_resolver.async_send_to(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) {
  118. log->info("sent {} bytes to resolver", bytes);
  119. });
  120. });
  121. resolver = sup->create_actor<resolver_actor_t>()
  122. .resolve_timeout(timeout / 2)
  123. .hosts_path(hosts_path.c_str())
  124. .server_addresses(fmt::format("127.0.0.1:{}", local_port))
  125. .timeout(timeout)
  126. .finish();
  127. sup->do_process();
  128. sup->request<payload::address_request_t>(resolver->get_address(), "google.com", 80).send(timeout);
  129. io_ctx.run();
  130. REQUIRE(sup->responses.size() == 1);
  131. auto &results = sup->responses.at(0)->payload.res->results;
  132. REQUIRE(results.size() == 1);
  133. REQUIRE_THAT(results.at(0).to_string(), StartsWith("142.250."));
  134. }
  135. };
  136. F().run();
  137. }
  138. void test_success_ip() {
  139. struct F : fixture_t {
  140. void main() noexcept override {
  141. auto local_port = remote_resolver.local_endpoint().port();
  142. write_file(hosts_path, "");
  143. resolver = sup->create_actor<resolver_actor_t>()
  144. .resolve_timeout(timeout / 2)
  145. .hosts_path(hosts_path.c_str())
  146. .server_addresses(fmt::format("127.0.0.1:{}", local_port))
  147. .timeout(timeout)
  148. .finish();
  149. sup->do_process();
  150. sup->request<payload::address_request_t>(resolver->get_address(), "127.0.0.1", 80).send(timeout);
  151. sup->do_process();
  152. REQUIRE(sup->responses.size() == 1);
  153. auto &results = sup->responses.at(0)->payload.res->results;
  154. REQUIRE(results.size() == 1);
  155. REQUIRE(results.at(0) == asio::ip::make_address("127.0.0.1"));
  156. }
  157. };
  158. F().run();
  159. }
  160. void test_success_ipv6() {
  161. struct F : fixture_t {
  162. void main() noexcept override {
  163. auto local_port = remote_resolver.local_endpoint().port();
  164. write_file(hosts_path, "");
  165. resolver = sup->create_actor<resolver_actor_t>()
  166. .resolve_timeout(timeout / 2)
  167. .hosts_path(hosts_path.c_str())
  168. .server_addresses(fmt::format("127.0.0.1:{}", local_port))
  169. .timeout(timeout)
  170. .finish();
  171. sup->do_process();
  172. sup->request<payload::address_request_t>(resolver->get_address(),
  173. "[fde8:6819:8685:4d00:be03:58ff:fe74:c854]", 80)
  174. .send(timeout);
  175. sup->do_process();
  176. REQUIRE(sup->responses.size() == 1);
  177. auto &results = sup->responses.at(0)->payload.res->results;
  178. REQUIRE(results.size() == 1);
  179. REQUIRE(results.at(0) == asio::ip::make_address("fde8:6819:8685:4d00:be03:58ff:fe74:c854"));
  180. }
  181. };
  182. F().run();
  183. }
  184. void test_garbage() {
  185. struct F : fixture_t {
  186. void main() noexcept override {
  187. auto local_port = remote_resolver.local_endpoint().port();
  188. write_file(hosts_path, "");
  189. auto buff = asio::buffer(rx_buff.data(), rx_buff.size());
  190. remote_resolver.async_receive_from(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) -> void {
  191. log->info("received {} bytes from resolver", bytes);
  192. const unsigned char reply[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  193. auto reply_str = std::string_view(reinterpret_cast<const char *>(reply), sizeof(reply));
  194. auto buff = asio::buffer(reply_str.data(), reply_str.size());
  195. remote_resolver.async_send_to(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) {
  196. log->info("sent {} bytes to resolver", bytes);
  197. });
  198. });
  199. resolver = sup->create_actor<resolver_actor_t>()
  200. .resolve_timeout(timeout / 2)
  201. .hosts_path(hosts_path.c_str())
  202. .server_addresses(fmt::format("127.0.0.1:{}", local_port))
  203. .timeout(timeout)
  204. .finish();
  205. sup->do_process();
  206. sup->request<payload::address_request_t>(resolver->get_address(), "google.com", 80).send(timeout);
  207. io_ctx.run();
  208. REQUIRE(sup->responses.size() == 1);
  209. auto &ee = sup->responses.at(0)->payload.ee;
  210. REQUIRE(ee);
  211. REQUIRE(ee->ec.value() == static_cast<int>(utils::error_code_t::cares_failure));
  212. }
  213. };
  214. F().run();
  215. }
  216. void test_multi_replies() {
  217. struct F : fixture_t {
  218. void main() noexcept override {
  219. auto local_port = remote_resolver.local_endpoint().port();
  220. write_file(hosts_path, "");
  221. auto buff = asio::buffer(rx_buff.data(), rx_buff.size());
  222. remote_resolver.async_receive_from(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) -> void {
  223. log->info("received {} bytes from resolver", bytes);
  224. const unsigned char reply[] = {
  225. 0x5e, 0x60, 0x81, 0x80, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x72, 0x65, 0x6c,
  226. 0x61, 0x79, 0x73, 0x09, 0x73, 0x79, 0x6e, 0x63, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x03, 0x6e, 0x65,
  227. 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x05, 0x31,
  228. 0x00, 0x0d, 0x0a, 0x70, 0x61, 0x72, 0x2d, 0x6b, 0x38, 0x73, 0x2d, 0x76, 0x34, 0xc0, 0x13, 0xc0,
  229. 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x00, 0x04, 0x33, 0x9f, 0x56, 0xd0, 0xc0,
  230. 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x00, 0x04, 0x33, 0x9f, 0x4b, 0x11};
  231. auto reply_str = std::string_view(reinterpret_cast<const char *>(reply), sizeof(reply));
  232. auto buff = asio::buffer(reply_str.data(), reply_str.size());
  233. remote_resolver.async_send_to(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) {
  234. log->info("sent {} bytes to resolver", bytes);
  235. });
  236. });
  237. resolver = sup->create_actor<resolver_actor_t>()
  238. .resolve_timeout(timeout / 2)
  239. .hosts_path(hosts_path.c_str())
  240. .server_addresses(fmt::format("127.0.0.1:{}", local_port))
  241. .timeout(timeout)
  242. .finish();
  243. sup->do_process();
  244. sup->request<payload::address_request_t>(resolver->get_address(), "relays.syncthing.net", 80).send(timeout);
  245. io_ctx.run();
  246. REQUIRE(sup->responses.size() == 1);
  247. auto &results = sup->responses.at(0)->payload.res->results;
  248. REQUIRE(results.size() == 2);
  249. REQUIRE(results.at(0) == asio::ip::make_address("51.159.86.208"));
  250. REQUIRE(results.at(1) == asio::ip::make_address("51.159.75.17"));
  251. }
  252. };
  253. F().run();
  254. }
  255. void test_wrong() {
  256. struct F : fixture_t {
  257. void main() noexcept override {
  258. auto local_port = remote_resolver.local_endpoint().port();
  259. write_file(hosts_path, "");
  260. auto buff = asio::buffer(rx_buff.data(), rx_buff.size());
  261. remote_resolver.async_receive_from(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) -> void {
  262. log->info("received {} bytes from resolver", bytes);
  263. const unsigned char reply[] = {0x0e, 0x51, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
  264. 0x00, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x61,
  265. 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00,
  266. 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x04, 0x8e, 0xfa, 0xcb, 0x8e};
  267. auto reply_str = std::string_view(reinterpret_cast<const char *>(reply), sizeof(reply));
  268. auto buff = asio::buffer(reply_str.data(), reply_str.size());
  269. remote_resolver.async_send_to(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) {
  270. log->info("sent {} bytes to resolver", bytes);
  271. });
  272. });
  273. resolver = sup->create_actor<resolver_actor_t>()
  274. .resolve_timeout(timeout / 2)
  275. .hosts_path(hosts_path.c_str())
  276. .server_addresses(fmt::format("127.0.0.1:{}", local_port))
  277. .timeout(timeout)
  278. .finish();
  279. sup->do_process();
  280. sup->request<payload::address_request_t>(resolver->get_address(), "google.com", 80).send(timeout);
  281. io_ctx.run();
  282. REQUIRE(sup->responses.size() == 1);
  283. auto &ee = sup->responses.at(0)->payload.ee;
  284. REQUIRE(ee);
  285. REQUIRE(ee->ec.value() == static_cast<int>(utils::error_code_t::cares_failure));
  286. }
  287. };
  288. F().run();
  289. }
  290. void test_timeout() {
  291. struct F : fixture_t {
  292. void main() noexcept override {
  293. auto local_port = remote_resolver.local_endpoint().port();
  294. write_file(hosts_path, "");
  295. resolver = sup->create_actor<resolver_actor_t>()
  296. .resolve_timeout(timeout / 2)
  297. .hosts_path(hosts_path.c_str())
  298. .server_addresses(fmt::format("127.0.0.1:{}", local_port))
  299. .timeout(timeout)
  300. .finish();
  301. sup->do_process();
  302. sup->request<payload::address_request_t>(resolver->get_address(), "google.com", 80).send(timeout);
  303. io_ctx.run();
  304. REQUIRE(sup->responses.size() == 1);
  305. auto &ee = sup->responses.at(0)->payload.ee;
  306. REQUIRE(ee);
  307. REQUIRE(ee->ec.value() == static_cast<int>(r::error_code_t::request_timeout));
  308. }
  309. };
  310. F().run();
  311. }
  312. void test_cancellation() {
  313. struct F : fixture_t {
  314. void main() noexcept override {
  315. auto local_port = remote_resolver.local_endpoint().port();
  316. write_file(hosts_path, "");
  317. resolver = sup->create_actor<resolver_actor_t>()
  318. .resolve_timeout(timeout / 2)
  319. .hosts_path(hosts_path.c_str())
  320. .server_addresses(fmt::format("127.0.0.1:{}", local_port))
  321. .timeout(timeout)
  322. .finish();
  323. sup->do_process();
  324. auto resolver_address = resolver->get_address();
  325. auto request = sup->request<payload::address_request_t>(resolver_address, "google.com", 80).send(timeout);
  326. sup->send<message::resolve_cancel_t::payload_t>(resolver_address, request, sup->get_address());
  327. io_ctx.run();
  328. REQUIRE(sup->responses.size() == 1);
  329. auto &ee = sup->responses.at(0)->payload.ee;
  330. REQUIRE(ee);
  331. REQUIRE(ee->ec.value() == static_cast<int>(asio::error::operation_aborted));
  332. }
  333. };
  334. F().run();
  335. }
  336. int _init() {
  337. REGISTER_TEST_CASE(test_local_resolver, "test_local_resolver", "[resolver]");
  338. REGISTER_TEST_CASE(test_success_resolver, "test_success_resolver", "[resolver]");
  339. REGISTER_TEST_CASE(test_success_ip, "test_success_ip", "[resolver]");
  340. REGISTER_TEST_CASE(test_success_ipv6, "test_success_ipv6", "[resolver]");
  341. REGISTER_TEST_CASE(test_multi_replies, "test_multi_replies", "[resolver]");
  342. REGISTER_TEST_CASE(test_garbage, "test_garbage", "[resolver]");
  343. REGISTER_TEST_CASE(test_wrong, "test_wrong", "[resolver]");
  344. REGISTER_TEST_CASE(test_timeout, "test_timeout", "[resolver]");
  345. REGISTER_TEST_CASE(test_cancellation, "test_cancellation", "[resolver]");
  346. return 1;
  347. }
  348. static int v = _init();