3c60bf3c862a5f88baaa17f24104994dbc5ce32c
[pingcheck] / test / test_dns.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
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.
13
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.
16
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.
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>
29 #include <boost/bind.hpp>
30
31 #include <logfunc.hpp>
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"
38 #include "dns/cname.h"
39
40 using boost::asio::ip::address;
41
42 //------------------------------------------------------------------------------
43 // Global Test fixture (created once for test suite)
44 //------------------------------------------------------------------------------
45
46 // constants for master
47 address name_server = address::from_string("127.0.0.1");
48 int resolved_ip_ttl_threshold = 3;
49 int max_address_resolution_attempts = 2;
50 std::string cache_file = DnsCache::DoNotUseCacheFile;
51
52 // rely on boost that there will only be one instance
53 struct GlobalFixture;
54 GlobalFixture *global_fixture;
55
56
57 struct GlobalFixture
58 {
59
60     GlobalFixture()
61         : IoService()
62         , Cache()
63         , Master()
64     {
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,
86                                  name_server,
87                                  resolved_ip_ttl_threshold,
88                                  max_address_resolution_attempts,
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;
95     }
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
124         {   // cname.test --> host1.test
125             Cname cname("host1.test", 35);
126             Cache->update("cname.test", cname);
127         }
128
129         {   // cname2.test --> cname.test --> host1.test
130             Cname cname("cname.test", 33);
131             Cache->update("cname2.test", cname);
132         }
133
134         {   // cname3.test --> cname2.test --> cname.test --> host1.test
135             Cname cname("cname2.test", 37);
136             Cache->update("cname3.test", cname);
137         }
138         BOOST_TEST_MESSAGE( "Done filling cache." );
139     }
140
141     // these variables will not be available in test cases:
142     IoServiceItem IoService;
143     DnsCacheItem Cache;
144     DnsMasterItem Master;
145 };
146
147 // this causes above fixture to be created only once before tests start and
148 // destructed after tests end; however, variables are not accessible in test
149 // cases
150 BOOST_GLOBAL_FIXTURE( GlobalFixture )
151
152
153 // using this as suite-level fixture makes variable Master accessible in all
154 //   test cases
155 struct TestFixture
156 {
157     IoServiceItem IoService;
158     DnsCacheItem Cache;
159     DnsMasterItem Master;
160
161     TestFixture()
162         : IoService()
163         , Cache()
164         , Master()
165     {
166         BOOST_TEST_MESSAGE("Create test-level fixture");
167         GlobalFixture *global = global_fixture;
168         IoService = global->IoService;
169         Cache = global->Cache;
170         Master = global->Master;
171     }
172
173     virtual ~TestFixture() {}
174 };
175
176 //------------------------------------------------------------------------------
177 // test suite
178 //------------------------------------------------------------------------------
179
180 BOOST_FIXTURE_TEST_SUITE( TestDns, TestFixture )
181
182 BOOST_AUTO_TEST_CASE( create_master )
183 {
184     // simple checks
185     BOOST_CHECK_EQUAL( Master->get_resolved_ip_ttl_threshold(),
186                                    resolved_ip_ttl_threshold );
187     BOOST_CHECK_EQUAL( Master->get_max_address_resolution_attempts(),
188                                    max_address_resolution_attempts );
189 }
190
191 //------------------------------------------------------------------------------
192 // test Cache
193 //------------------------------------------------------------------------------
194 BOOST_FIXTURE_TEST_SUITE( TestDnsCache, TestFixture )
195
196 BOOST_AUTO_TEST_CASE( cache_retrieve_ip1 )
197 {
198     HostAddressVec ips = Cache->get_ips("host1.test");
199     BOOST_CHECK_EQUAL( ips.size(), 1 );
200     HostAddress ip = ips.front();
201     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
202     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 61 );
203 }
204
205
206 BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 )
207 {
208     HostAddressVec ips = Cache->get_ips("host2_3.test");
209     BOOST_CHECK_EQUAL( ips.size(), 2 );
210     HostAddress ip = ips[0];
211     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.2" );
212     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 92 );
213     ip = ips[1];
214     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.3" );
215     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 93 );
216 }
217
218 BOOST_AUTO_TEST_CASE( cache_retrieve_cname )
219 {
220     HostAddressVec ips = Cache->get_ips("cname.test");
221     BOOST_CHECK( ips.empty() );
222
223     Cname cname = Cache->get_cname("cname.test");
224     BOOST_CHECK_EQUAL( cname.Host, "host1.test" );
225     BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 35 );
226 }
227
228 BOOST_AUTO_TEST_CASE( cache_retrieve_recursive1 )
229 {
230     // should get IP from host1 but ttl from cname since is smaller
231     HostAddressVec ips = Cache->get_ips_recursive("cname.test");
232     BOOST_CHECK_EQUAL( ips.size(), 1 );
233     HostAddress ip = ips.front();
234     BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" );
235     BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 35 );
236 }
237
238 BOOST_AUTO_TEST_CASE( cache_retrieve_recursive2 )
239 {
240     // should get IP from host1 but ttl from cname2 since is smaller
241     HostAddressVec ips = Cache->get_ips_recursive("cname3.test");
242     BOOST_CHECK_EQUAL( ips.size(), 1 );
243     HostAddress ip = ips.front();
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
248 BOOST_AUTO_TEST_CASE( cache_skip_test1 )
249 {
250     // build a cname chain where first one is out of date
251     {   // skip_chain_first -(120)-> skip_chain_second -(0)-> skip_chain_third
252         //  -(120)-> skip_chain_fourth -(60)-> IPs
253         Cname first("skip_chain_second.test", 120);
254         Cache->update("skip_chain_first.test", first);
255         Cname second("skip_chain_third.test", 0);
256         Cache->update("skip_chain_second.test", second);
257         Cname third("skip_chain_fourth.test", 120);
258         Cache->update("skip_chain_third.test", third);
259         HostAddressVec ips;
260         ips.push_back( HostAddress( address::from_string("192.168.42.4"), 60) );
261         Cache->update("skip_chain_fourth.test", ips);
262     }
263
264     // normal recursive call should give nothing since all are outdated
265     bool check_up_to_date = true;
266     HostAddressVec ips = Cache->get_ips_recursive("skip_chain_first.test",
267                                                   check_up_to_date);
268     BOOST_CHECK( ips.empty() );
269
270     // now try to skip
271     std::string first_outdated = Cache->get_first_outdated_cname(
272                                                     "skip_chain_first.test", 5);
273     BOOST_CHECK_EQUAL( first_outdated, "skip_chain_third.test" );
274 }
275
276
277 BOOST_AUTO_TEST_SUITE_END()    // of TestDnsCache
278
279
280 // -----------------------------------------------------------------------------
281 // test resolver
282 // -----------------------------------------------------------------------------
283
284 BOOST_FIXTURE_TEST_SUITE( TestDnsResolver, TestFixture )
285
286 void resolve_callback(IoServiceItem io_serv, ResolverItem resolver,
287                       const bool was_success, const int cname_count);
288
289 BOOST_AUTO_TEST_CASE( create_resolver_v4 )
290 {
291     // create resolver
292     std::string hostname = "www.intra2net.com";
293     ResolverItem resolver = Master->get_resolver_for(hostname,
294                                                      PingProtocol_ICMP);
295     BOOST_CHECK_EQUAL( resolver->get_hostname(), hostname );
296     BOOST_CHECK( !resolver->is_resolving() );
297
298     // cancel should have no effect
299     resolver->cancel_resolve();
300     BOOST_CHECK( !resolver->is_resolving() );
301
302     // should not have any ips since cache did not load anything
303     BOOST_CHECK_EQUAL( resolver->get_resolved_ip_count(), 0 );
304     BOOST_CHECK( !resolver->have_up_to_date_ip() );
305     std::string no_ip = "0.0.0.0";
306     BOOST_CHECK_EQUAL( resolver->get_next_ip().get_ip().to_string(), no_ip );
307 }
308
309
310 void resolve_callback(IoServiceItem io_serv, ResolverItem resolver,
311                       const bool was_success, const int cname_count)
312 {
313     resolver->cancel_resolve();
314     io_serv->stop();
315     BOOST_TEST_MESSAGE( "Stopped io_service" );
316 }
317
318
319 BOOST_AUTO_TEST_CASE( do_resolve )
320 {
321     // create resolver
322     std::string hostname = "www.intra2net.com";
323     ResolverItem resolver = Master->get_resolver_for(hostname,
324                                                      PingProtocol_ICMP);
325
326     // set callback
327     callback_type callback = boost::bind( resolve_callback, IoService, resolver,
328                                           _1, _2 );
329     // start resolving
330     resolver->async_resolve(callback);
331     IoService->run();
332     // this will block until io service is stopped in resolve_callback
333
334     // check for result
335     BOOST_CHECK( resolver->have_up_to_date_ip() );
336 }
337
338 BOOST_AUTO_TEST_SUITE_END()   // of TestDnsResolver
339
340 BOOST_AUTO_TEST_SUITE_END()