dns_client_unittests.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. // Copyright (C) 2013-2014 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 <config.h>
  15. #include <d2/dns_client.h>
  16. #include <dns/opcode.h>
  17. #include <asiodns/io_fetch.h>
  18. #include <asiodns/logger.h>
  19. #include <asiolink/interval_timer.h>
  20. #include <dns/messagerenderer.h>
  21. #include <asio/ip/udp.hpp>
  22. #include <asio/socket_base.hpp>
  23. #include <boost/bind.hpp>
  24. #include <boost/scoped_ptr.hpp>
  25. #include <gtest/gtest.h>
  26. #include <nc_test_utils.h>
  27. using namespace std;
  28. using namespace isc;
  29. using namespace isc::asiolink;
  30. using namespace isc::asiodns;
  31. using namespace isc::d2;
  32. using namespace isc;
  33. using namespace isc::dns;
  34. using namespace isc::util;
  35. using namespace asio;
  36. using namespace asio::ip;
  37. namespace {
  38. const char* TEST_ADDRESS = "127.0.0.1";
  39. const uint16_t TEST_PORT = 5301;
  40. const size_t MAX_SIZE = 1024;
  41. const long TEST_TIMEOUT = 5 * 1000;
  42. // @brief Test Fixture class.
  43. //
  44. // This test fixture class implements DNSClient::Callback so as it can be
  45. // installed as a completion callback for tests it implements. This callback
  46. // is called when a DDNS transaction (send and receive) completes. This allows
  47. // for the callback function to directly access class members. In particular,
  48. // the callback function can access IOService on which run() was called and
  49. // call stop() on it.
  50. //
  51. // Many of the tests defined here schedule execution of certain tasks and block
  52. // until tasks are completed or a timeout is hit. However, if timeout is not
  53. // properly handled a task may be hanging for a long time. In order to prevent
  54. // it, the asiolink::IntervalTimer is used to break a running test if test
  55. // timeout is hit. This will result in test failure.
  56. class DNSClientTest : public virtual ::testing::Test, DNSClient::Callback {
  57. public:
  58. IOService service_;
  59. D2UpdateMessagePtr response_;
  60. DNSClient::Status status_;
  61. uint8_t receive_buffer_[MAX_SIZE];
  62. DNSClientPtr dns_client_;
  63. bool corrupt_response_;
  64. bool expect_response_;
  65. asiolink::IntervalTimer test_timer_;
  66. int received_;
  67. int expected_;
  68. // @brief Constructor.
  69. //
  70. // This constructor overrides the default logging level of asiodns logger to
  71. // prevent it from emitting debug messages from IOFetch class. Such an error
  72. // message can be emitted if timeout occurs when DNSClient class is
  73. // waiting for a response. Some of the tests are checking DNSClient behavior
  74. // in case when response from the server is not received. Tests output would
  75. // become messy if such errors were logged.
  76. DNSClientTest()
  77. : service_(),
  78. status_(DNSClient::SUCCESS),
  79. corrupt_response_(false),
  80. expect_response_(true),
  81. test_timer_(service_),
  82. received_(0), expected_(0) {
  83. asiodns::logger.setSeverity(isc::log::INFO);
  84. response_.reset();
  85. dns_client_.reset(new DNSClient(response_, this));
  86. // Set the test timeout to break any running tasks if they hang.
  87. test_timer_.setup(boost::bind(&DNSClientTest::testTimeoutHandler, this),
  88. TEST_TIMEOUT);
  89. }
  90. // @brief Destructor.
  91. //
  92. // Sets the asiodns logging level back to DEBUG.
  93. virtual ~DNSClientTest() {
  94. asiodns::logger.setSeverity(isc::log::DEBUG);
  95. };
  96. // @brief Exchange completion callback.
  97. //
  98. // This callback is called when the exchange with the DNS server is
  99. // complete or an error occurred. This includes the occurrence of a timeout.
  100. //
  101. // @param status A status code returned by DNSClient.
  102. virtual void operator()(DNSClient::Status status) {
  103. status_ = status;
  104. if (!expected_ || (expected_ == ++received_))
  105. {
  106. service_.stop();
  107. }
  108. if (expect_response_) {
  109. if (!corrupt_response_) {
  110. // We should have received a response.
  111. EXPECT_EQ(DNSClient::SUCCESS, status_);
  112. ASSERT_TRUE(response_);
  113. EXPECT_EQ(D2UpdateMessage::RESPONSE, response_->getQRFlag());
  114. ASSERT_EQ(1,
  115. response_->getRRCount(D2UpdateMessage::SECTION_ZONE));
  116. D2ZonePtr zone = response_->getZone();
  117. ASSERT_TRUE(zone);
  118. EXPECT_EQ("example.com.", zone->getName().toText());
  119. EXPECT_EQ(RRClass::IN().getCode(), zone->getClass().getCode());
  120. } else {
  121. EXPECT_EQ(DNSClient::INVALID_RESPONSE, status_);
  122. }
  123. // If we don't expect a response, the status should indicate a timeout.
  124. } else {
  125. EXPECT_EQ(DNSClient::TIMEOUT, status_);
  126. }
  127. }
  128. // @brief Handler invoked when test timeout is hit.
  129. //
  130. // This callback stops all running (hanging) tasks on IO service.
  131. void testTimeoutHandler() {
  132. service_.stop();
  133. FAIL() << "Test timeout hit.";
  134. }
  135. // @brief Handler invoked when test request is received.
  136. //
  137. // This callback handler is installed when performing async read on a
  138. // socket to emulate reception of the DNS Update request by a server.
  139. // As a result, this handler will send an appropriate DNS Update response
  140. // message back to the address from which the request has come.
  141. //
  142. // @param socket A pointer to a socket used to receive a query and send a
  143. // response.
  144. // @param remote A pointer to an object which specifies the host (address
  145. // and port) from which a request has come.
  146. // @param receive_length A length (in bytes) of the received data.
  147. // @param corrupt_response A bool value which indicates that the server's
  148. // response should be invalid (true) or valid (false)
  149. void udpReceiveHandler(udp::socket* socket, udp::endpoint* remote,
  150. size_t receive_length, const bool corrupt_response) {
  151. // The easiest way to create a response message is to copy the entire
  152. // request.
  153. OutputBuffer response_buf(receive_length);
  154. response_buf.writeData(receive_buffer_, receive_length);
  155. // If a response is to be valid, we have to modify it slightly. If not,
  156. // we leave it as is.
  157. if (!corrupt_response) {
  158. // For a valid response the QR bit must be set. This bit
  159. // differentiates both types of messages. Note that the 3rd byte of
  160. // the message header comprises this bit in the front followed by
  161. // the message code and reserved zeros. Therefore, this byte
  162. // has the following value:
  163. // 10101000,
  164. // where a leading bit is a QR flag. The hexadecimal value is 0xA8.
  165. // Write it at message offset 2.
  166. response_buf.writeUint8At(0xA8, 2);
  167. }
  168. // A response message is now ready to send. Send it!
  169. socket->send_to(asio::buffer(response_buf.getData(),
  170. response_buf.getLength()),
  171. *remote);
  172. }
  173. // @brief Request handler for testing clients using TSIG
  174. //
  175. // This callback handler is installed when performing async read on a
  176. // socket to emulate reception of the DNS Update request with TSIG by a
  177. // server. As a result, this handler will send an appropriate DNS Update
  178. // response message back to the address from which the request has come.
  179. //
  180. // @param socket A pointer to a socket used to receive a query and send a
  181. // response.
  182. // @param remote A pointer to an object which specifies the host (address
  183. // and port) from which a request has come.
  184. // @param receive_length A length (in bytes) of the received data.
  185. // @param corrupt_response A bool value which indicates that the server's
  186. // response should be invalid (true) or valid (false)
  187. // @param client_key TSIG key the server should use to verify the inbound
  188. // request. If the pointer is NULL, the server will not attempt to
  189. // verify the request.
  190. // @param server_key TSIG key the server should use to sign the outbound
  191. // request. If the pointer is NULL, the server will not sign the outbound
  192. // response. If the pointer is not NULL and not the same value as the
  193. // client_key, the server will use a new context to sign the response then
  194. // the one used to verify it. This allows us to simulate the server
  195. // signing with the wrong key.
  196. void TSIGReceiveHandler(udp::socket* socket, udp::endpoint* remote,
  197. size_t receive_length,
  198. TSIGKeyPtr client_key,
  199. TSIGKeyPtr server_key) {
  200. TSIGContextPtr context;
  201. if (client_key) {
  202. context.reset(new TSIGContext(*client_key));
  203. }
  204. isc::util::InputBuffer received_data_buffer(receive_buffer_,
  205. receive_length);
  206. dns::Message request(Message::PARSE);
  207. request.fromWire(received_data_buffer);
  208. // If contex is not NULL, then we need to verify the message.
  209. if (context) {
  210. TSIGError error = context->verify(request.getTSIGRecord(),
  211. receive_buffer_, receive_length);
  212. if (error != TSIGError::NOERROR()) {
  213. isc_throw(TSIGVerifyError, "TSIG verification failed: "
  214. << error.toText());
  215. }
  216. }
  217. dns::Message response(Message::RENDER);
  218. response.setOpcode(Opcode(Opcode::UPDATE_CODE));
  219. response.setHeaderFlag(dns::Message::HEADERFLAG_QR, true);
  220. response.setQid(request.getQid());
  221. response.setRcode(Rcode::NOERROR());
  222. dns::Question question(Name("example.com."),
  223. RRClass::IN(), RRType::SOA());
  224. response.addQuestion(question);
  225. MessageRenderer renderer;
  226. if (!server_key) {
  227. // don't sign the response.
  228. context.reset();
  229. } else if (server_key != client_key) {
  230. // use a different key to sign the response.
  231. context.reset(new TSIGContext(*server_key));
  232. } // otherwise use the context based on client_key.
  233. response.toWire(renderer, context.get());
  234. // A response message is now ready to send. Send it!
  235. socket->send_to(asio::buffer(renderer.getData(), renderer.getLength()),
  236. *remote);
  237. }
  238. // This test verifies that when invalid response placeholder object is
  239. // passed to a constructor, constructor throws the appropriate exception.
  240. // It also verifies that the constructor will not throw if the supplied
  241. // callback object is NULL.
  242. void runConstructorTest() {
  243. EXPECT_NO_THROW(DNSClient(response_, NULL, DNSClient::UDP));
  244. // The TCP Transport is not supported right now. So, we return exception
  245. // if caller specified TCP as a preferred protocol. This test will be
  246. // removed once TCP is supported.
  247. EXPECT_THROW(DNSClient(response_, NULL, DNSClient::TCP),
  248. isc::NotImplemented);
  249. }
  250. // This test verifies that it accepted timeout values belong to the range of
  251. // <0, DNSClient::getMaxTimeout()>.
  252. void runInvalidTimeoutTest() {
  253. expect_response_ = false;
  254. // Create outgoing message. Simply set the required message fields:
  255. // error code and Zone section. This is enough to create on-wire format
  256. // of this message and send it.
  257. D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
  258. ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
  259. ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
  260. // Start with a valid timeout equal to maximal allowed. This way we will
  261. // ensure that doUpdate doesn't throw an exception for valid timeouts.
  262. unsigned int timeout = DNSClient::getMaxTimeout();
  263. EXPECT_NO_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
  264. TEST_PORT, message, timeout));
  265. // Cross the limit and expect that exception is thrown this time.
  266. timeout = DNSClient::getMaxTimeout() + 1;
  267. EXPECT_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
  268. TEST_PORT, message, timeout),
  269. isc::BadValue);
  270. }
  271. // This test verifies the DNSClient behavior when a server does not respond
  272. // do the DNS Update message. In such case, the callback function is
  273. // expected to be called and the TIME_OUT error code should be returned.
  274. void runSendNoReceiveTest() {
  275. // We expect no response from a server.
  276. expect_response_ = false;
  277. // Create outgoing message. Simply set the required message fields:
  278. // error code and Zone section. This is enough to create on-wire format
  279. // of this message and send it.
  280. D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
  281. ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
  282. ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
  283. /// @todo The timeout value could be set to 0 to trigger timeout
  284. /// instantly. However, it may lead to situations that the message sent
  285. /// in one test will not be dropped by the kernel by the time, the next
  286. /// test starts. This will lead to intermittent unit test errors as
  287. /// described in the ticket http://bind10.isc.org/ticket/3265.
  288. /// Increasing the timeout to a non-zero value mitigates this problem.
  289. /// The proper way to solve this problem is to receive the packet
  290. /// on our own and drop it. Such a fix will need to be applied not only
  291. /// to this test but also for other tests that rely on arbitrary timeout
  292. /// values.
  293. const int timeout = 500;
  294. // The doUpdate() function starts asynchronous message exchange with DNS
  295. // server. When message exchange is done or timeout occurs, the
  296. // completion callback will be triggered. The doUpdate function returns
  297. // immediately.
  298. EXPECT_NO_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
  299. TEST_PORT, message, timeout));
  300. // This starts the execution of tasks posted to IOService. run() blocks
  301. // until stop() is called in the completion callback function.
  302. service_.run();
  303. }
  304. // This test verifies that DNSClient can send DNS Update and receive a
  305. // corresponding response from a server.
  306. void runSendReceiveTest(const bool corrupt_response,
  307. const bool two_sends) {
  308. corrupt_response_ = corrupt_response;
  309. // Create a request DNS Update message.
  310. D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
  311. ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
  312. ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
  313. // In order to perform the full test, when the client sends the request
  314. // and receives a response from the server, we have to emulate the
  315. // server's response in the test. A request will be sent via loopback
  316. // interface to 127.0.0.1 and known test port. Response must be sent
  317. // to 127.0.0.1 and a source port which has been used to send the
  318. // request. A new socket is created, specifically to handle sending
  319. // responses. The reuse address option is set so as both sockets can
  320. // use the same address. This new socket is bound to the test address
  321. // and port, where requests will be sent.
  322. udp::socket udp_socket(service_.get_io_service(), asio::ip::udp::v4());
  323. udp_socket.set_option(socket_base::reuse_address(true));
  324. udp_socket.bind(udp::endpoint(address::from_string(TEST_ADDRESS),
  325. TEST_PORT));
  326. // Once socket is created, we can post an IO request to receive some
  327. // a packet from this socket. This is asynchronous operation and
  328. // nothing is received until another IO request to send a query is
  329. // posted and the run() is invoked on this IO. A callback function is
  330. // attached to this asynchronous read. This callback function requires
  331. // that a socket object used to receive the request is passed to it,
  332. // because the same socket will be used by the callback function to send
  333. // a response. Also, the remote object is passed to the callback,
  334. // because it holds a source address and port where request originated.
  335. // Callback function will send a response to this address and port.
  336. // The last parameter holds a length of the received request. It is
  337. // required to construct a response.
  338. udp::endpoint remote;
  339. udp_socket.async_receive_from(asio::buffer(receive_buffer_,
  340. sizeof(receive_buffer_)),
  341. remote,
  342. boost::bind(&DNSClientTest::udpReceiveHandler,
  343. this, &udp_socket, &remote, _2,
  344. corrupt_response));
  345. // The socket is now ready to receive the data. Let's post some request
  346. // message then. Set timeout to some reasonable value to make sure that
  347. // there is sufficient amount of time for the test to generate a
  348. // response.
  349. const int timeout = 500;
  350. expected_++;
  351. dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS), TEST_PORT,
  352. message, timeout);
  353. // It is possible to request that two packets are sent concurrently.
  354. if (two_sends) {
  355. expected_++;
  356. dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS), TEST_PORT,
  357. message, timeout);
  358. }
  359. // Kick of the message exchange by actually running the scheduled
  360. // "send" and "receive" operations.
  361. service_.run();
  362. udp_socket.close();
  363. // Since the callback, operator(), calls stop() on the io_service,
  364. // we must reset it in order for subsequent calls to run() or
  365. // run_one() to work.
  366. service_.get_io_service().reset();
  367. }
  368. // Performs a single request-response exchange with or without TSIG
  369. //
  370. // @param client_key TSIG passed to dns_client and also used by the
  371. // ""server" to verify the request.
  372. // request.
  373. // @param server_key TSIG key the "server" should use to sign the response.
  374. // If this is NULL, then client_key is used.
  375. // @param should_pass indicates if the test should pass.
  376. void runTSIGTest(TSIGKeyPtr client_key, TSIGKeyPtr server_key,
  377. bool should_pass = true) {
  378. // Tell operator() method if we expect an invalid response.
  379. corrupt_response_ = !should_pass;
  380. // Create a request DNS Update message.
  381. D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
  382. ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
  383. ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
  384. // Setup our "loopback" server.
  385. udp::socket udp_socket(service_.get_io_service(), asio::ip::udp::v4());
  386. udp_socket.set_option(socket_base::reuse_address(true));
  387. udp_socket.bind(udp::endpoint(address::from_string(TEST_ADDRESS),
  388. TEST_PORT));
  389. udp::endpoint remote;
  390. udp_socket.async_receive_from(asio::buffer(receive_buffer_,
  391. sizeof(receive_buffer_)),
  392. remote,
  393. boost::bind(&DNSClientTest::
  394. TSIGReceiveHandler, this,
  395. &udp_socket, &remote, _2,
  396. client_key, server_key));
  397. // The socket is now ready to receive the data. Let's post some request
  398. // message then. Set timeout to some reasonable value to make sure that
  399. // there is sufficient amount of time for the test to generate a
  400. // response.
  401. const int timeout = 500;
  402. expected_++;
  403. dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS), TEST_PORT,
  404. message, timeout, client_key);
  405. // Kick of the message exchange by actually running the scheduled
  406. // "send" and "receive" operations.
  407. service_.run();
  408. udp_socket.close();
  409. // Since the callback, operator(), calls stop() on the io_service,
  410. // we must reset it in order for subsequent calls to run() or
  411. // run_one() to work.
  412. service_.get_io_service().reset();
  413. }
  414. };
  415. // Verify that the DNSClient object can be created if provided parameters are
  416. // valid. Constructor should throw exceptions when parameters are invalid.
  417. TEST_F(DNSClientTest, constructor) {
  418. runConstructorTest();
  419. }
  420. // This test verifies that the maximal allowed timeout value is maximal int
  421. // value.
  422. TEST_F(DNSClientTest, getMaxTimeout) {
  423. EXPECT_EQ(std::numeric_limits<int>::max(), DNSClient::getMaxTimeout());
  424. }
  425. // Verify that timeout is reported when no response is received from DNS.
  426. TEST_F(DNSClientTest, timeout) {
  427. runSendNoReceiveTest();
  428. }
  429. // Verify that exception is thrown when invalid (too high) timeout value is
  430. // specified for asynchronous DNS Update.
  431. TEST_F(DNSClientTest, invalidTimeout) {
  432. runInvalidTimeoutTest();
  433. }
  434. // Verifies that TSIG can be used to sign requests and verify responses.
  435. TEST_F(DNSClientTest, runTSIGTest) {
  436. std::string secret ("key number one");
  437. TSIGKeyPtr key_one;
  438. ASSERT_NO_THROW(key_one.reset(new
  439. TSIGKey(Name("one.com"),
  440. TSIGKey::HMACMD5_NAME(),
  441. secret.c_str(), secret.size())));
  442. secret = "key number two";
  443. TSIGKeyPtr key_two;
  444. ASSERT_NO_THROW(key_two.reset(new
  445. TSIGKey(Name("two.com"),
  446. TSIGKey::HMACMD5_NAME(),
  447. secret.c_str(), secret.size())));
  448. TSIGKeyPtr nokey;
  449. // Should be able to send and receive with no keys.
  450. // Neither client nor server will attempt to sign or verify.
  451. runTSIGTest(nokey, nokey);
  452. // Client signs the request, server verfies but doesn't sign.
  453. runTSIGTest(key_one, nokey, false);
  454. // Client and server use the same key to sign and verify.
  455. runTSIGTest(key_one, key_one);
  456. // Server uses different key to sign the response.
  457. runTSIGTest(key_one, key_two, false);
  458. // Client neither signs nor verifies, server responds with a signed answer
  459. // Since we are "liberal" in what we accept this should be ok.
  460. runTSIGTest(nokey, key_two);
  461. }
  462. // Verify that the DNSClient receives the response from DNS and the received
  463. // buffer can be decoded as DNS Update Response.
  464. TEST_F(DNSClientTest, sendReceive) {
  465. // false means that server response is not corrupted.
  466. runSendReceiveTest(false, false);
  467. }
  468. // Verify that the DNSClient reports an error when the response is received from
  469. // a DNS and this response is corrupted.
  470. TEST_F(DNSClientTest, sendReceiveCurrupted) {
  471. // true means that server's response is corrupted.
  472. runSendReceiveTest(true, false);
  473. }
  474. // Verify that it is possible to use the same DNSClient instance to
  475. // perform the following sequence of message exchanges:
  476. // 1. send
  477. // 2. receive
  478. // 3. send
  479. // 4. receive
  480. TEST_F(DNSClientTest, sendReceiveTwice) {
  481. runSendReceiveTest(false, false);
  482. runSendReceiveTest(false, false);
  483. EXPECT_EQ(2, received_);
  484. }
  485. // Verify that it is possible to use the DNSClient instance to perform the
  486. // following sequence of message exchanges:
  487. // 1. send
  488. // 2. send
  489. // 3. receive
  490. // 4. receive
  491. // @todo THIS Test does not function. The method runSendReceive only
  492. // schedules one "server" receive. In other words only one request is
  493. // listened for and then received. Once it is received, the operator()
  494. // method calls stop() on the io_service, which causes the second receive
  495. // to be cancelled. It is also unclear, what the asio layer does with a
  496. // second receive on the same socket.
  497. TEST_F(DNSClientTest, DISABLED_concurrentSendReceive) {
  498. runSendReceiveTest(false, true);
  499. }
  500. } // End of anonymous namespace