080-resolver.cpp 17 KB

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