added option min-time-between-resolves-option and tests for it
[pingcheck] / test / test_dns.cpp
CommitLineData
26b0f687
CH
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on 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>
8d26221d
CH
29#include <boost/bind.hpp>
30
31#include <logfunc.hpp>
26b0f687
CH
32
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"
8d26221d
CH
38#include "dns/cname.h"
39
40using boost::asio::ip::address;
26b0f687
CH
41
42//------------------------------------------------------------------------------
8d26221d 43// Global Test fixture (created once for test suite)
26b0f687
CH
44//------------------------------------------------------------------------------
45
8d26221d
CH
46// constants for master
47address name_server = address::from_string("127.0.0.1");
f833126b
CH
48int resolved_ip_ttl_threshold = 5;
49uint32_t min_time_between_resolves = 3; // should be < resolved_ip_ttl_threshold
8d26221d
CH
50int max_address_resolution_attempts = 2;
51std::string cache_file = DnsCache::DoNotUseCacheFile;
52
53// rely on boost that there will only be one instance
54struct GlobalFixture;
55GlobalFixture *global_fixture;
26b0f687 56
8d26221d
CH
57
58struct GlobalFixture
26b0f687 59{
8d26221d
CH
60
61 GlobalFixture()
62 : IoService()
63 , Cache()
64 , Master()
26b0f687 65 {
8d26221d
CH
66 BOOST_TEST_MESSAGE("Create global fixture");
67
68 // setup logging so we can see output from out code
69 I2n::Logger::enable_stderr_log( true );
4389b86d 70 I2n::Logger::set_log_level( I2n::Logger::LogLevel::Info );
8d26221d
CH
71 I2n::Logger::GlobalLogger.info() << "Logging enabled for DnsTest";
72
73 // IoService
74 IoServiceItem io_service_temp( new boost::asio::io_service() );
75 io_service_temp.swap( IoService );
76 io_service_temp.reset();
77
78 // DNS Cache
79 DnsCacheItem cache_temp = DnsCacheItem(
f833126b 80 new DnsCache(IoService, cache_file, min_time_between_resolves) );
8d26221d
CH
81 cache_temp.swap( Cache );
82 cache_temp.reset();
83 fill_cache();
84
85 // create master
86 DnsMaster::create_master(IoService,
26b0f687
CH
87 name_server,
88 resolved_ip_ttl_threshold,
89 max_address_resolution_attempts,
8d26221d
CH
90 Cache);
91 Master = DnsMaster::get_instance();
92
93 // remember this instance, so we can later access all these variables
94 if (global_fixture == 0)
95 global_fixture = this;
26b0f687 96 }
8d26221d
CH
97
98 ~GlobalFixture()
99 {
100 BOOST_TEST_MESSAGE("Destructing global fixture");
101 IoService->stop();
102 IoService.reset();
103 Master.reset();
104 }
105
106 void fill_cache()
107 {
108 BOOST_TEST_MESSAGE( "Filling cache..." );
109 {
110 HostAddress ip(address::from_string("192.168.42.1"), 61);
111 HostAddressVec ips;
112 ips.push_back(ip);
113 Cache->update("host1.test", ips);
114 }
115
116 {
117 HostAddress ip1(address::from_string("192.168.42.2"), 92);
118 HostAddress ip2(address::from_string("192.168.42.3"), 93);
119 HostAddressVec ips;
120 ips.push_back(ip1);
121 ips.push_back(ip2);
122 Cache->update("host2_3.test", ips);
123 }
124
00c81aa0
CH
125 {
126 // cname.test --> host1.test
127 Cache->update("cname.test", Cname("host1.test", 35) );
8d26221d 128
00c81aa0
CH
129 // cname2.test --> cname.test --> host1.test
130 Cache->update("cname2.test", Cname("cname.test", 33) );
9d1b2726 131
00c81aa0
CH
132 // cname3.test --> cname2.test --> cname.test --> host1.test
133 Cache->update("cname3.test", Cname("cname2.test", 37) );
134 }
135
f833126b 136 { // for cache_outdated_test
00c81aa0
CH
137 HostAddressVec ips;
138 ips.push_back( HostAddress( address::from_string("192.168.42.4"),
139 0 ) );
140 ips.push_back( HostAddress( address::from_string("192.168.42.5"),
141 resolved_ip_ttl_threshold-1 ) );
142 ips.push_back( HostAddress( address::from_string("192.168.42.6"),
143 resolved_ip_ttl_threshold ) );
144 ips.push_back( HostAddress( address::from_string("192.168.42.7"),
145 resolved_ip_ttl_threshold+1 ) );
146 Cache->update("host_outdated.test", ips);
147
148 Cache->update( "cname_outdated.test",
149 Cname("host_outdated.test", resolved_ip_ttl_threshold) );
150 Cache->update( "cname_up_to_date.test",
151 Cname("host_outdated.test", resolved_ip_ttl_threshold+1));
152 }
8d26221d 153
f833126b
CH
154 { // for cache_ttl_below_thresh_test
155 // TTLs < min_time_between_resolves should be corrected
156 HostAddressVec ips;
157 ips.push_back( HostAddress( address::from_string("192.128.42.8"),
158 1 ) );
159 Cache->update("host_ttl_below_thresh.test", ips);
160
161 Cache->update( "cname_ttl_below_thresh.test",
162 Cname("host_ttl_below_thresh.test", 2) );
163 }
164
8d26221d
CH
165 BOOST_TEST_MESSAGE( "Done filling cache." );
166 }
167
168 // these variables will not be available in test cases:
169 IoServiceItem IoService;
170 DnsCacheItem Cache;
171 DnsMasterItem Master;
172};
173
174// this causes above fixture to be created only once before tests start and
175// destructed after tests end; however, variables are not accessible in test
176// cases
177BOOST_GLOBAL_FIXTURE( GlobalFixture )
178
179
180// using this as suite-level fixture makes variable Master accessible in all
181// test cases
182struct TestFixture
183{
184 IoServiceItem IoService;
185 DnsCacheItem Cache;
186 DnsMasterItem Master;
187
188 TestFixture()
189 : IoService()
190 , Cache()
191 , Master()
192 {
193 BOOST_TEST_MESSAGE("Create test-level fixture");
194 GlobalFixture *global = global_fixture;
195 IoService = global->IoService;
196 Cache = global->Cache;
197 Master = global->Master;
198 }
199
200 virtual ~TestFixture() {}
201};
26b0f687
CH
202
203//------------------------------------------------------------------------------
204// test suite
205//------------------------------------------------------------------------------
206
8d26221d 207BOOST_FIXTURE_TEST_SUITE( TestDns, TestFixture )
26b0f687
CH
208
209BOOST_AUTO_TEST_CASE( create_master )
210{
8d26221d
CH
211 // simple checks
212 BOOST_CHECK_EQUAL( Master->get_resolved_ip_ttl_threshold(),
213 resolved_ip_ttl_threshold );
214 BOOST_CHECK_EQUAL( Master->get_max_address_resolution_attempts(),
215 max_address_resolution_attempts );
26b0f687
CH
216}
217
8d26221d
CH
218//------------------------------------------------------------------------------
219// test Cache
220//------------------------------------------------------------------------------
221BOOST_FIXTURE_TEST_SUITE( TestDnsCache, TestFixture )
222
223BOOST_AUTO_TEST_CASE( cache_retrieve_ip1 )
26b0f687 224{
8d26221d 225 HostAddressVec ips = Cache->get_ips("host1.test");
f833126b 226 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
8d26221d 227 HostAddress ip = ips.front();
4389b86d 228 BOOST_CHECK( ip.is_valid() );
8d26221d
CH
229 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
230 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 61 );
231}
232
233
234BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 )
235{
236 HostAddressVec ips = Cache->get_ips("host2_3.test");
237 BOOST_CHECK_EQUAL( ips.size(), 2 );
238 HostAddress ip = ips[0];
4389b86d 239 BOOST_CHECK( ip.is_valid() );
8d26221d
CH
240 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.2" );
241 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 92 );
242 ip = ips[1];
4389b86d 243 BOOST_CHECK( ip.is_valid() );
8d26221d
CH
244 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.3" );
245 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 93 );
246}
26b0f687 247
8d26221d
CH
248BOOST_AUTO_TEST_CASE( cache_retrieve_cname )
249{
250 HostAddressVec ips = Cache->get_ips("cname.test");
251 BOOST_CHECK( ips.empty() );
252
253 Cname cname = Cache->get_cname("cname.test");
254 BOOST_CHECK_EQUAL( cname.Host, "host1.test" );
255 BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 35 );
256}
257
258BOOST_AUTO_TEST_CASE( cache_retrieve_recursive1 )
259{
260 // should get IP from host1 but ttl from cname since is smaller
261 HostAddressVec ips = Cache->get_ips_recursive("cname.test");
f833126b 262 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
8d26221d 263 HostAddress ip = ips.front();
4389b86d 264 BOOST_CHECK( ip.is_valid() );
8d26221d
CH
265 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
266 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 35 );
267}
268
269BOOST_AUTO_TEST_CASE( cache_retrieve_recursive2 )
270{
271 // should get IP from host1 but ttl from cname2 since is smaller
272 HostAddressVec ips = Cache->get_ips_recursive("cname3.test");
f833126b 273 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
8d26221d 274 HostAddress ip = ips.front();
4389b86d 275 BOOST_CHECK( ip.is_valid() );
8d26221d
CH
276 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
277 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 33 );
278}
279
9d1b2726
CH
280void cname_skip_test(const uint32_t ttl1, const uint32_t ttl2,
281 const uint32_t ttl3, const uint32_t ttl4,
282 const DnsCacheItem &cache,
283 const std::string &correct_host_after_skip)
8d26221d 284{
9d1b2726
CH
285 { // create cname chain:
286 // skip_chain_first -(ttl1)-> skip_chain_second -(ttl2)->
287 // skip_chain_third -(ttl3)-> skip_chain_fourth -(ttl4)-> IPs
288 cache->update( "skip_chain_first.test",
289 Cname("skip_chain_second.test", ttl1) );
290 cache->update( "skip_chain_second.test",
291 Cname("skip_chain_third.test", ttl2) );
292 cache->update( "skip_chain_third.test",
293 Cname("skip_chain_fourth.test", ttl3) );
8d26221d 294 HostAddressVec ips;
00c81aa0
CH
295 ips.push_back( HostAddress( address::from_string("192.168.42.100"),
296 ttl4 ) );
9d1b2726 297 cache->update("skip_chain_fourth.test", ips);
8d26221d
CH
298 }
299
9d1b2726 300 // normal recursive call should give nothing since one cname is outdated
8d26221d 301 bool check_up_to_date = true;
9d1b2726 302 HostAddressVec ips = cache->get_ips_recursive("skip_chain_first.test",
8d26221d 303 check_up_to_date);
9d1b2726
CH
304 bool one_is_out_of_date = (ttl1 < 5) || (ttl2 < 5)
305 || (ttl3 < 5) || (ttl4 < 5);
306 BOOST_CHECK_EQUAL( ips.empty(), one_is_out_of_date );
8d26221d 307
9d1b2726
CH
308 // now find host to resolve after the outdated one
309 std::string first_outdated = cache->get_first_outdated_cname(
8d26221d 310 "skip_chain_first.test", 5);
9d1b2726
CH
311 BOOST_CHECK_EQUAL( first_outdated, correct_host_after_skip );
312}
313
314BOOST_AUTO_TEST_CASE( cache_skip_tests )
315{
316 // build a cname chain where first one is out of date
317 cname_skip_test(0, 120, 120, 60, Cache, "skip_chain_second.test");
318
319 // build a cname chain where second one is out of date
320 cname_skip_test(120, 0, 120, 60, Cache, "skip_chain_third.test");
321
322 // build a cname chain where third one is out of date
323 cname_skip_test(120, 120, 0, 120, Cache, "skip_chain_fourth.test");
324
325 // build a cname chain where just IPs are out of date
326 cname_skip_test(120, 120, 120, 0, Cache, "");
327
328 // build a cname chain where all are out of date
329 cname_skip_test(0, 0, 0, 0, Cache, "skip_chain_second.test");
330
331 // build a cname chain where all is up to date
332 cname_skip_test(120, 120, 120, 120, Cache, "");
333
334 // test case with very short cname list that is up-to-date
335 BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("cname.test", 5), "" );
336
337 // test case where there is no cname at all
338 BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("host1.test", 5), "" );
8d26221d
CH
339}
340
4389b86d
CH
341BOOST_AUTO_TEST_CASE( cache_load_test )
342{
343 std::stringstream file_name;
344 file_name << DATA_DIR_STRING << "/" << "dns_cache_example.xml";
345 BOOST_TEST_MESSAGE( "loading cache from file " << file_name.str() );
f833126b
CH
346 DnsCache loaded_cache( IoService,
347 file_name.str(),
348 min_time_between_resolves );
4389b86d 349 HostAddressVec ips = loaded_cache.get_ips("abc.xyz");
f833126b 350 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
4389b86d
CH
351 HostAddress ip = ips.front();
352 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "11.22.33.44" );
353 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 567 );
354 BOOST_CHECK_EQUAL( ip.get_ttl().get_updated_value(), 0 );
355
356 Cname cname = loaded_cache.get_cname("cname1.xyz");
357 BOOST_CHECK_EQUAL( cname.Host, "abc.xyz" );
358 BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 27 );
359 BOOST_CHECK_EQUAL( cname.Ttl.get_updated_value(), 0 );
360
361 // not testing Ttl set time since is private
362}
363
00c81aa0
CH
364BOOST_AUTO_TEST_CASE( cache_outdated_test )
365{
366 bool check_up_to_date = false;
367 HostAddressVec ips = Cache->get_ips("host_outdated.test", check_up_to_date);
368 BOOST_CHECK_EQUAL( ips.size(), 4 );
369 ips = Cache->get_ips_recursive("host_outdated.test", check_up_to_date);
370 BOOST_CHECK_EQUAL( ips.size(), 4 );
371 Cname cname = Cache->get_cname("cname_outdated.test", check_up_to_date);
372 BOOST_CHECK( !cname.Host.empty() );
373 ips = Cache->get_ips_recursive("cname_outdated.test", check_up_to_date);
374 BOOST_CHECK_EQUAL( ips.size(), 4 );
375 ips = Cache->get_ips_recursive("cname_up_to_date.test", check_up_to_date);
376 BOOST_CHECK_EQUAL( ips.size(), 4 );
377
378 check_up_to_date = true;
379 ips = Cache->get_ips( "host_outdated.test", check_up_to_date );
380 BOOST_CHECK_EQUAL( ips.size(), 1 );
381 ips = Cache->get_ips_recursive("host_outdated.test", check_up_to_date);
382 BOOST_CHECK_EQUAL( ips.size(), 1 );
383 cname = Cache->get_cname("cname_outdated.test", check_up_to_date);
384 BOOST_CHECK( cname.Host.empty() );
385 ips = Cache->get_ips_recursive("cname_outdated.test", check_up_to_date);
386 BOOST_CHECK_EQUAL( ips.size(), 0 );
387 ips = Cache->get_ips_recursive("cname_up_to_date.test", check_up_to_date);
388 BOOST_CHECK_EQUAL( ips.size(), 1 );
389}
390
f833126b
CH
391BOOST_AUTO_TEST_CASE( cache_ttl_below_thresh_test )
392{
393 // in fill_cache,
394 HostAddressVec ips = Cache->get_ips("host_ttl_below_thresh.test", false);
395 BOOST_REQUIRE_EQUAL( ips.size(), 1 );
396 HostAddress ip = ips.front();
397 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), min_time_between_resolves );
00c81aa0 398
f833126b
CH
399 Cname cname = Cache->get_cname("cname_ttl_below_thresh.test", false);
400 BOOST_CHECK_EQUAL( cname.Ttl.get_value(), min_time_between_resolves );
401}
8d26221d
CH
402
403BOOST_AUTO_TEST_SUITE_END() // of TestDnsCache
404
405
406// -----------------------------------------------------------------------------
407// test resolver
408// -----------------------------------------------------------------------------
409
410BOOST_FIXTURE_TEST_SUITE( TestDnsResolver, TestFixture )
411
8d26221d
CH
412BOOST_AUTO_TEST_CASE( create_resolver_v4 )
413{
26b0f687
CH
414 // create resolver
415 std::string hostname = "www.intra2net.com";
8d26221d
CH
416 ResolverItem resolver = Master->get_resolver_for(hostname,
417 PingProtocol_ICMP);
26b0f687
CH
418 BOOST_CHECK_EQUAL( resolver->get_hostname(), hostname );
419 BOOST_CHECK( !resolver->is_resolving() );
4389b86d 420 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
26b0f687
CH
421
422 // cancel should have no effect
423 resolver->cancel_resolve();
424 BOOST_CHECK( !resolver->is_resolving() );
4389b86d 425 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
26b0f687
CH
426
427 // should not have any ips since cache did not load anything
428 BOOST_CHECK_EQUAL( resolver->get_resolved_ip_count(), 0 );
429 BOOST_CHECK( !resolver->have_up_to_date_ip() );
430 std::string no_ip = "0.0.0.0";
431 BOOST_CHECK_EQUAL( resolver->get_next_ip().get_ip().to_string(), no_ip );
432}
433
8d26221d
CH
434
435void resolve_callback(IoServiceItem io_serv, ResolverItem resolver,
436 const bool was_success, const int cname_count)
437{
438 resolver->cancel_resolve();
439 io_serv->stop();
440 BOOST_TEST_MESSAGE( "Stopped io_service" );
00c81aa0
CH
441 BOOST_CHECK( was_success );
442 BOOST_CHECK_EQUAL( cname_count, 0 );
8d26221d
CH
443}
444
445
446BOOST_AUTO_TEST_CASE( do_resolve )
447{
448 // create resolver
449 std::string hostname = "www.intra2net.com";
450 ResolverItem resolver = Master->get_resolver_for(hostname,
451 PingProtocol_ICMP);
4389b86d
CH
452 BOOST_CHECK( !resolver->is_resolving() );
453 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
8d26221d
CH
454
455 // set callback
456 callback_type callback = boost::bind( resolve_callback, IoService, resolver,
457 _1, _2 );
458 // start resolving
459 resolver->async_resolve(callback);
4389b86d 460 BOOST_CHECK( resolver->is_resolving() );
8d26221d
CH
461 IoService->run();
462 // this will block until io service is stopped in resolve_callback
463
464 // check for result
465 BOOST_CHECK( resolver->have_up_to_date_ip() );
466}
467
468BOOST_AUTO_TEST_SUITE_END() // of TestDnsResolver
469
26b0f687 470BOOST_AUTO_TEST_SUITE_END()