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>
33 #include "host/pingprotocol.h"
34 #include "dns/hostaddress.h"
35 #include "dns/dnsmaster.h"
36 #include "dns/dnscache.h"
37 #include "dns/resolverbase.h"
38 #include "dns/cname.h"
40 using boost::asio::ip::address;
42 // constants for master
43 address name_server = address::from_string("127.0.0.1");
44 const int resolved_ip_ttl_threshold = 5; // this should be greater than...
45 const uint32_t min_time_between_resolves = 3; // ... this!
46 const int max_address_resolution_attempts = 2;
47 const int max_recursion_count = 10;
48 const std::string cache_file = DnsCache::DoNotUseCacheFile;
50 //------------------------------------------------------------------------------
51 // Global Test fixture (created once for test suite)
52 //------------------------------------------------------------------------------
54 // rely on boost that there will only be one instance
56 GlobalFixture *global_fixture;
67 BOOST_TEST_MESSAGE("Create global fixture");
69 // setup logging so we can see output from out code
70 I2n::Logger::enable_stderr_log( true );
71 I2n::Logger::set_log_level( I2n::Logger::LogLevel::Info );
72 I2n::Logger::GlobalLogger.info() << "Logging enabled for DnsTest";
75 IoServiceItem io_service_temp( new boost::asio::io_service() );
76 io_service_temp.swap( IoService );
77 io_service_temp.reset();
80 DnsCacheItem cache_temp = DnsCacheItem(
81 new DnsCache(IoService, cache_file, min_time_between_resolves) );
82 cache_temp.swap( Cache );
87 DnsMaster::create_master(IoService,
89 resolved_ip_ttl_threshold,
90 max_address_resolution_attempts,
93 Master = DnsMaster::get_instance();
95 // remember this instance, so we can later access all these variables
96 if (global_fixture == 0)
97 global_fixture = this;
102 BOOST_TEST_MESSAGE("Destructing global fixture");
110 BOOST_TEST_MESSAGE( "Filling cache..." );
112 HostAddress ip(address::from_string("192.168.42.1"), 61);
115 Cache->update("host1.test", ips);
119 HostAddress ip1(address::from_string("192.168.42.2"), 92);
120 HostAddress ip2(address::from_string("192.168.42.3"), 93);
124 Cache->update("host2_3.test", ips);
128 // cname.test --> host1.test
129 Cache->update("cname.test", Cname("host1.test", 35) );
131 // cname2.test --> cname.test --> host1.test
132 Cache->update("cname2.test", Cname("cname.test", 33) );
134 // cname3.test --> cname2.test --> cname.test --> host1.test
135 Cache->update("cname3.test", Cname("cname2.test", 37) );
138 { // for cache_outdated_test
140 ips.push_back( HostAddress( address::from_string("192.168.42.4"),
142 ips.push_back( HostAddress( address::from_string("192.168.42.5"),
143 resolved_ip_ttl_threshold-1 ) );
144 ips.push_back( HostAddress( address::from_string("192.168.42.6"),
145 resolved_ip_ttl_threshold ) );
146 ips.push_back( HostAddress( address::from_string("192.168.42.7"),
147 resolved_ip_ttl_threshold+1 ) );
148 Cache->update("host_outdated.test", ips);
150 Cache->update( "cname_outdated.test",
151 Cname("host_outdated.test", resolved_ip_ttl_threshold) );
152 Cache->update( "cname_up_to_date.test",
153 Cname("host_outdated.test", resolved_ip_ttl_threshold+1));
156 { // for cache_ttl_below_thresh_test
157 // TTLs < min_time_between_resolves should be corrected
159 ips.push_back( HostAddress( address::from_string("192.128.42.8"),
161 Cache->update("host_ttl_below_thresh.test", ips);
163 Cache->update( "cname_ttl_below_thresh.test",
164 Cname("host_ttl_below_thresh.test", 2) );
167 { // create a CNAME loop cname_loop1.test --> cname_loop2.test
168 // --> cname_loop3.test --> cname_loop1.test
169 Cache->update( "cname_loop1.test", Cname("cname_loop2.test", 60) );
170 Cache->update( "cname_loop2.test", Cname("cname_loop3.test", 60) );
171 Cache->update( "cname_loop3.test", Cname("cname_loop1.test", 60) );
174 BOOST_TEST_MESSAGE( "Done filling cache." );
177 // these variables will not be available in test cases:
178 IoServiceItem IoService;
180 DnsMasterItem Master;
183 // this causes above fixture to be created only once before tests start and
184 // destructed after tests end; however, variables are not accessible in test
186 BOOST_GLOBAL_FIXTURE( GlobalFixture )
189 // using this as suite-level fixture makes variable Master accessible in all
193 IoServiceItem IoService;
195 DnsMasterItem Master;
202 BOOST_TEST_MESSAGE("Create test-level fixture");
203 GlobalFixture *global = global_fixture;
204 IoService = global->IoService;
205 Cache = global->Cache;
206 Master = global->Master;
209 virtual ~TestFixture() {}
212 //------------------------------------------------------------------------------
214 //------------------------------------------------------------------------------
216 BOOST_FIXTURE_TEST_SUITE( TestDns, TestFixture )
218 BOOST_AUTO_TEST_CASE( create_master )
221 BOOST_CHECK_EQUAL( Master->get_resolved_ip_ttl_threshold(),
222 resolved_ip_ttl_threshold );
223 BOOST_CHECK_EQUAL( Master->get_max_address_resolution_attempts(),
224 max_address_resolution_attempts );
227 //------------------------------------------------------------------------------
229 //------------------------------------------------------------------------------
230 BOOST_FIXTURE_TEST_SUITE( TestDnsCache, TestFixture )
232 BOOST_AUTO_TEST_CASE( cache_retrieve_ip1 )
234 HostAddressVec ips = Cache->get_ips("host1.test");
235 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
236 HostAddress ip = ips.front();
237 BOOST_CHECK( ip.is_valid() );
238 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
239 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 61 );
243 BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 )
245 HostAddressVec ips = Cache->get_ips("host2_3.test");
246 BOOST_CHECK_EQUAL( ips.size(), 2 );
247 HostAddress ip = ips[0];
248 BOOST_CHECK( ip.is_valid() );
249 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.2" );
250 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 92 );
252 BOOST_CHECK( ip.is_valid() );
253 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.3" );
254 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 93 );
257 BOOST_AUTO_TEST_CASE( cache_retrieve_cname )
259 HostAddressVec ips = Cache->get_ips("cname.test");
260 BOOST_CHECK( ips.empty() );
262 Cname cname = Cache->get_cname("cname.test");
263 BOOST_CHECK_EQUAL( cname.Host, "host1.test" );
264 BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 35 );
267 BOOST_AUTO_TEST_CASE( cache_retrieve_recursive1 )
269 // should get IP from host1 but ttl from cname since is smaller
270 HostAddressVec ips = Cache->get_ips_recursive("cname.test");
271 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
272 HostAddress ip = ips.front();
273 BOOST_CHECK( ip.is_valid() );
274 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
275 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 35 );
278 BOOST_AUTO_TEST_CASE( cache_retrieve_recursive2 )
280 // should get IP from host1 but ttl from cname2 since is smaller
281 HostAddressVec ips = Cache->get_ips_recursive("cname3.test");
282 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
283 HostAddress ip = ips.front();
284 BOOST_CHECK( ip.is_valid() );
285 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
286 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 33 );
289 void cname_skip_test(const uint32_t ttl1, const uint32_t ttl2,
290 const uint32_t ttl3, const uint32_t ttl4,
291 const DnsCacheItem &cache,
292 const std::string &correct_host_after_skip)
294 { // create cname chain:
295 // skip_chain_first -(ttl1)-> skip_chain_second -(ttl2)->
296 // skip_chain_third -(ttl3)-> skip_chain_fourth -(ttl4)-> IPs
297 cache->update( "skip_chain_first.test",
298 Cname("skip_chain_second.test", ttl1) );
299 cache->update( "skip_chain_second.test",
300 Cname("skip_chain_third.test", ttl2) );
301 cache->update( "skip_chain_third.test",
302 Cname("skip_chain_fourth.test", ttl3) );
304 ips.push_back( HostAddress( address::from_string("192.168.42.100"),
306 cache->update("skip_chain_fourth.test", ips);
309 // normal recursive call should give nothing since one cname is outdated
310 bool check_up_to_date = true;
311 HostAddressVec ips = cache->get_ips_recursive("skip_chain_first.test",
313 bool one_is_out_of_date = (ttl1 < 5) || (ttl2 < 5)
314 || (ttl3 < 5) || (ttl4 < 5);
315 BOOST_CHECK_EQUAL( ips.empty(), one_is_out_of_date );
317 // now find host to resolve after the outdated one
318 std::string first_outdated = cache->get_first_outdated_cname(
319 "skip_chain_first.test", 5);
320 BOOST_CHECK_EQUAL( first_outdated, correct_host_after_skip );
323 BOOST_AUTO_TEST_CASE( cache_skip_tests )
325 // build a cname chain where first one is out of date
326 cname_skip_test(0, 120, 120, 60, Cache, "skip_chain_second.test");
328 // build a cname chain where second one is out of date
329 cname_skip_test(120, 0, 120, 60, Cache, "skip_chain_third.test");
331 // build a cname chain where third one is out of date
332 cname_skip_test(120, 120, 0, 120, Cache, "skip_chain_fourth.test");
334 // build a cname chain where just IPs are out of date
335 cname_skip_test(120, 120, 120, 0, Cache, "");
337 // build a cname chain where all are out of date
338 cname_skip_test(0, 0, 0, 0, Cache, "skip_chain_second.test");
340 // build a cname chain where all is up to date
341 cname_skip_test(120, 120, 120, 120, Cache, "");
343 // test case with very short cname list that is up-to-date
344 BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("cname.test", 5), "" );
346 // test case where there is no cname at all
347 BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("host1.test", 5), "" );
350 BOOST_AUTO_TEST_CASE( cache_load_test )
352 std::stringstream file_name;
353 file_name << DATA_DIR_STRING << "/" << "dns_cache_example.xml";
354 BOOST_TEST_MESSAGE( "loading cache from file " << file_name.str() );
355 DnsCache loaded_cache( IoService,
357 min_time_between_resolves );
358 HostAddressVec ips = loaded_cache.get_ips("abc.xyz");
359 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
360 HostAddress ip = ips.front();
361 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "11.22.33.44" );
362 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 567 );
363 BOOST_CHECK_EQUAL( ip.get_ttl().get_updated_value(), 0 );
365 Cname cname = loaded_cache.get_cname("cname1.xyz");
366 BOOST_CHECK_EQUAL( cname.Host, "abc.xyz" );
367 BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 27 );
368 BOOST_CHECK_EQUAL( cname.Ttl.get_updated_value(), 0 );
370 // not testing Ttl set time since is private
373 BOOST_AUTO_TEST_CASE( cache_outdated_test )
375 bool check_up_to_date = false;
376 HostAddressVec ips = Cache->get_ips("host_outdated.test", check_up_to_date);
377 BOOST_CHECK_EQUAL( ips.size(), 4 );
378 ips = Cache->get_ips_recursive("host_outdated.test", check_up_to_date);
379 BOOST_CHECK_EQUAL( ips.size(), 4 );
380 Cname cname = Cache->get_cname("cname_outdated.test", check_up_to_date);
381 BOOST_CHECK( !cname.Host.empty() );
382 ips = Cache->get_ips_recursive("cname_outdated.test", check_up_to_date);
383 BOOST_CHECK_EQUAL( ips.size(), 4 );
384 ips = Cache->get_ips_recursive("cname_up_to_date.test", check_up_to_date);
385 BOOST_CHECK_EQUAL( ips.size(), 4 );
387 check_up_to_date = true;
388 ips = Cache->get_ips( "host_outdated.test", check_up_to_date );
389 BOOST_CHECK_EQUAL( ips.size(), 1 );
390 ips = Cache->get_ips_recursive("host_outdated.test", check_up_to_date);
391 BOOST_CHECK_EQUAL( ips.size(), 1 );
392 cname = Cache->get_cname("cname_outdated.test", check_up_to_date);
393 BOOST_CHECK( cname.Host.empty() );
394 ips = Cache->get_ips_recursive("cname_outdated.test", check_up_to_date);
395 BOOST_CHECK_EQUAL( ips.size(), 0 );
396 ips = Cache->get_ips_recursive("cname_up_to_date.test", check_up_to_date);
397 BOOST_CHECK_EQUAL( ips.size(), 1 );
400 BOOST_AUTO_TEST_CASE( cache_ttl_below_thresh_test )
402 HostAddressVec ips = Cache->get_ips("host_ttl_below_thresh.test", false);
403 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
404 HostAddress ip = ips.front();
405 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), min_time_between_resolves );
407 Cname cname = Cache->get_cname("cname_ttl_below_thresh.test", false);
408 BOOST_CHECK_EQUAL( cname.Ttl.get_value(), min_time_between_resolves );
411 BOOST_AUTO_TEST_CASE( cache_retrieve_loop )
413 BOOST_CHECK_EQUAL( Cache->get_ips_recursive("cname_loop1.test").size(), 0 );
414 BOOST_CHECK_EQUAL( Cache->get_ips_recursive("cname_loop2.test").size(), 0 );
415 BOOST_CHECK_EQUAL( Cache->get_ips_recursive("cname_loop3.test").size(), 0 );
419 BOOST_AUTO_TEST_SUITE_END() // of TestDnsCache
422 // -----------------------------------------------------------------------------
424 // -----------------------------------------------------------------------------
426 BOOST_FIXTURE_TEST_SUITE( TestDnsResolver, TestFixture )
428 BOOST_AUTO_TEST_CASE( create_resolver_v4 )
431 std::string hostname = "www.intra2net.com";
432 ResolverItem resolver = Master->get_resolver_for(hostname,
434 BOOST_CHECK_EQUAL( resolver->get_hostname(), hostname );
435 BOOST_CHECK( !resolver->is_resolving() );
436 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
438 // cancel should have no effect
439 resolver->cancel_resolve();
440 BOOST_CHECK( !resolver->is_resolving() );
441 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
443 // should not have any ips since cache did not load anything
444 BOOST_CHECK_EQUAL( resolver->get_resolved_ip_count(), 0 );
445 BOOST_CHECK( !resolver->have_up_to_date_ip() );
446 std::string no_ip = "0.0.0.0";
447 BOOST_CHECK_EQUAL( resolver->get_next_ip().get_ip().to_string(), no_ip );
451 struct ResolveCallback
454 IoServiceItem IoService;
455 ResolverItem Resolver;
457 bool LastCallWasSuccess;
458 int LastCallRecursionCount;
460 ResolveCallback(IoServiceItem &io_serv, ResolverItem &resolver)
464 , LastCallWasSuccess(true)
465 , LastCallRecursionCount(-10)
468 void call(const bool was_success, const int recursion_count)
470 BOOST_TEST_MESSAGE( "Callback called" );
473 Resolver->cancel_resolve();
476 LastCallWasSuccess = was_success;
477 LastCallRecursionCount = recursion_count;
481 BOOST_AUTO_TEST_CASE( do_resolve )
484 std::string hostname = "www.intra2net.com";
485 ResolverItem resolver = Master->get_resolver_for(hostname,
487 BOOST_CHECK( !resolver->is_resolving() );
488 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
491 ResolveCallback *callback_obj = new ResolveCallback(IoService, resolver);
492 // have to manually delete this obj in the end!
493 callback_type callback = boost::bind( &ResolveCallback::call, callback_obj,
497 resolver->async_resolve(callback);
498 BOOST_CHECK( resolver->is_resolving() );
500 // this will block until io service is stopped in resolve_callback
501 BOOST_TEST_MESSAGE( "Back to synchronous operation" );
505 BOOST_CHECK_EQUAL( callback_obj->CallCount, 1 );
506 BOOST_CHECK( callback_obj->LastCallWasSuccess );
507 BOOST_CHECK_EQUAL( callback_obj->LastCallRecursionCount, 0 );
508 BOOST_CHECK( resolver->have_up_to_date_ip() );
513 BOOST_AUTO_TEST_SUITE_END() // of TestDnsResolver
515 BOOST_AUTO_TEST_SUITE_END()