2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
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.
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.
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.
21 #define BOOST_TEST_MAIN
22 #define BOOST_TEST_DYN_LINK
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>
31 #include <logfunc.hpp>
32 #include <filefunc.hxx>
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"
42 using boost::asio::ip::address;
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;
52 //------------------------------------------------------------------------------
53 // Global Test fixture (created once for test suite)
54 //------------------------------------------------------------------------------
56 // rely on boost that there will only be one instance
58 GlobalFixture *global_fixture;
69 BOOST_TEST_MESSAGE("Create global fixture");
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";
77 IoServiceItem io_service_temp( new boost::asio::io_service() );
78 io_service_temp.swap( IoService );
79 io_service_temp.reset();
82 DnsCacheItem cache_temp = DnsCacheItem(
83 new DnsCache(IoService, cache_file, min_time_between_resolves) );
84 cache_temp.swap( Cache );
89 DnsMaster::create_master(IoService,
91 resolved_ip_ttl_threshold,
92 max_address_resolution_attempts,
95 Master = DnsMaster::get_instance();
97 // remember this instance, so we can later access all these variables
98 if (global_fixture == 0)
99 global_fixture = this;
104 BOOST_TEST_MESSAGE("Destructing global fixture");
112 BOOST_TEST_MESSAGE( "Filling cache..." );
114 HostAddress ip(address::from_string("192.168.42.1"), 61);
117 Cache->update("host1.test", DNS_IPv4, ips);
121 HostAddress ip1(address::from_string("192.168.42.2"), 92);
122 HostAddress ip2(address::from_string("192.168.42.3"), 93);
126 Cache->update("host2_3.test", DNS_IPv4, ips);
130 // cname.test --> host1.test
131 Cache->update("cname.test", Cname("host1.test", 35) );
133 // cname2.test --> cname.test --> host1.test
134 Cache->update("cname2.test", Cname("cname.test", 33) );
136 // cname3.test --> cname2.test --> cname.test --> host1.test
137 Cache->update("cname3.test", Cname("cname2.test", 37) );
140 { // for cache_outdated_test
142 ips.push_back( HostAddress( address::from_string("192.168.42.4"),
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);
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));
158 { // for cache_ttl_below_thresh_test
159 // TTLs < min_time_between_resolves should be corrected
161 ips.push_back( HostAddress( address::from_string("192.128.42.8"),
163 Cache->update("host_ttl_below_thresh.test", DNS_IPv4, ips);
165 Cache->update( "cname_ttl_below_thresh.test",
166 Cname("host_ttl_below_thresh.test", 2) );
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) );
176 { // create IPv4 and IPv6 for same host
178 ips.push_back( HostAddress( address::from_string("192.168.42.8"),
180 Cache->update("host_v4_and_v6.test", DNS_IPv4, ips);
182 ips.push_back( HostAddress(
183 address::from_string("2a00:1450:4001:808::1004"),
185 Cache->update("host_v4_and_v6.test", DNS_IPv6, ips);
188 BOOST_TEST_MESSAGE( "Done filling cache." );
191 // these variables will not be available in test cases:
192 IoServiceItem IoService;
194 DnsMasterItem Master;
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
200 BOOST_GLOBAL_FIXTURE( GlobalFixture )
203 // using this as suite-level fixture makes variable Master accessible in all
207 IoServiceItem IoService;
209 DnsMasterItem Master;
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;
223 virtual ~TestFixture() {}
226 //------------------------------------------------------------------------------
228 //------------------------------------------------------------------------------
230 BOOST_FIXTURE_TEST_SUITE( TestDns, TestFixture )
232 BOOST_AUTO_TEST_CASE( create_master )
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 );
241 //------------------------------------------------------------------------------
243 //------------------------------------------------------------------------------
244 BOOST_FIXTURE_TEST_SUITE( TestDnsCache, TestFixture )
246 BOOST_AUTO_TEST_CASE( cache_retrieve_ip1 )
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 );
257 BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 )
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 );
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 );
271 BOOST_AUTO_TEST_CASE( cache_retrieve_cname )
273 HostAddressVec ips = Cache->get_ips("cname.test", DNS_IPv4);
274 BOOST_CHECK( ips.empty() );
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 );
281 BOOST_AUTO_TEST_CASE( cache_retrieve_recursive1 )
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 );
292 BOOST_AUTO_TEST_CASE( cache_retrieve_recursive2 )
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 );
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)
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) );
318 ips.push_back( HostAddress( address::from_string("192.168.42.100"),
320 cache->update("skip_chain_fourth.test", DNS_IPv4, ips);
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",
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 );
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 );
338 BOOST_AUTO_TEST_CASE( cache_skip_tests )
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");
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");
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");
349 // build a cname chain where just IPs are out of date
350 cname_skip_test(120, 120, 120, 0, Cache, "");
352 // build a cname chain where all are out of date
353 cname_skip_test(0, 0, 0, 0, Cache, "skip_chain_second.test");
355 // build a cname chain where all is up to date
356 cname_skip_test(120, 120, 120, 120, Cache, "");
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), "" );
361 // test case where there is no cname at all
362 BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("host1.test", 5), "" );
366 void create_cache(const std::string &file_name, IoServiceItem io_serv)
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 );
374 ips.push_back( HostAddress(address::from_string("11.22.33.44"), 567) );
375 save_cache.update("abc.xyz", DNS_IPv4, ips);
377 save_cache.update("cname1.xyz", Cname("abc.xyz", 27));
379 // is saved when destructed
383 void test_cache(const std::string &file_name, IoServiceItem io_serv)
385 BOOST_TEST_MESSAGE( "loading cache from file " << file_name );
386 DnsCache loaded_cache( io_serv,
388 min_time_between_resolves );
389 HostAddressVec ips = loaded_cache.get_ips("abc.xyz", DNS_IPv4);
390 BOOST_CHECK_EQUAL( ips.size(), 1 );
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 );
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 );
404 // not testing Ttl set time since is private
408 BOOST_AUTO_TEST_CASE( cache_save_and_load_test )
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();
415 create_cache( file_name, IoService );
418 test_cache( file_name, IoService );
422 BOOST_AUTO_TEST_CASE( cache_load_compatibility_test )
424 std::stringstream file_name;
425 file_name << DATA_DIR_STRING << "/" << "dns_cache_compatibility_test.xml";
426 test_cache( file_name.str(), IoService );
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 );
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
439 BOOST_AUTO_TEST_CASE( cache_outdated_test )
441 bool check_up_to_date = false;
442 HostAddressVec ips = Cache->get_ips("host_outdated.test", DNS_IPv4,
444 BOOST_CHECK_EQUAL( ips.size(), 4 );
445 ips = Cache->get_ips_recursive("host_outdated.test", DNS_IPv4,
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,
452 BOOST_CHECK_EQUAL( ips.size(), 4 );
453 ips = Cache->get_ips_recursive("cname_up_to_date.test", DNS_IPv4,
455 BOOST_CHECK_EQUAL( ips.size(), 4 );
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,
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,
467 BOOST_CHECK_EQUAL( ips.size(), 0 );
468 ips = Cache->get_ips_recursive("cname_up_to_date.test", DNS_IPv4,
470 BOOST_CHECK_EQUAL( ips.size(), 1 );
473 BOOST_AUTO_TEST_CASE( cache_ttl_below_thresh_test )
475 HostAddressVec ips = Cache->get_ips("host_ttl_below_thresh.test", DNS_IPv4,
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 );
481 Cname cname = Cache->get_cname("cname_ttl_below_thresh.test", false);
482 BOOST_CHECK_EQUAL( cname.Ttl.get_value(), min_time_between_resolves );
485 BOOST_AUTO_TEST_CASE( cache_retrieve_loop )
488 Cache->get_ips_recursive("cname_loop1.test", DNS_IPv4).size(), 0 );
490 Cache->get_ips_recursive("cname_loop2.test", DNS_IPv4).size(), 0 );
492 Cache->get_ips_recursive("cname_loop3.test", DNS_IPv4).size(), 0 );
496 BOOST_AUTO_TEST_CASE( cache_IPv6 )
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 );
505 ips = Cache->get_ips("host_v4_and_v6.test", DNS_IPv6);
506 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
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 );
514 BOOST_AUTO_TEST_SUITE_END() // of TestDnsCache
517 // -----------------------------------------------------------------------------
519 // -----------------------------------------------------------------------------
521 BOOST_FIXTURE_TEST_SUITE( TestDnsResolver, TestFixture )
523 BOOST_AUTO_TEST_CASE( create_resolver_v4 )
526 std::string hostname = "www.intra2net.com";
527 ResolverItem resolver = Master->get_resolver_for(hostname,
529 BOOST_CHECK_EQUAL( resolver->get_hostname(), hostname );
530 BOOST_CHECK( !resolver->is_resolving() );
531 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
533 // cancel should have no effect
534 resolver->cancel_resolve();
535 BOOST_CHECK( !resolver->is_resolving() );
536 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
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 );
546 struct ResolveCallback
549 IoServiceItem IoService;
550 ResolverItem Resolver;
552 bool LastCallWasSuccess;
553 int LastCallRecursionCount;
555 ResolveCallback(IoServiceItem &io_serv, ResolverItem &resolver)
559 , LastCallWasSuccess(true)
560 , LastCallRecursionCount(-10)
563 void call(const bool was_success, const int recursion_count)
565 BOOST_TEST_MESSAGE( "Callback called" );
568 Resolver->cancel_resolve();
571 LastCallWasSuccess = was_success;
572 LastCallRecursionCount = recursion_count;
576 BOOST_AUTO_TEST_CASE( do_resolve )
579 std::string hostname = "www.intra2net.com";
580 ResolverItem resolver = Master->get_resolver_for(hostname,
582 BOOST_CHECK( !resolver->is_resolving() );
583 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
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,
592 resolver->async_resolve(callback);
593 BOOST_CHECK( resolver->is_resolving() );
595 // this will block until io service is stopped in resolve_callback
596 BOOST_TEST_MESSAGE( "Back to synchronous operation" );
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() );
608 BOOST_AUTO_TEST_SUITE_END() // of TestDnsResolver
610 BOOST_AUTO_TEST_SUITE_END()