Log burnt IP only once
[bpdyndnsd] / src / service.cpp
1 /** @file
2  * @brief The abstract service class. This class represents all services.
3  *
4  *
5  *
6  * @copyright Intra2net AG
7  * @license GPLv2
8 */
9
10 #include "service.hpp"
11 #include <boost/foreach.hpp>
12
13 using namespace std;
14
15 /// Number of update errors until a service will be blocked
16 const int MaxErrorCount = 3;
17 /// Number of seconds a service will be blocked if MaxErrorCount is reached
18 const int ErrorBlockServiceSeconds = 15 * 60;
19
20 /**
21  * Default Constructor
22  */
23 Service::Service()
24     : Login("NOT SERIALIZED")
25     , Password("NOT SERIALIZED")
26     , ActualIP("0.0.0.0")
27     , ActualIPIsBurnt(false)
28     , UpdateInterval(15)
29     , MaxUpdatesWithinInterval(3)
30     , MaxEqualUpdatesInSuccession(2)
31     , DNSCacheTTL(0)
32     , ErrorCount(0)
33     , ErrorServiceBlockedUntil(0)
34     , Log(new Logger())
35 {
36 }
37
38
39 /**
40  * Default Destructor needed for deserialization.
41  */
42 Service::~Service()
43 {
44 }
45
46
47 /**
48  * Setter for member Protocol.
49  * @param _protocol Value to set Protocol to.
50  */
51 void Service::set_protocol(const string& _protocol)
52 {
53     Protocol = _protocol;
54 }
55
56
57 /**
58  * Getter for memeber Protocol.
59  * @return Value of member Protocol.
60  */
61 string Service::get_protocol() const
62 {
63     return Protocol;
64 }
65
66
67 /**
68  * Setter for member Hostname.
69  * @param _hostname Value to set Hostname to.
70  */
71 void Service::set_hostname(const string& _hostname)
72 {
73     Hostname = _hostname;
74 }
75
76
77 /**
78  * Getter for member Hostname.
79  * @return Value of member Hostname.
80  */
81 string Service::get_hostname() const
82 {
83     return Hostname;
84 }
85
86
87 /**
88  * Setter for member Login.
89  * @param _login Value to set Login to.
90  */
91 void Service::set_login(const string& _login)
92 {
93     Login = _login;
94 }
95
96
97 /**
98  * Getter for member Login.
99  * @return Value of member Login.
100  */
101 string Service::get_login() const
102 {
103     return Login;
104 }
105
106
107 /**
108  * Setter for member Password.
109  * @param _password Value to set Password to.
110  */
111 void Service::set_password(const string& _password)
112 {
113     Password = _password;
114 }
115
116
117 /**
118  * Getter for member Password.
119  * @return Value of member Password.
120  */
121 string Service::get_password() const
122 {
123     return Password;
124 }
125
126
127
128 void Service::set_logger(const Logger::Ptr& _log)
129 {
130     Log = _log;
131 }
132
133
134 /**
135  * Getter for member Log.
136  * @return Shared pointer to Logger object.
137  */
138 Logger::Ptr Service::get_logger() const
139 {
140     return Log;
141 }
142
143
144 /**
145  * Setter for member LastUpdates.
146  * @param _last_updates Value to set LastUpdates to.
147  */
148 void Service::set_last_updates(std::map<time_t,std::string> _last_updates)
149 {
150     std::map<time_t,std::string> temp = _last_updates;
151     LastUpdates.swap(temp);
152 }
153
154
155 /**
156  * Getter for member LastUpdates.
157  * @return Value of member LastUpdates.
158  */
159 const std::map<time_t,std::string> Service::get_last_updates() const
160 {
161     return LastUpdates;
162 }
163
164
165 /**
166  * Setter for member ActualIP.
167  * @param _actual_ip Value to set ActualIP to.
168  */
169 void Service::set_actual_ip(const std::string& _actual_ip)
170 {
171     ActualIP = _actual_ip;
172 }
173
174
175 /**
176  * Getter for member ActualIP.
177  * @return Value of member ActualIP.
178  */
179 std::string Service::get_actual_ip() const
180 {
181     return ActualIP;
182 }
183
184
185 /**
186  * Overloading of comparison operator.
187  * @param other Reference to other Service object.
188  * @return True if they equal, false if not.
189  */
190 bool Service::operator== (const Service& other) const
191 {
192     if ( ( this->Protocol == other.Protocol ) && ( this->Hostname == other.Hostname ) )
193         return true;
194     return false;
195 }
196
197
198 /**
199  * Overloading of disparate operator.
200  * @param other Reference to other Service object.
201  * @return True if they differ, false if they are equal.
202  */
203 bool Service::operator!= (const Service& other) const
204 {
205     return !(*this == other);
206 }
207
208
209 /**
210  * Checks if update will exceed max update interval or if the IP address is burnt.
211  * @param current_time Current time.
212  * @param changed_to_online True if we just changed to online, false if we were already online
213  * @param ip_host The new ip to set for the hostname.
214  * @return True if update is allowed, false if update would exceed max update interval.
215  */
216 bool Service::update_allowed(const time_t current_time, bool changed_to_online, const std::string& ip_host)
217 {
218     Log->print_last_updates(ip_host,current_time,UpdateInterval,MaxUpdatesWithinInterval,MaxEqualUpdatesInSuccession,LastUpdates,get_service_name());
219
220     // Check for update interval overstepping.
221     int i = 0;
222     for ( std::map<time_t,std::string>::reverse_iterator r_iter = LastUpdates.rbegin(); (r_iter != LastUpdates.rend()) && ( i < MaxUpdatesWithinInterval ); r_iter++)
223     {
224         if ( (i == (MaxUpdatesWithinInterval-1)) && ( (r_iter->first + ((time_t)UpdateInterval*60)) >= current_time ) )
225         {
226             Log->print_update_not_allowed(changed_to_online,current_time,r_iter->first,MaxUpdatesWithinInterval,get_service_name());
227             return false;
228         }
229         i++;
230     }
231
232     if (ActualIPIsBurnt)
233     {
234         // Changed IP address removes the "burnt state"
235         if (ip_host != ActualIP)
236             ActualIPIsBurnt = false;
237         else
238         {
239             // IP is burnt and didn't change -> Update not allowed
240             return false;
241         }
242     }
243
244     // Check for burnt IP.
245     // Only check for burnt IP address if there are at least max_equal_updates_in_succession entries in the last_updates map.
246     if ( (MaxEqualUpdatesInSuccession != 0) && ((int)LastUpdates.size() >= MaxEqualUpdatesInSuccession) )
247     {
248         bool ip_burnt = true;
249         i = 0;
250         // Reverse iterate "last_updates" list so the latest update
251         // will be the first entry (map key is the unix timestamp)
252         for ( std::map<time_t,std::string>::reverse_iterator r_iter = LastUpdates.rbegin();
253             ( r_iter != LastUpdates.rend() ) && ( i < MaxEqualUpdatesInSuccession ); r_iter++ )
254         {
255             if ( ip_host != r_iter->second )
256             {
257                 ip_burnt = false;
258                 break;
259             }
260             i++;
261         }
262
263         if ( ip_burnt )
264         {
265             // IP Address is burnt. Too many updates in succession with the same IP. Log once.
266             Log->print_ip_burnt(ip_host,get_service_name());
267             ActualIPIsBurnt = true;
268             return false;
269         }
270     }
271
272     return true;
273 }
274
275
276 /**
277  * Service update method, common to each service.
278  * @param ip The new ip to set for the hostname.
279  * @param current_time Current time
280  * @param changed_to_online True if we just changed to online, false if we were already online
281  */
282 void Service::update(const string& ip, const time_t current_time, bool changed_to_online)
283 {
284     const std::string service_name = get_service_name();
285
286     // Check if service is blocked for a short period of time (because of update errors)
287     if (ErrorServiceBlockedUntil && current_time < ErrorServiceBlockedUntil)
288     {
289         Log->print_update_service_is_blocked(service_name, ErrorServiceBlockedUntil - current_time);
290         return;
291     }
292
293     // test if update is permitted by UpdateInterval Status
294     if ( update_allowed(current_time, changed_to_online, ip) )
295     {
296         Log->print_update_service(service_name);
297
298         UpdateErrorCode update_res = perform_update(ip);
299
300         if (update_res == UpdateOk)
301         {
302             // if update was successful, we need to set the Lastupdated and ActualIP base member.
303             set_last_update(current_time,ip);
304             ActualIP = ip;
305             Log->print_update_service_successful(service_name);
306
307             ErrorCount = 0;
308             ErrorServiceBlockedUntil = 0;
309         }
310         else
311         {
312             if (update_res == NoChange || update_res == Blocked)
313             {
314                 // Log update for burnt IP logic and update "ActualIP" of this service
315                 set_last_update(current_time,ip);
316                 ActualIP = ip;
317             }
318
319             // problem while trying to update service
320             Log->print_update_service_failure(service_name);
321
322             ++ErrorCount;
323             if (ErrorCount >= MaxErrorCount)
324             {
325                 Log->print_block_service(service_name, ErrorBlockServiceSeconds);
326
327                 ErrorServiceBlockedUntil = time(NULL) + ErrorBlockServiceSeconds;
328                 ErrorCount = 0;
329             }
330         }
331     }
332 }
333
334
335 /**
336 * Sets the given time into the LastUpdates member and deletes expired entries.
337 * @param _timeout Value to set into LastUpdates.
338 */
339 void Service::set_last_update(const time_t current_time, const string& ip)
340 {
341     // Insert value into the list.
342     LastUpdates.insert(make_pair(current_time,ip));
343
344     // Get the maximum of MaxUpdatesWithinInterval and MaxEqualUpdatesInSuccession
345     int maximum = max(MaxUpdatesWithinInterval,MaxEqualUpdatesInSuccession);
346
347     // Check for expired entries:
348
349     // MaxUpdatesWithinInterval given in service config, then use this to check for expired entries.
350     if ( maximum > 0 )
351     {
352         // Delete the oldest entry if there are more than max(MaxUpdatesWithinInterval,MaxEqualUpdatesInSuccession)+1 entries in the list.
353         if ( LastUpdates.size() > (size_t)(maximum+1) )
354             LastUpdates.erase(LastUpdates.begin());
355         return;
356     }
357     // UpdateInterval given in service config, then use this to check for expired entries.
358     else if ( UpdateInterval > 0 )
359     {
360         // Delete the oldest entry if it's older than current_time - UpdateInterval(minutes) + 1.
361         if ( (current_time - ((time_t)UpdateInterval*60) + 1) > LastUpdates.begin()->first )
362             LastUpdates.erase(LastUpdates.begin());
363         return;
364     }
365     // Neither MaxUpdatesWithinInterval nor UpdateInterval are given, so keep fix number of 10 entries.
366     else
367     {
368         if ( LastUpdates.size() > 10 )
369             LastUpdates.erase(LastUpdates.begin());
370         return;
371     }
372 }
373
374
375 /**
376  * Getter the last updated time.
377  * @return Value of the last update as time_t.
378  */
379 time_t Service::get_last_update_time( )
380 {
381     time_t last_update = 0;
382     if ( !LastUpdates.empty() )
383     {
384         std::map<time_t,std::string>::reverse_iterator r_iter = LastUpdates.rbegin();
385         if ( r_iter != LastUpdates.rend() )
386             last_update = r_iter->first;
387     }
388     return last_update;
389 }
390
391
392 /**
393  * Setter for member Timeout.
394  * @param _timeout Value to set Timeout to.
395  */
396 void Service::set_update_interval(const int _update_interval)
397 {
398     UpdateInterval = _update_interval;
399 }
400
401
402 /**
403  * Getter for member Timeout.
404  * @return Value of Timeout.
405  */
406 int Service::get_update_interval() const
407 {
408     return UpdateInterval;
409 }
410
411
412 /**
413  * Setter for member Max_updates_per_timeout.
414  * @param  _max_updates_per_timeout Value to set Max_updates_per_timeout to.
415  */
416 void Service::set_max_updates_within_interval(const int _max_updates_within_interval)
417 {
418     MaxUpdatesWithinInterval = _max_updates_within_interval;
419 }
420
421
422 /**
423  * Getter for member Max_updates_per_timeout.
424  * @return Value of Max_updates_per_timeout.
425  */
426 int Service::get_max_updates_within_interval() const
427 {
428     return MaxUpdatesWithinInterval;
429 }
430
431
432 /**
433  * Setter for member MaxEqualUpdatesInSuccession.
434  * @param  _max_equal_updates_in_succession Value to set MaxEqualUpdatesInSuccession to.
435  */
436 void Service::set_max_equal_updates_in_succession(const int _max_equal_updates_in_succession)
437 {
438     MaxEqualUpdatesInSuccession = _max_equal_updates_in_succession;
439 }
440
441
442 /**
443  * Getter for member MaxEqualUpdatesInSuccession.
444  * @return Value of MaxEqualUpdatesInSuccession.
445  */
446 int Service::get_max_equal_updates_in_succession() const
447 {
448     return MaxEqualUpdatesInSuccession;
449 }
450
451
452 /**
453  * Get a unique service identify string
454  * @return A unique service identify string
455  */
456 string Service::get_service_name() const
457 {
458     string service_name;
459
460     service_name.append(Protocol);
461     service_name.append(" ");
462     service_name.append(Hostname);
463
464     return service_name;
465 }
466
467
468 /**
469  * Get member DNSCacheTTL
470  * @return DNSCacheTTL
471  */
472 int Service::get_dns_cache_ttl() const
473 {
474     return DNSCacheTTL;
475 }
476
477
478 /**
479  * Set member DNSCacheTTL
480  * @param _dns_cache_ttl DNSCacheTTL
481  */
482 void Service::set_dns_cache_ttl(const int _dns_cache_ttl)
483 {
484     DNSCacheTTL = _dns_cache_ttl;
485 }