created and passed first unit tests for DNS; finished recovery from PingScheduler...
[pingcheck] / src / dns / dnsmaster.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  Christian Herdtweck, Intra2net AG 2015
21  */
22
23 #include "dns/dnsmaster.h"
24
25 #include <logfunc.hpp>
26 #include <boost/bind.hpp>
27 #include <boost/asio/placeholders.hpp>
28
29 #include "dns/ippseudoresolver.h"
30 #include "dns/dnsresolver.h"
31
32 using boost::bind;
33 using I2n::Logger::GlobalLogger;
34
35
36 DnsMasterItem DnsMaster::TheOnlyInstance;
37
38 // just delegates work to other create_master function
39 void DnsMaster::create_master(const IoServiceItem &io_serv,
40                             const boost::asio::ip::address &default_name_server,
41                             const int resolved_ip_ttl_threshold,
42                             const int max_address_resolution_attempts,
43                             const std::string &cache_file)
44 {
45     GlobalLogger.info() << "Creating DNS Cache";
46     DnsCacheItem cache( new DnsCache(io_serv, cache_file) );
47     create_master(io_serv, default_name_server, resolved_ip_ttl_threshold,
48                   max_address_resolution_attempts, cache);
49 }
50
51 void DnsMaster::create_master(const IoServiceItem &io_serv,
52                             const boost::asio::ip::address &default_name_server,
53                             const int resolved_ip_ttl_threshold,
54                             const int max_address_resolution_attempts,
55                             const DnsCacheItem &cache)
56 {
57     if (TheOnlyInstance)
58     {
59         GlobalLogger.warning()
60             << "Blocking attempt to create another DnsMaster instance!";
61         return;
62     }
63
64     GlobalLogger.info() << "Creating DNS Master";
65     TheOnlyInstance.reset( new DnsMaster(io_serv,
66                                          default_name_server,
67                                          resolved_ip_ttl_threshold,
68                                          max_address_resolution_attempts,
69                                          cache)
70                          );
71 }
72
73 DnsMaster::DnsMaster(const IoServiceItem &io_serv,
74                      const boost::asio::ip::address &default_name_server,
75                      const int resolved_ip_ttl_threshold,
76                      const int max_address_resolution_attempts,
77                      const DnsCacheItem &cache)
78     : IoService( io_serv )
79     , DefaultNameServer( default_name_server )
80     , ResolvedIpTtlThreshold( resolved_ip_ttl_threshold )
81     , MaxAddressResolutionAttempts( max_address_resolution_attempts )
82     , Cache(cache)
83     , ResolverMap()
84 {
85 }
86
87
88 DnsMasterItem& DnsMaster::get_instance()
89 {
90     if ( !TheOnlyInstance )
91         GlobalLogger.error()
92             << "Request to return DnsMaster instance before creating it!";
93     return TheOnlyInstance;
94 }
95
96 DnsMaster::~DnsMaster()
97 {
98     GlobalLogger.info() << "DnsMaster is being destructed";
99
100     if (DnsMaster::TheOnlyInstance)
101     {   // apparently, this static variable still exists while itself is
102         // destructed...
103         //GlobalLogger.warning() << "DnsMaster is being destructed that is not "
104         //                       << "singleton instance TheOnlyInstance!";
105         DnsMaster::TheOnlyInstance.reset();
106     }
107
108     // Items in ResolverMap and the DnsCache might still be referenced by
109     // Resolvers and are smart pointers, anyway --> nothing to do here
110 }
111
112
113
114 ResolverItem& DnsMaster::get_resolver_for( const std::string &hostname,
115                                            const PingProtocol &ping_protocol )
116 {
117     // find suitable DnsIpProtocol for ping protocol
118     DnsIpProtocol protocol = ping2dns_protocol(ping_protocol);
119     return get_resolver_for(hostname, protocol);
120 }
121
122
123 ResolverItem& DnsMaster::get_resolver_for(const std::string &hostname,
124                                           const DnsIpProtocol &protocol)
125 {
126     // create key to ResolverMap
127     resolver_key_type key(hostname, protocol);
128     if ( ResolverMap.count(key) == 0 )
129     {   // need to create a resolver
130
131         // check if it is an ip address, so can create a simple pseudo resolver
132         if ( is_ip(hostname) )
133         {
134             boost::asio::ip::address ip
135                               = boost::asio::ip::address::from_string(hostname);
136             if ( (protocol == DNS_IPv4 && !ip.is_v4()) ||
137                  (protocol == DNS_IPv6 && !ip.is_v6()) )
138                 GlobalLogger.warning() << "Asked to create a DNS resolver "
139                                        << "for wrong IP protocol: v4 != v6! "
140                                        << "We will comply.";
141             GlobalLogger.info() << "Creating PseudoResolver for IP " << ip;
142             ResolverItem new_resolver( new IpPseudoResolver(IoService,
143                                                             hostname,
144                                                             Cache) );
145             ResolverMap[key] = new_resolver;
146         }
147         else
148         {
149             GlobalLogger.info() << "DnsMaster: Creating Resolver for host "
150                 << hostname << " and protocol " << to_string(protocol);
151             ResolverItem new_resolver( new DnsResolver(IoService,
152                                                        hostname,
153                                                        protocol,
154                                                        Cache,
155                                                        DefaultNameServer) );
156             ResolverMap[key] = new_resolver;
157         }
158     }
159     return ResolverMap[key];
160 }
161
162 /**
163  * return true if given hostname string actually is an IP
164  *
165  * delegates decision to boost::asio::ip::address::from_string
166  */
167 bool DnsMaster::is_ip(const std::string &hostname) const
168 {
169     try
170     {
171         boost::asio::ip::address ip = boost::asio::ip::address::from_string(
172                                                                       hostname);
173         return ip.is_v4() || ip.is_v6();
174     }
175     catch ( const std::exception &ex )
176     {
177         return false;
178     }
179 }
180
181
182 DnsIpProtocol DnsMaster::ping2dns_protocol(const PingProtocol& pprot)
183 {
184     switch (pprot)
185     {
186         case PingProtocol_ICMP:     return DNS_IPv4; break;
187         case PingProtocol_ICMPv6:   return DNS_IPv6; break;
188         case PingProtocol_TCP:      return DNS_IPv4; break;
189         case PingProtocol_TCP_IPv6: return DNS_IPv6; break;
190         default:
191             GlobalLogger.warning() << "Unexpected ping protocol: "
192                                    << static_cast<int>(pprot);
193             return DNS_IPALL;
194             break;
195     }
196 }
197
198 /*boost::asio::ip::address &DnsMaster::get_name_server() const
199 {
200     return NameServer;
201 }*/
202
203 int DnsMaster::get_resolved_ip_ttl_threshold() const
204 {
205     return ResolvedIpTtlThreshold;
206 }
207
208 int DnsMaster::get_max_address_resolution_attempts() const
209 {
210     return MaxAddressResolutionAttempts;
211 }
212
213 std::string to_string(const DnsIpProtocol &protocol)
214 {
215     switch (protocol)
216     {
217         case DNS_IPv4:  return "IPv4"; break;
218         case DNS_IPv6:  return "IPv6"; break;
219         case DNS_IPALL: return "IPv4/6"; break;
220         default: GlobalLogger.warning() << "Unexpected protocol in to_string!";
221                  return "Unexpected Protocol"; break;
222     }
223 }
224 // (created using vim -- the world's best text editor)
225