added test to DNS: load cache from file
[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
9d1b2726
CH
124 // cname.test --> host1.test
125 Cache->update("cname.test", Cname("host1.test", 35) );
8d26221d 126
9d1b2726
CH
127 // cname2.test --> cname.test --> host1.test
128 Cache->update("cname2.test", Cname("cname.test", 33) );
129
130 // cname3.test --> cname2.test --> cname.test --> host1.test
131 Cache->update("cname3.test", Cname("cname2.test", 37) );
8d26221d 132
8d26221d
CH
133 BOOST_TEST_MESSAGE( "Done filling cache." );
134 }
135
136 // these variables will not be available in test cases:
137 IoServiceItem IoService;
138 DnsCacheItem Cache;
139 DnsMasterItem Master;
140};
141
142// this causes above fixture to be created only once before tests start and
143// destructed after tests end; however, variables are not accessible in test
144// cases
145BOOST_GLOBAL_FIXTURE( GlobalFixture )
146
147
148// using this as suite-level fixture makes variable Master accessible in all
149// test cases
150struct TestFixture
151{
152 IoServiceItem IoService;
153 DnsCacheItem Cache;
154 DnsMasterItem Master;
155
156 TestFixture()
157 : IoService()
158 , Cache()
159 , Master()
160 {
161 BOOST_TEST_MESSAGE("Create test-level fixture");
162 GlobalFixture *global = global_fixture;
163 IoService = global->IoService;
164 Cache = global->Cache;
165 Master = global->Master;
166 }
167
168 virtual ~TestFixture() {}
169};
26b0f687
CH
170
171//------------------------------------------------------------------------------
172// test suite
173//------------------------------------------------------------------------------
174
8d26221d 175BOOST_FIXTURE_TEST_SUITE( TestDns, TestFixture )
26b0f687
CH
176
177BOOST_AUTO_TEST_CASE( create_master )
178{
8d26221d
CH
179 // simple checks
180 BOOST_CHECK_EQUAL( Master->get_resolved_ip_ttl_threshold(),
181 resolved_ip_ttl_threshold );
182 BOOST_CHECK_EQUAL( Master->get_max_address_resolution_attempts(),
183 max_address_resolution_attempts );
26b0f687
CH
184}
185
8d26221d
CH
186//------------------------------------------------------------------------------
187// test Cache
188//------------------------------------------------------------------------------
189BOOST_FIXTURE_TEST_SUITE( TestDnsCache, TestFixture )
190
191BOOST_AUTO_TEST_CASE( cache_retrieve_ip1 )
26b0f687 192{
8d26221d
CH
193 HostAddressVec ips = Cache->get_ips("host1.test");
194 BOOST_CHECK_EQUAL( ips.size(), 1 );
195 HostAddress ip = ips.front();
4389b86d 196 BOOST_CHECK( ip.is_valid() );
8d26221d
CH
197 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
198 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 61 );
199}
200
201
202BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 )
203{
204 HostAddressVec ips = Cache->get_ips("host2_3.test");
205 BOOST_CHECK_EQUAL( ips.size(), 2 );
206 HostAddress ip = ips[0];
4389b86d 207 BOOST_CHECK( ip.is_valid() );
8d26221d
CH
208 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.2" );
209 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 92 );
210 ip = ips[1];
4389b86d 211 BOOST_CHECK( ip.is_valid() );
8d26221d
CH
212 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.3" );
213 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 93 );
214}
26b0f687 215
8d26221d
CH
216BOOST_AUTO_TEST_CASE( cache_retrieve_cname )
217{
218 HostAddressVec ips = Cache->get_ips("cname.test");
219 BOOST_CHECK( ips.empty() );
220
221 Cname cname = Cache->get_cname("cname.test");
222 BOOST_CHECK_EQUAL( cname.Host, "host1.test" );
223 BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 35 );
224}
225
226BOOST_AUTO_TEST_CASE( cache_retrieve_recursive1 )
227{
228 // should get IP from host1 but ttl from cname since is smaller
229 HostAddressVec ips = Cache->get_ips_recursive("cname.test");
230 BOOST_CHECK_EQUAL( ips.size(), 1 );
231 HostAddress ip = ips.front();
4389b86d 232 BOOST_CHECK( ip.is_valid() );
8d26221d
CH
233 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
234 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 35 );
235}
236
237BOOST_AUTO_TEST_CASE( cache_retrieve_recursive2 )
238{
239 // should get IP from host1 but ttl from cname2 since is smaller
240 HostAddressVec ips = Cache->get_ips_recursive("cname3.test");
241 BOOST_CHECK_EQUAL( ips.size(), 1 );
242 HostAddress ip = ips.front();
4389b86d 243 BOOST_CHECK( ip.is_valid() );
8d26221d
CH
244 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
245 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 33 );
246}
247
9d1b2726
CH
248void cname_skip_test(const uint32_t ttl1, const uint32_t ttl2,
249 const uint32_t ttl3, const uint32_t ttl4,
250 const DnsCacheItem &cache,
251 const std::string &correct_host_after_skip)
8d26221d 252{
9d1b2726
CH
253 { // create cname chain:
254 // skip_chain_first -(ttl1)-> skip_chain_second -(ttl2)->
255 // skip_chain_third -(ttl3)-> skip_chain_fourth -(ttl4)-> IPs
256 cache->update( "skip_chain_first.test",
257 Cname("skip_chain_second.test", ttl1) );
258 cache->update( "skip_chain_second.test",
259 Cname("skip_chain_third.test", ttl2) );
260 cache->update( "skip_chain_third.test",
261 Cname("skip_chain_fourth.test", ttl3) );
8d26221d 262 HostAddressVec ips;
9d1b2726
CH
263 ips.push_back( HostAddress( address::from_string("192.168.42.4"), ttl4) );
264 cache->update("skip_chain_fourth.test", ips);
8d26221d
CH
265 }
266
9d1b2726 267 // normal recursive call should give nothing since one cname is outdated
8d26221d 268 bool check_up_to_date = true;
9d1b2726 269 HostAddressVec ips = cache->get_ips_recursive("skip_chain_first.test",
8d26221d 270 check_up_to_date);
9d1b2726
CH
271 bool one_is_out_of_date = (ttl1 < 5) || (ttl2 < 5)
272 || (ttl3 < 5) || (ttl4 < 5);
273 BOOST_CHECK_EQUAL( ips.empty(), one_is_out_of_date );
8d26221d 274
9d1b2726
CH
275 // now find host to resolve after the outdated one
276 std::string first_outdated = cache->get_first_outdated_cname(
8d26221d 277 "skip_chain_first.test", 5);
9d1b2726
CH
278 BOOST_CHECK_EQUAL( first_outdated, correct_host_after_skip );
279}
280
281BOOST_AUTO_TEST_CASE( cache_skip_tests )
282{
283 // build a cname chain where first one is out of date
284 cname_skip_test(0, 120, 120, 60, Cache, "skip_chain_second.test");
285
286 // build a cname chain where second one is out of date
287 cname_skip_test(120, 0, 120, 60, Cache, "skip_chain_third.test");
288
289 // build a cname chain where third one is out of date
290 cname_skip_test(120, 120, 0, 120, Cache, "skip_chain_fourth.test");
291
292 // build a cname chain where just IPs are out of date
293 cname_skip_test(120, 120, 120, 0, Cache, "");
294
295 // build a cname chain where all are out of date
296 cname_skip_test(0, 0, 0, 0, Cache, "skip_chain_second.test");
297
298 // build a cname chain where all is up to date
299 cname_skip_test(120, 120, 120, 120, Cache, "");
300
301 // test case with very short cname list that is up-to-date
302 BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("cname.test", 5), "" );
303
304 // test case where there is no cname at all
305 BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("host1.test", 5), "" );
8d26221d
CH
306}
307
4389b86d
CH
308BOOST_AUTO_TEST_CASE( cache_load_test )
309{
310 std::stringstream file_name;
311 file_name << DATA_DIR_STRING << "/" << "dns_cache_example.xml";
312 BOOST_TEST_MESSAGE( "loading cache from file " << file_name.str() );
313 DnsCache loaded_cache( IoService, file_name.str() );
314 HostAddressVec ips = loaded_cache.get_ips("abc.xyz");
315 BOOST_CHECK_EQUAL( ips.size(), 1 );
316 HostAddress ip = ips.front();
317 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "11.22.33.44" );
318 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 567 );
319 BOOST_CHECK_EQUAL( ip.get_ttl().get_updated_value(), 0 );
320
321 Cname cname = loaded_cache.get_cname("cname1.xyz");
322 BOOST_CHECK_EQUAL( cname.Host, "abc.xyz" );
323 BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 27 );
324 BOOST_CHECK_EQUAL( cname.Ttl.get_updated_value(), 0 );
325
326 // not testing Ttl set time since is private
327}
328
8d26221d
CH
329
330BOOST_AUTO_TEST_SUITE_END() // of TestDnsCache
331
332
333// -----------------------------------------------------------------------------
334// test resolver
335// -----------------------------------------------------------------------------
336
337BOOST_FIXTURE_TEST_SUITE( TestDnsResolver, TestFixture )
338
8d26221d
CH
339BOOST_AUTO_TEST_CASE( create_resolver_v4 )
340{
26b0f687
CH
341 // create resolver
342 std::string hostname = "www.intra2net.com";
8d26221d
CH
343 ResolverItem resolver = Master->get_resolver_for(hostname,
344 PingProtocol_ICMP);
26b0f687
CH
345 BOOST_CHECK_EQUAL( resolver->get_hostname(), hostname );
346 BOOST_CHECK( !resolver->is_resolving() );
4389b86d 347 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
26b0f687
CH
348
349 // cancel should have no effect
350 resolver->cancel_resolve();
351 BOOST_CHECK( !resolver->is_resolving() );
4389b86d 352 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
26b0f687
CH
353
354 // should not have any ips since cache did not load anything
355 BOOST_CHECK_EQUAL( resolver->get_resolved_ip_count(), 0 );
356 BOOST_CHECK( !resolver->have_up_to_date_ip() );
357 std::string no_ip = "0.0.0.0";
358 BOOST_CHECK_EQUAL( resolver->get_next_ip().get_ip().to_string(), no_ip );
359}
360
8d26221d
CH
361
362void resolve_callback(IoServiceItem io_serv, ResolverItem resolver,
363 const bool was_success, const int cname_count)
364{
365 resolver->cancel_resolve();
366 io_serv->stop();
367 BOOST_TEST_MESSAGE( "Stopped io_service" );
368}
369
370
371BOOST_AUTO_TEST_CASE( do_resolve )
372{
373 // create resolver
374 std::string hostname = "www.intra2net.com";
375 ResolverItem resolver = Master->get_resolver_for(hostname,
376 PingProtocol_ICMP);
4389b86d
CH
377 BOOST_CHECK( !resolver->is_resolving() );
378 BOOST_CHECK( !resolver->is_waiting_to_resolve() );
8d26221d
CH
379
380 // set callback
381 callback_type callback = boost::bind( resolve_callback, IoService, resolver,
382 _1, _2 );
383 // start resolving
384 resolver->async_resolve(callback);
4389b86d 385 BOOST_CHECK( resolver->is_resolving() );
8d26221d
CH
386 IoService->run();
387 // this will block until io service is stopped in resolve_callback
388
389 // check for result
390 BOOST_CHECK( resolver->have_up_to_date_ip() );
391}
392
393BOOST_AUTO_TEST_SUITE_END() // of TestDnsResolver
394
26b0f687 395BOOST_AUTO_TEST_SUITE_END()