080-resolver.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  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_success_ipv6() {
  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. resolver = sup->create_actor<resolver_actor_t>()
  170. .resolve_timeout(timeout / 2)
  171. .hosts_path(hosts_path.c_str())
  172. .resolvconf_path(resolv_conf_path.c_str())
  173. .timeout(timeout)
  174. .finish();
  175. sup->do_process();
  176. sup->request<payload::address_request_t>(resolver->get_address(),
  177. "[fde8:6819:8685:4d00:be03:58ff:fe74:c854]", 80)
  178. .send(timeout);
  179. sup->do_process();
  180. REQUIRE(sup->responses.size() == 1);
  181. auto &results = sup->responses.at(0)->payload.res->results;
  182. REQUIRE(results.size() == 1);
  183. REQUIRE(results.at(0) == asio::ip::make_address("fde8:6819:8685:4d00:be03:58ff:fe74:c854"));
  184. }
  185. };
  186. F().run();
  187. }
  188. void test_garbage() {
  189. struct F : fixture_t {
  190. void main() noexcept override {
  191. auto local_port = remote_resolver.local_endpoint().port();
  192. write_file(resolv_conf_path, fmt::format("nameserver 127.0.0.1:{}\n", local_port));
  193. write_file(hosts_path, "");
  194. auto buff = asio::buffer(rx_buff.data(), rx_buff.size());
  195. remote_resolver.async_receive_from(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) -> void {
  196. log->info("received {} bytes from resolver", bytes);
  197. const unsigned char reply[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  198. auto reply_str = std::string_view(reinterpret_cast<const char *>(reply), sizeof(reply));
  199. auto buff = asio::buffer(reply_str.data(), reply_str.size());
  200. remote_resolver.async_send_to(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) {
  201. log->info("sent {} bytes to resolver", bytes);
  202. });
  203. });
  204. resolver = sup->create_actor<resolver_actor_t>()
  205. .resolve_timeout(timeout / 2)
  206. .hosts_path(hosts_path.c_str())
  207. .resolvconf_path(resolv_conf_path.c_str())
  208. .timeout(timeout)
  209. .finish();
  210. sup->do_process();
  211. sup->request<payload::address_request_t>(resolver->get_address(), "google.com", 80).send(timeout);
  212. io_ctx.run();
  213. REQUIRE(sup->responses.size() == 1);
  214. auto &ee = sup->responses.at(0)->payload.ee;
  215. REQUIRE(ee);
  216. REQUIRE(ee->ec.value() == static_cast<int>(utils::error_code_t::cares_failure));
  217. }
  218. };
  219. F().run();
  220. }
  221. void test_multi_replies() {
  222. struct F : fixture_t {
  223. void main() noexcept override {
  224. auto local_port = remote_resolver.local_endpoint().port();
  225. write_file(resolv_conf_path, fmt::format("nameserver 127.0.0.1:{}\n", local_port));
  226. write_file(hosts_path, "");
  227. auto buff = asio::buffer(rx_buff.data(), rx_buff.size());
  228. remote_resolver.async_receive_from(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) -> void {
  229. log->info("received {} bytes from resolver", bytes);
  230. const unsigned char reply[] = {
  231. 0x5e, 0x60, 0x81, 0x80, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06, 0x72, 0x65, 0x6c,
  232. 0x61, 0x79, 0x73, 0x09, 0x73, 0x79, 0x6e, 0x63, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x03, 0x6e, 0x65,
  233. 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x05, 0x31,
  234. 0x00, 0x0d, 0x0a, 0x70, 0x61, 0x72, 0x2d, 0x6b, 0x38, 0x73, 0x2d, 0x76, 0x34, 0xc0, 0x13, 0xc0,
  235. 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x00, 0x04, 0x33, 0x9f, 0x56, 0xd0, 0xc0,
  236. 0x32, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x00, 0x04, 0x33, 0x9f, 0x4b, 0x11};
  237. auto reply_str = std::string_view(reinterpret_cast<const char *>(reply), sizeof(reply));
  238. auto buff = asio::buffer(reply_str.data(), reply_str.size());
  239. remote_resolver.async_send_to(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) {
  240. log->info("sent {} bytes to resolver", bytes);
  241. });
  242. });
  243. resolver = sup->create_actor<resolver_actor_t>()
  244. .resolve_timeout(timeout / 2)
  245. .hosts_path(hosts_path.c_str())
  246. .resolvconf_path(resolv_conf_path.c_str())
  247. .timeout(timeout)
  248. .finish();
  249. sup->do_process();
  250. sup->request<payload::address_request_t>(resolver->get_address(), "relays.syncthing.net", 80).send(timeout);
  251. io_ctx.run();
  252. REQUIRE(sup->responses.size() == 1);
  253. auto &results = sup->responses.at(0)->payload.res->results;
  254. REQUIRE(results.size() == 2);
  255. REQUIRE(results.at(0) == asio::ip::make_address("51.159.86.208"));
  256. REQUIRE(results.at(1) == asio::ip::make_address("51.159.75.17"));
  257. }
  258. };
  259. F().run();
  260. }
  261. void test_wrong() {
  262. struct F : fixture_t {
  263. void main() noexcept override {
  264. auto local_port = remote_resolver.local_endpoint().port();
  265. write_file(resolv_conf_path, fmt::format("nameserver 127.0.0.1:{}\n", local_port));
  266. write_file(hosts_path, "");
  267. auto buff = asio::buffer(rx_buff.data(), rx_buff.size());
  268. remote_resolver.async_receive_from(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) -> void {
  269. log->info("received {} bytes from resolver", bytes);
  270. const unsigned char reply[] = {0x0e, 0x51, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
  271. 0x00, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x61,
  272. 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00,
  273. 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x04, 0x8e, 0xfa, 0xcb, 0x8e};
  274. auto reply_str = std::string_view(reinterpret_cast<const char *>(reply), sizeof(reply));
  275. auto buff = asio::buffer(reply_str.data(), reply_str.size());
  276. remote_resolver.async_send_to(buff, resolver_endpoint, [&](sys::error_code ec, size_t bytes) {
  277. log->info("sent {} bytes to resolver", bytes);
  278. });
  279. });
  280. resolver = sup->create_actor<resolver_actor_t>()
  281. .resolve_timeout(timeout / 2)
  282. .hosts_path(hosts_path.c_str())
  283. .resolvconf_path(resolv_conf_path.c_str())
  284. .timeout(timeout)
  285. .finish();
  286. sup->do_process();
  287. sup->request<payload::address_request_t>(resolver->get_address(), "google.com", 80).send(timeout);
  288. io_ctx.run();
  289. REQUIRE(sup->responses.size() == 1);
  290. auto &ee = sup->responses.at(0)->payload.ee;
  291. REQUIRE(ee);
  292. REQUIRE(ee->ec.value() == static_cast<int>(utils::error_code_t::cares_failure));
  293. }
  294. };
  295. F().run();
  296. }
  297. void test_timeout() {
  298. struct F : fixture_t {
  299. void main() noexcept override {
  300. auto local_port = remote_resolver.local_endpoint().port();
  301. write_file(resolv_conf_path, fmt::format("nameserver 127.0.0.1:{}\n", local_port));
  302. write_file(hosts_path, "");
  303. resolver = sup->create_actor<resolver_actor_t>()
  304. .resolve_timeout(timeout / 2)
  305. .hosts_path(hosts_path.c_str())
  306. .resolvconf_path(resolv_conf_path.c_str())
  307. .timeout(timeout)
  308. .finish();
  309. sup->do_process();
  310. sup->request<payload::address_request_t>(resolver->get_address(), "google.com", 80).send(timeout);
  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>(r::error_code_t::request_timeout));
  316. }
  317. };
  318. F().run();
  319. }
  320. void test_cancellation() {
  321. struct F : fixture_t {
  322. void main() noexcept override {
  323. auto local_port = remote_resolver.local_endpoint().port();
  324. write_file(resolv_conf_path, fmt::format("nameserver 127.0.0.1:{}\n", local_port));
  325. write_file(hosts_path, "");
  326. resolver = sup->create_actor<resolver_actor_t>()
  327. .resolve_timeout(timeout / 2)
  328. .hosts_path(hosts_path.c_str())
  329. .resolvconf_path(resolv_conf_path.c_str())
  330. .timeout(timeout)
  331. .finish();
  332. sup->do_process();
  333. auto resolver_address = resolver->get_address();
  334. auto request = sup->request<payload::address_request_t>(resolver_address, "google.com", 80).send(timeout);
  335. sup->send<message::resolve_cancel_t::payload_t>(resolver_address, request, sup->get_address());
  336. io_ctx.run();
  337. REQUIRE(sup->responses.size() == 1);
  338. auto &ee = sup->responses.at(0)->payload.ee;
  339. REQUIRE(ee);
  340. REQUIRE(ee->ec.value() == static_cast<int>(asio::error::operation_aborted));
  341. }
  342. };
  343. F().run();
  344. }
  345. int _init() {
  346. REGISTER_TEST_CASE(test_local_resolver, "test_local_resolver", "[resolver]");
  347. REGISTER_TEST_CASE(test_success_resolver, "test_success_resolver", "[resolver]");
  348. REGISTER_TEST_CASE(test_success_ip, "test_success_ip", "[resolver]");
  349. REGISTER_TEST_CASE(test_success_ipv6, "test_success_ipv6", "[resolver]");
  350. REGISTER_TEST_CASE(test_multi_replies, "test_multi_replies", "[resolver]");
  351. REGISTER_TEST_CASE(test_garbage, "test_garbage", "[resolver]");
  352. REGISTER_TEST_CASE(test_wrong, "test_wrong", "[resolver]");
  353. REGISTER_TEST_CASE(test_timeout, "test_timeout", "[resolver]");
  354. REGISTER_TEST_CASE(test_cancellation, "test_cancellation", "[resolver]");
  355. return 1;
  356. }
  357. static int v = _init();