more cname-skip unit-tests and simplification of skip-finder function
[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 );
69 I2n::Logger::set_log_level( I2n::Logger::LogLevel::Debug );
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();
196 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
197 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 61 );
198}
199
200
201BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 )
202{
203 HostAddressVec ips = Cache->get_ips("host2_3.test");
204 BOOST_CHECK_EQUAL( ips.size(), 2 );
205 HostAddress ip = ips[0];
206 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.2" );
207 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 92 );
208 ip = ips[1];
209 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.3" );
210 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 93 );
211}
26b0f687 212
8d26221d
CH
213BOOST_AUTO_TEST_CASE( cache_retrieve_cname )
214{
215 HostAddressVec ips = Cache->get_ips("cname.test");
216 BOOST_CHECK( ips.empty() );
217
218 Cname cname = Cache->get_cname("cname.test");
219 BOOST_CHECK_EQUAL( cname.Host, "host1.test" );
220 BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 35 );
221}
222
223BOOST_AUTO_TEST_CASE( cache_retrieve_recursive1 )
224{
225 // should get IP from host1 but ttl from cname since is smaller
226 HostAddressVec ips = Cache->get_ips_recursive("cname.test");
227 BOOST_CHECK_EQUAL( ips.size(), 1 );
228 HostAddress ip = ips.front();
229 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
230 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 35 );
231}
232
233BOOST_AUTO_TEST_CASE( cache_retrieve_recursive2 )
234{
235 // should get IP from host1 but ttl from cname2 since is smaller
236 HostAddressVec ips = Cache->get_ips_recursive("cname3.test");
237 BOOST_CHECK_EQUAL( ips.size(), 1 );
238 HostAddress ip = ips.front();
239 BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
240 BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 33 );
241}
242
9d1b2726
CH
243void cname_skip_test(const uint32_t ttl1, const uint32_t ttl2,
244 const uint32_t ttl3, const uint32_t ttl4,
245 const DnsCacheItem &cache,
246 const std::string &correct_host_after_skip)
8d26221d 247{
9d1b2726
CH
248 { // create cname chain:
249 // skip_chain_first -(ttl1)-> skip_chain_second -(ttl2)->
250 // skip_chain_third -(ttl3)-> skip_chain_fourth -(ttl4)-> IPs
251 cache->update( "skip_chain_first.test",
252 Cname("skip_chain_second.test", ttl1) );
253 cache->update( "skip_chain_second.test",
254 Cname("skip_chain_third.test", ttl2) );
255 cache->update( "skip_chain_third.test",
256 Cname("skip_chain_fourth.test", ttl3) );
8d26221d 257 HostAddressVec ips;
9d1b2726
CH
258 ips.push_back( HostAddress( address::from_string("192.168.42.4"), ttl4) );
259 cache->update("skip_chain_fourth.test", ips);
8d26221d
CH
260 }
261
9d1b2726 262 // normal recursive call should give nothing since one cname is outdated
8d26221d 263 bool check_up_to_date = true;
9d1b2726 264 HostAddressVec ips = cache->get_ips_recursive("skip_chain_first.test",
8d26221d 265 check_up_to_date);
9d1b2726
CH
266 bool one_is_out_of_date = (ttl1 < 5) || (ttl2 < 5)
267 || (ttl3 < 5) || (ttl4 < 5);
268 BOOST_CHECK_EQUAL( ips.empty(), one_is_out_of_date );
8d26221d 269
9d1b2726
CH
270 // now find host to resolve after the outdated one
271 std::string first_outdated = cache->get_first_outdated_cname(
8d26221d 272 "skip_chain_first.test", 5);
9d1b2726
CH
273 BOOST_CHECK_EQUAL( first_outdated, correct_host_after_skip );
274}
275
276BOOST_AUTO_TEST_CASE( cache_skip_tests )
277{
278 // build a cname chain where first one is out of date
279 cname_skip_test(0, 120, 120, 60, Cache, "skip_chain_second.test");
280
281 // build a cname chain where second one is out of date
282 cname_skip_test(120, 0, 120, 60, Cache, "skip_chain_third.test");
283
284 // build a cname chain where third one is out of date
285 cname_skip_test(120, 120, 0, 120, Cache, "skip_chain_fourth.test");
286
287 // build a cname chain where just IPs are out of date
288 cname_skip_test(120, 120, 120, 0, Cache, "");
289
290 // build a cname chain where all are out of date
291 cname_skip_test(0, 0, 0, 0, Cache, "skip_chain_second.test");
292
293 // build a cname chain where all is up to date
294 cname_skip_test(120, 120, 120, 120, Cache, "");
295
296 // test case with very short cname list that is up-to-date
297 BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("cname.test", 5), "" );
298
299 // test case where there is no cname at all
300 BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("host1.test", 5), "" );
8d26221d
CH
301}
302
303
304BOOST_AUTO_TEST_SUITE_END() // of TestDnsCache
305
306
307// -----------------------------------------------------------------------------
308// test resolver
309// -----------------------------------------------------------------------------
310
311BOOST_FIXTURE_TEST_SUITE( TestDnsResolver, TestFixture )
312
8d26221d
CH
313BOOST_AUTO_TEST_CASE( create_resolver_v4 )
314{
26b0f687
CH
315 // create resolver
316 std::string hostname = "www.intra2net.com";
8d26221d
CH
317 ResolverItem resolver = Master->get_resolver_for(hostname,
318 PingProtocol_ICMP);
26b0f687
CH
319 BOOST_CHECK_EQUAL( resolver->get_hostname(), hostname );
320 BOOST_CHECK( !resolver->is_resolving() );
321
322 // cancel should have no effect
323 resolver->cancel_resolve();
324 BOOST_CHECK( !resolver->is_resolving() );
325
326 // should not have any ips since cache did not load anything
327 BOOST_CHECK_EQUAL( resolver->get_resolved_ip_count(), 0 );
328 BOOST_CHECK( !resolver->have_up_to_date_ip() );
329 std::string no_ip = "0.0.0.0";
330 BOOST_CHECK_EQUAL( resolver->get_next_ip().get_ip().to_string(), no_ip );
331}
332
8d26221d
CH
333
334void resolve_callback(IoServiceItem io_serv, ResolverItem resolver,
335 const bool was_success, const int cname_count)
336{
337 resolver->cancel_resolve();
338 io_serv->stop();
339 BOOST_TEST_MESSAGE( "Stopped io_service" );
340}
341
342
343BOOST_AUTO_TEST_CASE( do_resolve )
344{
345 // create resolver
346 std::string hostname = "www.intra2net.com";
347 ResolverItem resolver = Master->get_resolver_for(hostname,
348 PingProtocol_ICMP);
349
350 // set callback
351 callback_type callback = boost::bind( resolve_callback, IoService, resolver,
352 _1, _2 );
353 // start resolving
354 resolver->async_resolve(callback);
355 IoService->run();
356 // this will block until io service is stopped in resolve_callback
357
358 // check for result
359 BOOST_CHECK( resolver->have_up_to_date_ip() );
360}
361
362BOOST_AUTO_TEST_SUITE_END() // of TestDnsResolver
363
26b0f687 364BOOST_AUTO_TEST_SUITE_END()