Commit | Line | Data |
---|---|---|
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 | ||
32 | using namespace std; | |
0b72647e | 33 | using boost::function; |
e58d7507 | 34 | using boost::shared_ptr; |
0b72647e | 35 | |
9490a7bd CH |
36 | using 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 | */ | |
53 | PingRotate::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 | */ | |
90 | PingRotate::~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 | 101 | void 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 | ||
113 | void 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 |
139 | void PingRotate::stop_pinging() |
140 | { | |
141 | Ping->stop_pinging(); | |
142 | } | |
143 | ||
9490a7bd | 144 | void 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 | ||
151 | int PingRotate::get_resolved_ip_count() const | |
152 | { | |
9490a7bd | 153 | return Resolver->get_resolved_ip_count(); |
823623d9 GMF |
154 | } |
155 | ||
d4b20892 | 156 | bool PingRotate::have_up_to_date_ip() const |
823623d9 | 157 | { |
9490a7bd | 158 | return Resolver->have_up_to_date_ip(); |
823623d9 GMF |
159 | } |
160 | ||
161 | void PingRotate::set_ping_done_callback( function<void(bool)> ping_done_callback ) | |
162 | { | |
163 | PingDoneCallback = ping_done_callback; | |
164 | } | |
165 | ||
d4828254 | 166 | void PingRotate::ping_done_handler( bool ping_success ) const |
0b72647e GMF |
167 | { |
168 | PingDoneCallback( ping_success ); | |
1513bb78 GMF |
169 | } |
170 | ||
3eb8c4bd GMF |
171 | void PingRotate::init_ping_protocol() |
172 | { | |
173 | get_next_ping_protocol(); | |
174 | } | |
175 | ||
1513bb78 GMF |
176 | void PingRotate::update_ping_protocol() |
177 | { | |
0b72647e GMF |
178 | if ( can_change_ping_protocol() ) |
179 | { | |
180 | get_next_ping_protocol(); | |
181 | } | |
182 | } | |
183 | ||
184 | void 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 | ||
195 | bool 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 | |
202 | void 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 | 214 | void 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 | } |