changed how dns deals with cnames and recursion: remember cnames and implement recurs...
[pingcheck] / src / host / pingrotate.cpp
CommitLineData
0b72647e
GMF
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#include "host/pingrotate.h"
22
9490a7bd
CH
23#include <logfunc.hpp>
24
0b72647e 25#include <boost/bind.hpp>
a435b71b 26#include <boost/foreach.hpp>
0b72647e 27
780b0bca 28#include "boost_assert_handler.h"
c5b4902d 29#include "dns/dnsmaster.h"
0b72647e
GMF
30#include "host/pingerfactory.h"
31
32using namespace std;
0b72647e 33using boost::function;
e58d7507 34using boost::shared_ptr;
0b72647e 35
9490a7bd
CH
36using I2n::Logger::GlobalLogger;
37
0b72647e
GMF
38//-----------------------------------------------------------------------------
39// PingRotate
40//-----------------------------------------------------------------------------
41
42/**
43 * @brief Parameterized constructor.
44 *
ab2cb1ef 45 * @param io_serv The one @c io_service object that controls async processing
0b72647e
GMF
46 * @param network_interface The name of the network interface from where to
47 * dispatch the pings.
823623d9
GMF
48 * @param destination_address The remote address to ping.
49 * @param destination_port The remote port to ping.
823623d9 50 * @param protocol_list A list of protocols to be used to ping the
0b72647e
GMF
51 * host. The protocols will be used in the order they are in the list.
52 */
53PingRotate::PingRotate(
365036be 54 const IoServiceItem io_serv,
0b72647e 55 const string &network_interface,
823623d9
GMF
56 const string &destination_address,
57 const uint16_t destination_port,
079d19ab
CH
58 const int resolved_ip_ttl_threshold,
59 const int ping_reply_timeout,
8837460c 60 const PingProtocolList &protocol_list
0b72647e
GMF
61) :
62 IoService( io_serv ),
63 NetworkInterfaceName( network_interface ),
9490a7bd
CH
64 Resolver(),
65 DestinationAddress( destination_address ),
823623d9 66 DestinationPort( destination_port ),
079d19ab
CH
67 ResolvedIpTtlThreshold( resolved_ip_ttl_threshold ),
68 PingReplyTimeout( ping_reply_timeout ),
a435b71b 69 ProtocolRotate( protocol_list.size() ),
0b72647e 70 Ping(),
9490a7bd
CH
71 PingDoneCallback(),
72 DnsResolutionFinished( true ),
73 WantToPing( false )
0b72647e
GMF
74{
75 BOOST_ASSERT( !network_interface.empty() );
823623d9
GMF
76 BOOST_ASSERT( !destination_address.empty() );
77 BOOST_ASSERT( 0 < destination_port );
823623d9 78 BOOST_ASSERT( 0 < protocol_list.size() );
bf761dd7 79
a435b71b
CH
80 // fill circular buffer with protocols
81 BOOST_FOREACH( const PingProtocol &prot, protocol_list )
82 ProtocolRotate.push_back(prot);
83
3eb8c4bd 84 init_ping_protocol();
0b72647e
GMF
85}
86
87/**
88 * @brief Destructor.
89 */
90PingRotate::~PingRotate()
91{
92}
93
94/**
95 * @brief Ping a destination address from an available local source.
96 *
0b72647e
GMF
97 * @param done_handler Done handler will be called on successful ping or timeout.
98 *
99 * @return void.
100 */
823623d9 101void PingRotate::ping( function<void(bool)> ping_done_callback )
0b72647e 102{
823623d9 103 BOOST_ASSERT( ( 0 < DestinationPort ) && ( DestinationPort < numeric_limits<uint16_t>::max() ) );
0b72647e 104
823623d9
GMF
105 set_ping_done_callback( ping_done_callback );
106
823623d9 107 update_ping_protocol();
0b72647e 108
9490a7bd
CH
109 WantToPing = true;
110 try_to_ping();
111}
112
113void PingRotate::try_to_ping()
114{
115 if ( !WantToPing )
116 {
117 GlobalLogger.info() << "PingRotate: not pinging yet (not requested to)";
118 return;
119 }
120 else if ( !DnsResolutionFinished )
121 {
122 GlobalLogger.info() << "PingRotate: not pinging yet (DNS not finished)";
123 return;
124 }
125
126 GlobalLogger.info() << "PingRotate: start ping";
127 WantToPing = false;
128 string destination_ip = Resolver->get_next_ip().get_ip().to_string();
129 // TODO: pinger will probably re-create IP from this
130 // --> change pingers to accept ip::address
bf761dd7 131
0b72647e
GMF
132 Ping->ping(
133 destination_ip,
823623d9 134 DestinationPort,
0b72647e
GMF
135 boost::bind(&PingRotate::ping_done_handler, this, _1)
136 );
137}
138
5a9bc2d1
CH
139void PingRotate::stop_pinging()
140{
141 Ping->stop_pinging();
142}
143
9490a7bd 144void PingRotate::start_resolving_ping_address() //lint !e1762
823623d9 145{
9490a7bd
CH
146 DnsResolutionFinished = false;
147 Resolver->async_resolve( boost::bind(&PingRotate::dns_resolve_callback,
148 this, _1, _2) );
823623d9
GMF
149}
150
151int PingRotate::get_resolved_ip_count() const
152{
9490a7bd 153 return Resolver->get_resolved_ip_count();
823623d9
GMF
154}
155
d4b20892 156bool PingRotate::have_up_to_date_ip() const
823623d9 157{
9490a7bd 158 return Resolver->have_up_to_date_ip();
823623d9
GMF
159}
160
161void PingRotate::set_ping_done_callback( function<void(bool)> ping_done_callback )
162{
163 PingDoneCallback = ping_done_callback;
164}
165
d4828254 166void PingRotate::ping_done_handler( bool ping_success ) const
0b72647e
GMF
167{
168 PingDoneCallback( ping_success );
1513bb78
GMF
169}
170
3eb8c4bd
GMF
171void PingRotate::init_ping_protocol()
172{
173 get_next_ping_protocol();
174}
175
1513bb78
GMF
176void PingRotate::update_ping_protocol()
177{
0b72647e
GMF
178 if ( can_change_ping_protocol() )
179 {
180 get_next_ping_protocol();
181 }
182}
183
184void PingRotate::get_next_ping_protocol()
185{
a435b71b 186 PingProtocol ping_protocol = ProtocolRotate.front();
ed9c5d7d
CH
187 ProtocolRotate.pop_front();
188 ProtocolRotate.push_back(ping_protocol);
0b72647e 189
ed9c5d7d 190 Ping = PingerFactory::createPinger( ping_protocol, IoService, NetworkInterfaceName, PingReplyTimeout );
0b72647e 191
ed9c5d7d 192 update_dns_resolver( ping_protocol );
0b72647e
GMF
193}
194
195bool PingRotate::can_change_ping_protocol() const
196{
bf761dd7
GMF
197 // TODO can_change_ping_protocol() and get_next_ping_protocol() may be implemented in a Algorithm
198 // class that can be exchanged in this class to provide an algorithm neutral class
80363c94 199 return true;
0b72647e 200}
bf761dd7
GMF
201
202void PingRotate::update_dns_resolver( PingProtocol current_protocol )
203{
9490a7bd
CH
204 // DNS master caches created resolvers and resolved IPs, so this will
205 // probably just return an existing resolver with already resolved IPs for
206 // requested protocol ( ICMP/TCP is ignored, only IPv4/v6 is important)
207 Resolver = DnsMaster::get_instance()->get_resolver_for(DestinationAddress,
208 current_protocol);
209 // start resolving if no ips available
210 if ( !Resolver->have_up_to_date_ip() )
211 start_resolving_ping_address();
212}
bf761dd7 213
9490a7bd 214void PingRotate::dns_resolve_callback(const bool was_success,
ad83004d 215 const int recursion_count)
9490a7bd
CH
216{
217 GlobalLogger.info() << "PingRotate: dns resolution finished "
218 << "with success = " << was_success << " "
ad83004d
CH
219 << "and recursion_count = " << recursion_count;
220 throw std::runtime_error("Only debugging DNS -- exit by error");
9490a7bd
CH
221 if (DnsResolutionFinished)
222 { // there were probably several calls to async_resolve before it could
223 // finish --> ignore this callback
224 GlobalLogger.info() << "PingRotate: dns resolve callback called "
225 << "but dns is marked as finished already - ignore";
226 return;
227 }
228 else
229 {
230 DnsResolutionFinished = true;
231 try_to_ping();
232 }
bf761dd7 233}