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