recursive_query_unittest_3.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <algorithm>
  15. #include <cstdlib>
  16. #include <iomanip>
  17. #include <iostream>
  18. #include <string>
  19. #include <vector>
  20. #include <gtest/gtest.h>
  21. #include <boost/bind.hpp>
  22. #include <asio.hpp>
  23. #include <util/buffer.h>
  24. #include <util/io_utilities.h>
  25. #include <dns/question.h>
  26. #include <dns/message.h>
  27. #include <dns/messagerenderer.h>
  28. #include <dns/opcode.h>
  29. #include <dns/name.h>
  30. #include <dns/rcode.h>
  31. #include <dns/rrtype.h>
  32. #include <dns/rrset.h>
  33. #include <dns/rrttl.h>
  34. #include <dns/rdata.h>
  35. #include <util/io_utilities.h>
  36. #include <asiodns/dns_service.h>
  37. #include <asiodns/io_fetch.h>
  38. #include <asiolink/io_address.h>
  39. #include <asiolink/io_endpoint.h>
  40. #include <asiolink/io_service.h>
  41. #include <resolve/recursive_query.h>
  42. #include <resolve/resolver_interface.h>
  43. using namespace asio;
  44. using namespace asio::ip;
  45. using namespace isc::asiolink;
  46. using namespace isc::dns;
  47. using namespace isc::dns::rdata;
  48. using namespace isc::util;
  49. using namespace isc::resolve;
  50. using namespace std;
  51. /// RecursiveQuery Test - 3
  52. ///
  53. /// The second part of the RecursiveQuery unit tests, this attempts to get the
  54. /// RecursiveQuery object to follow a set of EDNS-induced errors, causing the
  55. /// resolver to follow the fallback logic.
  56. ///
  57. /// - Send EDNS question over UDP - get FORMERR
  58. /// - Send EDNS question over TCP - get FORMERR
  59. /// - Send non-EDNS question over UDP - get RESPONSE
  60. ///
  61. /// By using the "test_server_" element of RecursiveQuery, all queries are
  62. /// directed to one or other of the "servers" in the RecursiveQueryTest3 class.
  63. namespace {
  64. const char* const TEST_ADDRESS3 = "127.0.0.1"; ///< Servers are on this address
  65. const uint16_t TEST_PORT3 = 5303; ///< ... and this port
  66. const size_t BUFFER_SIZE = 1024; ///< For all buffers
  67. const char* const DUMMY_ADDR3 = "1.2.3.4"; ///< address to return as A
  68. } // end anonymous namespace
  69. namespace isc {
  70. namespace asiodns {
  71. class MockResolver3 : public isc::resolve::ResolverInterface {
  72. public:
  73. virtual void resolve(const QuestionPtr& question,
  74. const ResolverInterface::CallbackPtr& callback) {
  75. }
  76. virtual ~MockResolver3() {}
  77. };
  78. /// \brief Test fixture for the RecursiveQuery Test
  79. class RecursiveQueryTest3 : public virtual ::testing::Test
  80. {
  81. public:
  82. /// \brief Status of query
  83. ///
  84. /// Set before the query and then by each "server" when responding.
  85. enum QueryStatus {
  86. NONE = 0, ///< Default
  87. EDNS_UDP = 1, ///< EDNS query over UDP
  88. NON_EDNS_UDP = 2, ///< Non-EDNS query over UDP
  89. COMPLETE = 6 ///< Query is complete
  90. };
  91. // Common stuff
  92. IOService service_; ///< Service to run everything
  93. DNSService dns_service_; ///< Resolver is part of "server"
  94. QuestionPtr question_; ///< What to ask
  95. QueryStatus last_; ///< What was the last state
  96. QueryStatus expected_; ///< Expected next state
  97. OutputBufferPtr question_buffer_; ///< Question we expect to receive
  98. boost::shared_ptr<MockResolver3> resolver_; ///< Mock resolver
  99. isc::nsas::NameserverAddressStore* nsas_; ///< Nameserver address store
  100. isc::cache::ResolverCache cache_; ///< Resolver cache
  101. // Data for TCP Server
  102. size_t tcp_cumulative_; ///< Cumulative TCP data received
  103. tcp::endpoint tcp_endpoint_; ///< Endpoint for TCP receives
  104. size_t tcp_length_; ///< Expected length value
  105. uint8_t tcp_receive_buffer_[BUFFER_SIZE]; ///< Receive buffer for TCP I/O
  106. OutputBufferPtr tcp_send_buffer_; ///< Send buffer for TCP I/O
  107. tcp::socket tcp_socket_; ///< Socket used by TCP server
  108. /// Data for UDP
  109. udp::endpoint udp_remote_; ///< Endpoint for UDP receives
  110. size_t udp_length_; ///< Expected length value
  111. uint8_t udp_receive_buffer_[BUFFER_SIZE]; ///< Receive buffer for UDP I/O
  112. OutputBufferPtr udp_send_buffer_; ///< Send buffer for UDP I/O
  113. udp::socket udp_socket_; ///< Socket used by UDP server
  114. /// \brief Constructor
  115. RecursiveQueryTest3() :
  116. service_(),
  117. dns_service_(service_, NULL, NULL, NULL),
  118. question_(new Question(Name("ednsfallback"),
  119. RRClass::IN(), RRType::A())),
  120. last_(NONE),
  121. expected_(NONE),
  122. question_buffer_(new OutputBuffer(BUFFER_SIZE)),
  123. resolver_(new MockResolver3()),
  124. nsas_(new isc::nsas::NameserverAddressStore(resolver_)),
  125. tcp_cumulative_(0),
  126. tcp_endpoint_(asio::ip::address::from_string(TEST_ADDRESS3),
  127. TEST_PORT3),
  128. tcp_length_(0),
  129. tcp_receive_buffer_(),
  130. tcp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
  131. tcp_socket_(service_.get_io_service()),
  132. udp_remote_(),
  133. udp_length_(0),
  134. udp_receive_buffer_(),
  135. udp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
  136. udp_socket_(service_.get_io_service(), udp::v4())
  137. {
  138. }
  139. /// \brief Set Common Message Bits
  140. ///
  141. /// Sets up the common bits of a response message returned by the handlers.
  142. ///
  143. /// \param message Message buffer in RENDER mode.
  144. /// \param qid QID to set the message to
  145. void setCommonMessage(isc::dns::Message& message, uint16_t qid) {
  146. message.setQid(qid);
  147. message.setHeaderFlag(Message::HEADERFLAG_QR);
  148. message.setOpcode(Opcode::QUERY());
  149. message.setHeaderFlag(Message::HEADERFLAG_AA);
  150. message.addQuestion(*question_);
  151. }
  152. /// \brief Set FORMERR answer
  153. ///
  154. /// \param message Message to update with FORMERR status
  155. void setFORMERR(isc::dns::Message& message) {
  156. message.setRcode(Rcode::FORMERR());
  157. }
  158. /// \brief Set Answer
  159. ///
  160. /// \param message Message to update with FORMERR status
  161. void setAnswer(isc::dns::Message& message) {
  162. // Give a response
  163. RRsetPtr answer(new RRset(Name("ednsfallback."), RRClass::IN(),
  164. RRType::A(), RRTTL(300)));
  165. answer->addRdata(createRdata(RRType::A(), RRClass::IN(), DUMMY_ADDR3));
  166. message.addRRset(Message::SECTION_ANSWER, answer);
  167. message.setRcode(Rcode::NOERROR());
  168. }
  169. /// \brief UDP Receive Handler
  170. ///
  171. /// This is invoked when a message is received over UDP from the
  172. /// RecursiveQuery object under test. It formats an answer and sends it
  173. /// asynchronously, with the UdpSendHandler method being specified as the
  174. /// completion handler.
  175. ///
  176. /// \param ec ASIO error code, completion code of asynchronous I/O issued
  177. /// by the "server" to receive data.
  178. /// \param length Amount of data received.
  179. void udpReceiveHandler(error_code ec = error_code(), size_t length = 0) {
  180. // Expected state should be one greater than the last state.
  181. EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
  182. last_ = expected_;
  183. Message query(Message::PARSE);
  184. // The QID in the incoming data is random so set it to 0 for the
  185. // data comparison check. (It is set to 0 in the buffer containing
  186. // the expected data.)
  187. // And check that question we received is what was expected.
  188. checkReceivedPacket(udp_receive_buffer_, length, query);
  189. // The message returned depends on what state we are in. Set up
  190. // common stuff first: bits not mentioned are set to 0.
  191. Message message(Message::RENDER);
  192. setCommonMessage(message, query.getQid());
  193. // Set up state-dependent bits:
  194. switch (expected_) {
  195. case EDNS_UDP:
  196. EXPECT_TRUE(query.getEDNS());
  197. // Return FORMERROR
  198. setFORMERR(message);
  199. expected_ = NON_EDNS_UDP;
  200. break;
  201. case NON_EDNS_UDP:
  202. EXPECT_FALSE(query.getEDNS());
  203. // Return the answer to the question.
  204. setAnswer(message);
  205. expected_ = COMPLETE;
  206. break;
  207. default:
  208. FAIL() << "UdpReceiveHandler called with unknown state";
  209. }
  210. // Convert to wire format
  211. udp_send_buffer_->clear();
  212. MessageRenderer renderer;
  213. renderer.setBuffer(udp_send_buffer_.get());
  214. message.toWire(renderer);
  215. renderer.setBuffer(NULL);
  216. // Return a message back to the IOFetch object (after setting the
  217. // expected length of data for the check in the send handler).
  218. udp_length_ = udp_send_buffer_->getLength();
  219. udp_socket_.async_send_to(asio::buffer(udp_send_buffer_->getData(),
  220. udp_send_buffer_->getLength()),
  221. udp_remote_,
  222. boost::bind(&RecursiveQueryTest3::udpSendHandler,
  223. this, _1, _2));
  224. }
  225. /// \brief UDP Send Handler
  226. ///
  227. /// Called when a send operation of the UDP server (i.e. a response
  228. /// being sent to the RecursiveQuery) has completed, this re-issues
  229. /// a read call.
  230. ///
  231. /// \param ec Completion error code of the send.
  232. /// \param length Actual number of bytes sent.
  233. void udpSendHandler(error_code ec = error_code(), size_t length = 0) {
  234. // Check send was OK
  235. EXPECT_EQ(0, ec.value());
  236. EXPECT_EQ(udp_length_, length);
  237. // Reissue the receive call to await the next message.
  238. udp_socket_.async_receive_from(
  239. asio::buffer(udp_receive_buffer_, sizeof(udp_receive_buffer_)),
  240. udp_remote_,
  241. boost::bind(&RecursiveQueryTest3::udpReceiveHandler,
  242. this, _1, _2));
  243. }
  244. /// \brief Completion Handler for Accepting TCP Data
  245. ///
  246. /// Called when the remote system connects to the "TCP server". It issues
  247. /// an asynchronous read on the socket to read data.
  248. ///
  249. /// \param socket Socket on which data will be received
  250. /// \param ec Boost error code, value should be zero.
  251. void tcpAcceptHandler(error_code ec = error_code(), size_t length = 0) {
  252. // Expect that the accept completed without a problem.
  253. EXPECT_EQ(0, ec.value());
  254. // Initiate a read on the socket, indicating that nothing has yet been
  255. // received.
  256. tcp_cumulative_ = 0;
  257. tcp_socket_.async_receive(
  258. asio::buffer(tcp_receive_buffer_, sizeof(tcp_receive_buffer_)),
  259. boost::bind(&RecursiveQueryTest3::tcpReceiveHandler, this, _1, _2));
  260. }
  261. /// \brief Completion Handler for Receiving TCP Data
  262. ///
  263. /// Reads data from the RecursiveQuery object and loops, reissuing reads,
  264. /// until all the message has been read. It then returns an appropriate
  265. /// response.
  266. ///
  267. /// \param socket Socket to use to send the answer
  268. /// \param ec ASIO error code, completion code of asynchronous I/O issued
  269. /// by the "server" to receive data.
  270. /// \param length Amount of data received.
  271. void tcpReceiveHandler(error_code ec = error_code(), size_t length = 0) {
  272. // Expect that the receive completed without a problem.
  273. EXPECT_EQ(0, ec.value());
  274. // Have we received all the data? We know this by checking if the two-
  275. // byte length count in the message is equal to the data received.
  276. tcp_cumulative_ += length;
  277. bool complete = false;
  278. if (tcp_cumulative_ > 2) {
  279. uint16_t dns_length = readUint16(tcp_receive_buffer_);
  280. complete = ((dns_length + 2) == tcp_cumulative_);
  281. }
  282. if (!complete) {
  283. // Not complete yet, issue another read.
  284. tcp_socket_.async_receive(
  285. asio::buffer(tcp_receive_buffer_ + tcp_cumulative_,
  286. sizeof(tcp_receive_buffer_) - tcp_cumulative_),
  287. boost::bind(&RecursiveQueryTest3::tcpReceiveHandler,
  288. this, _1, _2));
  289. return;
  290. }
  291. // Have received a TCP message. Expected state should be one greater
  292. // than the last state.
  293. EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
  294. last_ = expected_;
  295. Message query(Message::PARSE);
  296. // Check that question we received is what was expected. Note that we
  297. // have to ignore the two-byte header in order to parse the message.
  298. checkReceivedPacket(tcp_receive_buffer_ + 2, length - 2, query);
  299. // Return a message back. This is a referral to example.org, which
  300. // should result in another query over UDP. Note the setting of the
  301. // QID in the returned message with what was in the received message.
  302. Message message(Message::RENDER);
  303. setCommonMessage(message, query.getQid());
  304. // Set up state-dependent bits:
  305. switch (expected_) {
  306. default:
  307. FAIL() << "TcpReceiveHandler called with unknown state";
  308. }
  309. // Convert to wire format
  310. // Use a temporary buffer for the dns wire data (we copy it
  311. // to the 'real' buffer below)
  312. MessageRenderer renderer;
  313. message.toWire(renderer);
  314. // Also, take this opportunity to clear the accumulated read count in
  315. // readiness for the next read. (If any - at present, there is only
  316. // one read in the test, although extensions to this test suite could
  317. // change that.)
  318. tcp_cumulative_ = 0;
  319. // Unless we go through a callback loop we cannot simply use
  320. // async_send() multiple times, so we cannot send the size first
  321. // followed by the actual data. We copy them to a new buffer
  322. // first
  323. tcp_send_buffer_->clear();
  324. tcp_send_buffer_->writeUint16(renderer.getLength());
  325. tcp_send_buffer_->writeData(renderer.getData(), renderer.getLength());
  326. tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
  327. tcp_send_buffer_->getLength()),
  328. boost::bind(
  329. &RecursiveQueryTest3::tcpSendHandler,
  330. this,
  331. tcp_send_buffer_->getLength(), _1, _2));
  332. }
  333. /// \brief Completion Handler for Sending TCP data
  334. ///
  335. /// Called when the asynchronous send of data back to the RecursiveQuery
  336. /// by the TCP "server" in this class has completed. (This send has to
  337. /// be asynchronous because control needs to return to the caller in order
  338. /// for the IOService "run()" method to be called to run the handlers.)
  339. ///
  340. /// \param expected_length Number of bytes that were expected to have been
  341. /// sent.
  342. /// \param ec Boost error code, value should be zero.
  343. /// \param length Number of bytes sent.
  344. void tcpSendHandler(size_t expected_length = 0,
  345. error_code ec = error_code(),
  346. size_t length = 0)
  347. {
  348. EXPECT_EQ(0, ec.value()); // Expect no error
  349. EXPECT_EQ(expected_length, length); // And that amount sent is as
  350. // expected
  351. }
  352. /// \brief Check Received Packet
  353. ///
  354. /// Checks the packet received from the RecursiveQuery object to ensure
  355. /// that the question is what is expected.
  356. ///
  357. /// \param data Start of data. This is the start of the received buffer in
  358. /// the case of UDP data, and an offset into the buffer past the
  359. /// count field for TCP data.
  360. /// \param length Length of data.
  361. /// \return The QID of the message
  362. void checkReceivedPacket(uint8_t* data, size_t length, Message& message) {
  363. // Decode the received buffer.
  364. InputBuffer buffer(data, length);
  365. message.fromWire(buffer);
  366. // Check the packet.
  367. EXPECT_FALSE(message.getHeaderFlag(Message::HEADERFLAG_QR));
  368. Question question = **(message.beginQuestion());
  369. EXPECT_TRUE(question == *question_);
  370. }
  371. };
  372. /// \brief Resolver Callback Object
  373. ///
  374. /// Holds the success and failure callback methods for the resolver
  375. class ResolverCallback3 : public isc::resolve::ResolverInterface::Callback {
  376. public:
  377. /// \brief Constructor
  378. ResolverCallback3(IOService& service) :
  379. service_(service), run_(false), status_(false)
  380. {}
  381. /// \brief Destructor
  382. virtual ~ResolverCallback3()
  383. {}
  384. /// \brief Resolver Callback Success
  385. ///
  386. /// Called if the resolver detects that the call has succeeded.
  387. ///
  388. /// \param response Answer to the question.
  389. virtual void success(const isc::dns::MessagePtr response) {
  390. // There should be one RR each in the question and answer sections,
  391. // and two RRs in each of the the authority and additional sections.
  392. EXPECT_EQ(1, response->getRRCount(Message::SECTION_QUESTION));
  393. EXPECT_EQ(1, response->getRRCount(Message::SECTION_ANSWER));
  394. // Check the answer - that the RRset is there...
  395. EXPECT_TRUE(response->hasRRset(Message::SECTION_ANSWER,
  396. RRsetPtr(new RRset(Name("ednsfallback."),
  397. RRClass::IN(),
  398. RRType::A(),
  399. RRTTL(300)))));
  400. const RRsetIterator rrset_i = response->beginSection(Message::SECTION_ANSWER);
  401. // ... get iterator into the Rdata of this RRset and point to first
  402. // element...
  403. const RdataIteratorPtr rdata_i = (*rrset_i)->getRdataIterator();
  404. rdata_i->first();
  405. // ... and check it is what we expect.
  406. EXPECT_EQ(string(DUMMY_ADDR3), rdata_i->getCurrent().toText());
  407. // Flag completion
  408. run_ = true;
  409. status_ = true;
  410. service_.stop(); // Cause run() to exit.
  411. }
  412. /// \brief Resolver Failure Completion
  413. ///
  414. /// Called if the resolver detects that the resolution has failed.
  415. virtual void failure() {
  416. FAIL() << "Resolver reported completion failure";
  417. // Flag completion
  418. run_ = true;
  419. status_ = false;
  420. service_.stop(); // Cause run() to exit.
  421. }
  422. /// \brief Return status of "run" flag
  423. bool getRun() const {
  424. return (run_);
  425. }
  426. /// \brief Return "status" flag
  427. bool getStatus() const {
  428. return (status_);
  429. }
  430. private:
  431. IOService& service_; ///< Service handling the run queue
  432. bool run_; ///< Set true when completion handler run
  433. bool status_; ///< Set true for success, false on error
  434. };
  435. // Sets up the UDP and TCP "servers", then tries a resolution.
  436. TEST_F(RecursiveQueryTest3, Resolve) {
  437. // Set up the UDP server and issue the first read. The endpoint from which
  438. // the query is sent is put in udp_endpoint_ when the read completes, which
  439. // is referenced in the callback as the place to which the response is
  440. // sent.
  441. udp_socket_.set_option(socket_base::reuse_address(true));
  442. udp_socket_.bind(udp::endpoint(address::from_string(TEST_ADDRESS3),
  443. TEST_PORT3));
  444. udp_socket_.async_receive_from(asio::buffer(udp_receive_buffer_,
  445. sizeof(udp_receive_buffer_)),
  446. udp_remote_,
  447. boost::bind(&RecursiveQueryTest3::udpReceiveHandler,
  448. this, _1, _2));
  449. // Set up the TCP server and issue the accept. Acceptance will cause the
  450. // read to be issued.
  451. tcp::acceptor acceptor(service_.get_io_service(),
  452. tcp::endpoint(tcp::v4(), TEST_PORT3));
  453. acceptor.async_accept(tcp_socket_,
  454. boost::bind(&RecursiveQueryTest3::tcpAcceptHandler,
  455. this, _1, 0));
  456. // Set up the RecursiveQuery object. We will also test that it correctly
  457. // records RTT times by setting up a RTT recorder object as well.
  458. std::vector<std::pair<std::string, uint16_t> > upstream; // Empty
  459. std::vector<std::pair<std::string, uint16_t> > upstream_root; // Empty
  460. RecursiveQuery query(dns_service_, *nsas_, cache_,
  461. upstream, upstream_root);
  462. query.setTestServer(TEST_ADDRESS3, TEST_PORT3);
  463. boost::shared_ptr<RttRecorder> recorder(new RttRecorder());
  464. query.setRttRecorder(recorder);
  465. // Set up callback to receive notification that the query has completed.
  466. isc::resolve::ResolverInterface::CallbackPtr
  467. resolver_callback(new ResolverCallback3(service_));
  468. // Kick off the resolution process.
  469. expected_ = EDNS_UDP;
  470. query.resolve(question_, resolver_callback);
  471. service_.run();
  472. // Check what ran. (We have to cast the callback to ResolverCallback3 as we
  473. // lost the information on the derived class when we used a
  474. // ResolverInterface::CallbackPtr to store a pointer to it.)
  475. ResolverCallback3* rc
  476. = static_cast<ResolverCallback3*>(resolver_callback.get());
  477. EXPECT_TRUE(rc->getRun());
  478. EXPECT_TRUE(rc->getStatus());
  479. // Finally, check that all the RTTs were "reasonable" (defined here as
  480. // being below 2 seconds). This is an explicit check to test that the
  481. // variables in the RTT calculation are at least being initialized; if they
  482. // weren't, we would expect some absurdly high answers.
  483. vector<uint32_t> rtt = recorder->getRtt();
  484. EXPECT_GT(rtt.size(), 0);
  485. for (int i = 0; i < rtt.size(); ++i) {
  486. EXPECT_LT(rtt[i], 2000);
  487. }
  488. }
  489. } // namespace asiodns
  490. } // namespace isc