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