7938aa3f5fde3daa0387c5900005f23a6c678ff3
[pingcheck] / test / test_dns.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20
21 #define BOOST_TEST_MAIN
22 #define BOOST_TEST_DYN_LINK
23
24 #include <algorithm>
25
26 #include <boost/test/unit_test.hpp>
27 #include <boost/asio/io_service.hpp>
28 #include <boost/asio/ip/address.hpp>
29 #include <boost/bind.hpp>
30
31 #include <logfunc.hpp>
32 #include <filefunc.hxx>
33
34 #include "host/pingprotocol.h"
35 #include "dns/hostaddress.h"
36 #include "dns/dnsipprotocol.h"
37 #include "dns/dnsmaster.h"
38 #include "dns/dnscache.h"
39 #include "dns/resolverbase.h"
40 #include "dns/cname.h"
41
42 using boost::asio::ip::address;
43
44 // constants for master
45 address name_server = address::from_string("127.0.0.1");
46 const int resolved_ip_ttl_threshold = 5;      // this should be greater than...
47 const uint32_t min_time_between_resolves = 3; // ... this!
48 const int max_address_resolution_attempts = 2;
49 const int max_recursion_count = 10;
50 const std::string cache_file = DnsCache::DoNotUseCacheFile;
51
52 //------------------------------------------------------------------------------
53 // Global Test fixture (created once for test suite)
54 //------------------------------------------------------------------------------
55
56 // rely on boost that there will only be one instance
57 struct GlobalFixture;
58 GlobalFixture *global_fixture;
59
60
61 struct GlobalFixture
62 {
63
64     GlobalFixture()
65         : IoService()
66         , Cache()
67         , Master()
68     {
69         BOOST_TEST_MESSAGE("Create global fixture");
70
71         // setup logging so we can see output from out code
72         I2n::Logger::enable_stderr_log( true );
73         I2n::Logger::set_log_level( I2n::Logger::LogLevel::Info );
74         I2n::Logger::GlobalLogger.info() << "Logging enabled for DnsTest";
75
76         // IoService
77         IoServiceItem io_service_temp( new boost::asio::io_service() );
78         io_service_temp.swap( IoService );
79         io_service_temp.reset();
80
81         // DNS Cache
82         DnsCacheItem cache_temp = DnsCacheItem(
83                new DnsCache(IoService, cache_file, min_time_between_resolves) );
84         cache_temp.swap( Cache );
85         cache_temp.reset();
86         fill_cache();
87
88         // create master
89         DnsMaster::create_master(IoService,
90                                  name_server,
91                                  resolved_ip_ttl_threshold,
92                                  max_address_resolution_attempts,
93                                  max_recursion_count,
94                                  Cache);
95         Master = DnsMaster::get_instance();
96
97         // remember this instance, so we can later access all these variables
98         if (global_fixture == 0)
99             global_fixture = this;
100     }
101
102     ~GlobalFixture()
103     {
104         BOOST_TEST_MESSAGE("Destructing global fixture");
105         IoService->stop();
106         IoService.reset();
107         Master.reset();
108     }
109
110     void fill_cache()
111     {
112         BOOST_TEST_MESSAGE( "Filling cache..." );
113         {
114             HostAddress ip(address::from_string("192.168.42.1"), 61);
115             HostAddressVec ips;
116             ips.push_back(ip);
117             Cache->update("host1.test", DNS_IPv4, ips);
118         }
119
120         {
121             HostAddress ip1(address::from_string("192.168.42.2"), 92);
122             HostAddress ip2(address::from_string("192.168.42.3"), 93);
123             HostAddressVec ips;
124             ips.push_back(ip1);
125             ips.push_back(ip2);
126             Cache->update("host2_3.test", DNS_IPv4, ips);
127         }
128
129         {
130             // cname.test --> host1.test
131             Cache->update("cname.test", Cname("host1.test", 35) );
132
133             // cname2.test --> cname.test --> host1.test
134             Cache->update("cname2.test", Cname("cname.test", 33) );
135
136             // cname3.test --> cname2.test --> cname.test --> host1.test
137             Cache->update("cname3.test", Cname("cname2.test", 37) );
138         }
139
140         {   // for cache_outdated_test
141             HostAddressVec ips;
142             ips.push_back( HostAddress( address::from_string("192.168.42.4"),
143                                         0 ) );
144             ips.push_back( HostAddress( address::from_string("192.168.42.5"),
145                                         resolved_ip_ttl_threshold-1 ) );
146             ips.push_back( HostAddress( address::from_string("192.168.42.6"),
147                                         resolved_ip_ttl_threshold ) );
148             ips.push_back( HostAddress( address::from_string("192.168.42.7"),
149                                         resolved_ip_ttl_threshold+1 ) );
150             Cache->update("host_outdated.test", DNS_IPv4, ips);
151
152             Cache->update( "cname_outdated.test",
153                       Cname("host_outdated.test", resolved_ip_ttl_threshold) );
154             Cache->update( "cname_up_to_date.test",
155                       Cname("host_outdated.test", resolved_ip_ttl_threshold+1));
156         }
157
158         {   // for cache_ttl_below_thresh_test
159             // TTLs < min_time_between_resolves should be corrected
160             HostAddressVec ips;
161             ips.push_back( HostAddress( address::from_string("192.128.42.8"),
162                                         1 ) );
163             Cache->update("host_ttl_below_thresh.test", DNS_IPv4, ips);
164
165             Cache->update( "cname_ttl_below_thresh.test",
166                       Cname("host_ttl_below_thresh.test", 2) );
167         }
168
169         {   // create a CNAME loop  cname_loop1.test --> cname_loop2.test
170             //                  --> cname_loop3.test --> cname_loop1.test
171             Cache->update( "cname_loop1.test", Cname("cname_loop2.test", 60) );
172             Cache->update( "cname_loop2.test", Cname("cname_loop3.test", 60) );
173             Cache->update( "cname_loop3.test", Cname("cname_loop1.test", 60) );
174         }
175
176         {   // create IPv4 and IPv6 for same host
177             HostAddressVec ips;
178             ips.push_back( HostAddress( address::from_string("192.168.42.8"),
179                                         432 ) );
180             Cache->update("host_v4_and_v6.test", DNS_IPv4, ips);
181             ips.clear();
182             ips.push_back( HostAddress(
183                         address::from_string("2a00:1450:4001:808::1004"),
184                         543 ) );
185             Cache->update("host_v4_and_v6.test", DNS_IPv6, ips);
186         }
187
188         BOOST_TEST_MESSAGE( "Done filling cache." );
189     }
190
191     // these variables will not be available in test cases:
192     IoServiceItem IoService;
193     DnsCacheItem Cache;
194     DnsMasterItem Master;
195 };
196
197 // this causes above fixture to be created only once before tests start and
198 // destructed after tests end; however, variables are not accessible in test
199 // cases
200 BOOST_GLOBAL_FIXTURE( GlobalFixture )
201
202
203 // using this as suite-level fixture makes variable Master accessible in all
204 //   test cases
205 struct TestFixture
206 {
207     IoServiceItem IoService;
208     DnsCacheItem Cache;
209     DnsMasterItem Master;
210
211     TestFixture()
212         : IoService()
213         , Cache()
214         , Master()
215     {
216         BOOST_TEST_MESSAGE("Create test-level fixture");
217         GlobalFixture *global = global_fixture;
218         IoService = global->IoService;
219         Cache = global->Cache;
220         Master = global->Master;
221     }
222
223     virtual ~TestFixture() {}
224 };
225
226 //------------------------------------------------------------------------------
227 // test suite
228 //------------------------------------------------------------------------------
229
230 BOOST_FIXTURE_TEST_SUITE( TestDns, TestFixture )
231
232 BOOST_AUTO_TEST_CASE( create_master )
233 {
234     // simple checks
235     BOOST_CHECK_EQUAL( Master->get_resolved_ip_ttl_threshold(),
236                                    resolved_ip_ttl_threshold );
237     BOOST_CHECK_EQUAL( Master->get_max_address_resolution_attempts(),
238                                    max_address_resolution_attempts );
239 }
240
241 //------------------------------------------------------------------------------
242 // test Cache
243 //------------------------------------------------------------------------------
244 BOOST_FIXTURE_TEST_SUITE( TestDnsCache, TestFixture )
245
246 BOOST_AUTO_TEST_CASE( cache_retrieve_ip1 )
247 {
248     HostAddressVec ips = Cache->get_ips("host1.test", DNS_IPv4);
249     BOOST_REQUIRE_EQUAL( ips.size(), 1 );
250     HostAddress ip = ips.front();
251     BOOST_CHECK( ip.is_valid() );
252     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
253     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 61 );
254 }
255
256
257 BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 )
258 {
259     HostAddressVec ips = Cache->get_ips("host2_3.test", DNS_IPv4);
260     BOOST_CHECK_EQUAL( ips.size(), 2 );
261     HostAddress ip = ips[0];
262     BOOST_CHECK( ip.is_valid() );
263     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.2" );
264     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 92 );
265     ip = ips[1];
266     BOOST_CHECK( ip.is_valid() );
267     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.3" );
268     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 93 );
269 }
270
271 BOOST_AUTO_TEST_CASE( cache_retrieve_cname )
272 {
273     HostAddressVec ips = Cache->get_ips("cname.test", DNS_IPv4);
274     BOOST_CHECK( ips.empty() );
275
276     Cname cname = Cache->get_cname("cname.test");
277     BOOST_CHECK_EQUAL( cname.Host, "host1.test" );
278     BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 35 );
279 }
280
281 BOOST_AUTO_TEST_CASE( cache_retrieve_recursive1 )
282 {
283     // should get IP from host1 but ttl from cname since is smaller
284     HostAddressVec ips = Cache->get_ips_recursive("cname.test", DNS_IPv4);
285     BOOST_REQUIRE_EQUAL( ips.size(), 1 );
286     HostAddress ip = ips.front();
287     BOOST_CHECK( ip.is_valid() );
288     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
289     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 35 );
290 }
291
292 BOOST_AUTO_TEST_CASE( cache_retrieve_recursive2 )
293 {
294     // should get IP from host1 but ttl from cname2 since is smaller
295     HostAddressVec ips = Cache->get_ips_recursive("cname3.test", DNS_IPv4);
296     BOOST_REQUIRE_EQUAL( ips.size(), 1 );
297     HostAddress ip = ips.front();
298     BOOST_CHECK( ip.is_valid() );
299     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
300     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 33 );
301 }
302
303 void cname_skip_test(const uint32_t ttl1, const uint32_t ttl2,
304                      const uint32_t ttl3, const uint32_t ttl4,
305                      const DnsCacheItem &cache,
306                      const std::string &correct_host_after_skip)
307 {
308     {   // create cname chain:
309         // skip_chain_first -(ttl1)-> skip_chain_second -(ttl2)->
310         //   skip_chain_third -(ttl3)-> skip_chain_fourth -(ttl4)-> IPs
311         cache->update( "skip_chain_first.test",
312                  Cname("skip_chain_second.test", ttl1) );
313         cache->update( "skip_chain_second.test",
314                  Cname("skip_chain_third.test", ttl2) );
315         cache->update( "skip_chain_third.test",
316                  Cname("skip_chain_fourth.test", ttl3) );
317         HostAddressVec ips;
318         ips.push_back( HostAddress( address::from_string("192.168.42.100"),
319                                     ttl4 ) );
320         cache->update("skip_chain_fourth.test", DNS_IPv4, ips);
321     }
322
323     // normal recursive call should give nothing since one cname is outdated
324     bool check_up_to_date = true;
325     HostAddressVec ips = cache->get_ips_recursive("skip_chain_first.test",
326                                                   DNS_IPv4,
327                                                   check_up_to_date);
328     bool one_is_out_of_date = (ttl1 < 5) || (ttl2 < 5)
329                            || (ttl3 < 5) || (ttl4 < 5);
330     BOOST_CHECK_EQUAL( ips.empty(), one_is_out_of_date );
331
332     // now find host to resolve after the outdated one
333     std::string first_outdated = cache->get_first_outdated_cname(
334                                                     "skip_chain_first.test", 5);
335     BOOST_CHECK_EQUAL( first_outdated, correct_host_after_skip );
336 }
337
338 BOOST_AUTO_TEST_CASE( cache_skip_tests )
339 {
340     // build a cname chain where first one is out of date
341     cname_skip_test(0, 120, 120, 60, Cache, "skip_chain_second.test");
342
343     // build a cname chain where second one is out of date
344     cname_skip_test(120, 0, 120, 60, Cache, "skip_chain_third.test");
345
346     // build a cname chain where third one is out of date
347     cname_skip_test(120, 120, 0, 120, Cache, "skip_chain_fourth.test");
348
349     // build a cname chain where just IPs are out of date
350     cname_skip_test(120, 120, 120, 0, Cache, "");
351
352     // build a cname chain where all are out of date
353     cname_skip_test(0, 0, 0, 0, Cache, "skip_chain_second.test");
354
355     // build a cname chain where all is up to date
356     cname_skip_test(120, 120, 120, 120, Cache, "");
357
358     // test case with very short cname list that is up-to-date
359     BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("cname.test", 5), "" );
360
361     // test case where there is no cname at all
362     BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("host1.test", 5), "" );
363 }
364
365
366 void create_cache(const std::string &file_name, IoServiceItem io_serv)
367 {
368     BOOST_TEST_MESSAGE( "creating cache for file " << file_name );
369     if ( I2n::file_exists(file_name) )
370         BOOST_CHECK( I2n::unlink(file_name) );   // try to remove
371     DnsCache save_cache( io_serv, file_name, min_time_between_resolves );
372
373     HostAddressVec ips;
374     ips.push_back( HostAddress(address::from_string("11.22.33.44"), 567) );
375     save_cache.update("abc.xyz", DNS_IPv4, ips);
376
377     save_cache.update("cname1.xyz", Cname("abc.xyz", 27));
378
379     // is saved when destructed
380 }
381
382
383 void test_cache(const std::string &file_name, IoServiceItem io_serv)
384 {
385     BOOST_TEST_MESSAGE( "loading cache from file " << file_name );
386     DnsCache loaded_cache( io_serv,
387                            file_name,
388                            min_time_between_resolves );
389     HostAddressVec ips = loaded_cache.get_ips("abc.xyz", DNS_IPv4);
390     BOOST_CHECK_EQUAL( ips.size(), 1 );
391     if (ips.size() == 1)
392     {
393         HostAddress ip = ips.front();
394         BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "11.22.33.44" );
395         BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 567 );
396         //BOOST_CHECK_EQUAL( ip.get_ttl().get_updated_value(), 0 );
397     }
398
399     Cname cname = loaded_cache.get_cname("cname1.xyz");
400     BOOST_CHECK_EQUAL( cname.Host, "abc.xyz" );
401     BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 27 );
402     //BOOST_CHECK_EQUAL( cname.Ttl.get_updated_value(), 0 );
403
404     // not testing Ttl set time since is private
405 }
406
407
408 BOOST_AUTO_TEST_CASE( cache_save_and_load_test )
409 {
410     std::stringstream file_name_temp;
411     file_name_temp << DATA_DIR_STRING << "/" << "dns_cache_save_load_test.xml";
412     std::string file_name = file_name_temp.str();
413
414     // create and save
415     create_cache( file_name, IoService );
416
417     //  now load and test
418     test_cache( file_name, IoService );
419
420 }
421
422 BOOST_AUTO_TEST_CASE( cache_load_compatibility_test )
423 {
424     std::stringstream file_name;
425     file_name << DATA_DIR_STRING << "/" << "dns_cache_compatibility_test.xml";
426     test_cache( file_name.str(), IoService );
427
428     // If this test fails because the cache structure was changed, 
429     // you can overwrite the dns_cache_compatibility_test.xml by uncommenting
430     // the following code line.
431     //create_cache( file_name.str(), IoService );
432     //
433     // Run test again with arg --log_level=MESSAGE and look out for message
434     //   "create cache for file [...]dns_cache_compatibility_test.xml"
435     // You should then copy the dns_cache_compatibility_test.xml to test/data
436     // in git and commit together with the new cache structure code
437 }
438
439 BOOST_AUTO_TEST_CASE( cache_outdated_test )
440 {
441     bool check_up_to_date = false;
442     HostAddressVec ips = Cache->get_ips("host_outdated.test", DNS_IPv4,
443                                                               check_up_to_date);
444     BOOST_CHECK_EQUAL( ips.size(), 4 );
445     ips = Cache->get_ips_recursive("host_outdated.test", DNS_IPv4,
446                                                          check_up_to_date);
447     BOOST_CHECK_EQUAL( ips.size(), 4 );
448     Cname cname = Cache->get_cname("cname_outdated.test", check_up_to_date);
449     BOOST_CHECK( !cname.Host.empty() );
450     ips = Cache->get_ips_recursive("cname_outdated.test", DNS_IPv4,
451                                                           check_up_to_date);
452     BOOST_CHECK_EQUAL( ips.size(), 4 );
453     ips = Cache->get_ips_recursive("cname_up_to_date.test", DNS_IPv4,
454                                                             check_up_to_date);
455     BOOST_CHECK_EQUAL( ips.size(), 4 );
456
457     check_up_to_date = true;
458     ips = Cache->get_ips( "host_outdated.test", DNS_IPv4, check_up_to_date );
459     BOOST_CHECK_EQUAL( ips.size(), 1 );
460     ips = Cache->get_ips_recursive("host_outdated.test", DNS_IPv4,
461                                                          check_up_to_date);
462     BOOST_CHECK_EQUAL( ips.size(), 1 );
463     cname = Cache->get_cname("cname_outdated.test", check_up_to_date);
464     BOOST_CHECK( cname.Host.empty() );
465     ips = Cache->get_ips_recursive("cname_outdated.test", DNS_IPv4,
466                                                           check_up_to_date);
467     BOOST_CHECK_EQUAL( ips.size(), 0 );
468     ips = Cache->get_ips_recursive("cname_up_to_date.test", DNS_IPv4,
469                                                             check_up_to_date);
470     BOOST_CHECK_EQUAL( ips.size(), 1 );
471 }
472
473 BOOST_AUTO_TEST_CASE( cache_ttl_below_thresh_test )
474 {
475     HostAddressVec ips = Cache->get_ips("host_ttl_below_thresh.test", DNS_IPv4,
476                                                                       false);
477     BOOST_REQUIRE_EQUAL( ips.size(), 1 );
478     HostAddress ip = ips.front();
479     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), min_time_between_resolves );
480
481     Cname cname = Cache->get_cname("cname_ttl_below_thresh.test", false);
482     BOOST_CHECK_EQUAL( cname.Ttl.get_value(), min_time_between_resolves );
483 }
484
485 BOOST_AUTO_TEST_CASE( cache_retrieve_loop )
486 {
487     BOOST_CHECK_EQUAL(
488             Cache->get_ips_recursive("cname_loop1.test", DNS_IPv4).size(), 0 );
489     BOOST_CHECK_EQUAL(
490             Cache->get_ips_recursive("cname_loop2.test", DNS_IPv4).size(), 0 );
491     BOOST_CHECK_EQUAL(
492             Cache->get_ips_recursive("cname_loop3.test", DNS_IPv4).size(), 0 );
493 }
494
495
496 BOOST_AUTO_TEST_CASE( cache_IPv6 )
497 {
498     HostAddressVec ips = Cache->get_ips("host_v4_and_v6.test", DNS_IPv4);
499     BOOST_REQUIRE_EQUAL( ips.size(), 1 );
500     HostAddress ip = ips.front();
501     BOOST_CHECK( ip.is_valid() );
502     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.8" );
503     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 432 );
504
505     ips = Cache->get_ips("host_v4_and_v6.test", DNS_IPv6);
506     BOOST_REQUIRE_EQUAL( ips.size(), 1 );
507     ip = ips.front();
508     BOOST_CHECK( ip.is_valid() );
509     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "2a00:1450:4001:808::1004" );
510     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 543 );
511 }
512
513
514 BOOST_AUTO_TEST_SUITE_END()    // of TestDnsCache
515
516
517 // -----------------------------------------------------------------------------
518 // test resolver
519 // -----------------------------------------------------------------------------
520
521 BOOST_FIXTURE_TEST_SUITE( TestDnsResolver, TestFixture )
522
523 BOOST_AUTO_TEST_CASE( create_resolver_v4 )
524 {
525     // create resolver
526     std::string hostname = "www.intra2net.com";
527     ResolverItem resolver = Master->get_resolver_for(hostname,
528                                                      PingProtocol_ICMP);
529     BOOST_CHECK_EQUAL( resolver->get_hostname(), hostname );
530     BOOST_CHECK( !resolver->is_resolving() );
531     BOOST_CHECK( !resolver->is_waiting_to_resolve() );
532
533     // cancel should have no effect
534     resolver->cancel_resolve();
535     BOOST_CHECK( !resolver->is_resolving() );
536     BOOST_CHECK( !resolver->is_waiting_to_resolve() );
537
538     // should not have any ips since cache did not load anything
539     BOOST_CHECK_EQUAL( resolver->get_resolved_ip_count(), 0 );
540     BOOST_CHECK( !resolver->have_up_to_date_ip() );
541     std::string no_ip = "0.0.0.0";
542     BOOST_CHECK_EQUAL( resolver->get_next_ip().get_ip().to_string(), no_ip );
543 }
544
545
546 struct ResolveCallback
547 {
548 public:
549     IoServiceItem IoService;
550     ResolverItem Resolver;
551     int CallCount;
552     bool LastCallWasSuccess;
553     int LastCallRecursionCount;
554
555     ResolveCallback(IoServiceItem &io_serv, ResolverItem &resolver)
556         : IoService(io_serv)
557         , Resolver(resolver)
558         , CallCount(0)
559         , LastCallWasSuccess(true)
560         , LastCallRecursionCount(-10)
561     {}
562
563     void call(const bool was_success, const int recursion_count)
564     {
565         BOOST_TEST_MESSAGE( "Callback called" );
566         ++CallCount;
567
568         Resolver->cancel_resolve();
569         IoService->stop();
570
571         LastCallWasSuccess = was_success;
572         LastCallRecursionCount = recursion_count;
573     }
574 };
575
576 BOOST_AUTO_TEST_CASE( do_resolve )
577 {
578     // create resolver
579     std::string hostname = "www.intra2net.com";
580     ResolverItem resolver = Master->get_resolver_for(hostname,
581                                                      PingProtocol_ICMP);
582     BOOST_CHECK( !resolver->is_resolving() );
583     BOOST_CHECK( !resolver->is_waiting_to_resolve() );
584
585     // set callback
586     ResolveCallback *callback_obj = new ResolveCallback(IoService, resolver);
587     // have to manually delete this obj in the end!
588     callback_type callback = boost::bind( &ResolveCallback::call, callback_obj,
589                                           _1, _2 );
590
591     // start resolving
592     resolver->async_resolve(callback);
593     BOOST_CHECK( resolver->is_resolving() );
594     IoService->run();
595     // this will block until io service is stopped in resolve_callback
596     BOOST_TEST_MESSAGE( "Back to synchronous operation" );
597     IoService->reset();
598
599     // check for result
600     BOOST_CHECK_EQUAL( callback_obj->CallCount, 1 );
601     BOOST_CHECK( callback_obj->LastCallWasSuccess );
602     BOOST_CHECK_EQUAL( callback_obj->LastCallRecursionCount, 0 );
603     BOOST_CHECK( resolver->have_up_to_date_ip() );
604
605     delete callback_obj;
606 }
607
608 BOOST_AUTO_TEST_SUITE_END()   // of TestDnsResolver
609
610 BOOST_AUTO_TEST_SUITE_END()