Line |
Branch |
Exec |
Source |
1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
*/ |
4 |
|
|
|
5 |
|
|
#ifndef CVMFS_DNS_H_ |
6 |
|
|
#define CVMFS_DNS_H_ |
7 |
|
|
|
8 |
|
|
#include <stdint.h> |
9 |
|
|
|
10 |
|
|
#include <cstdio> |
11 |
|
|
#include <ctime> |
12 |
|
|
#include <map> |
13 |
|
|
#include <set> |
14 |
|
|
#include <string> |
15 |
|
|
#include <vector> |
16 |
|
|
|
17 |
|
|
#include "duplex_cares.h" |
18 |
|
|
#include "gtest/gtest_prod.h" |
19 |
|
|
#include "util/atomic.h" |
20 |
|
|
#include "util/prng.h" |
21 |
|
|
#include "util/single_copy.h" |
22 |
|
|
|
23 |
|
|
namespace dns { |
24 |
|
|
|
25 |
|
|
/** |
26 |
|
|
* Possible errors when trying to resolve a host name. |
27 |
|
|
*/ |
28 |
|
|
enum Failures { |
29 |
|
|
kFailOk = 0, |
30 |
|
|
kFailInvalidResolvers, |
31 |
|
|
kFailTimeout, |
32 |
|
|
kFailInvalidHost, |
33 |
|
|
kFailUnknownHost, ///< Resolver returned a negative reply |
34 |
|
|
kFailMalformed, |
35 |
|
|
kFailNoAddress, ///< Resolver returned a positive reply but without IPs |
36 |
|
|
kFailNotYetResolved, |
37 |
|
|
kFailOther, |
38 |
|
|
|
39 |
|
|
kFailNumEntries |
40 |
|
|
}; |
41 |
|
|
|
42 |
|
|
|
43 |
|
|
/** |
44 |
|
|
* Steers IP protocol selection. |
45 |
|
|
*/ |
46 |
|
|
enum IpPreference { |
47 |
|
|
// use system default, currently unused and mapped to IPv4 |
48 |
|
|
kIpPreferSystem = 0, |
49 |
|
|
kIpPreferV4, |
50 |
|
|
kIpPreferV6, |
51 |
|
|
}; |
52 |
|
|
|
53 |
|
16 |
inline const char *Code2Ascii(const Failures error) { |
54 |
|
|
const char *texts[kFailNumEntries + 1]; |
55 |
|
16 |
texts[0] = "OK"; |
56 |
|
16 |
texts[1] = "invalid resolver addresses"; |
57 |
|
16 |
texts[2] = "DNS query timeout"; |
58 |
|
16 |
texts[3] = "invalid host name to resolve"; |
59 |
|
16 |
texts[4] = "unknown host name"; |
60 |
|
16 |
texts[5] = "malformed DNS request"; |
61 |
|
16 |
texts[6] = "no IP address for host"; |
62 |
|
16 |
texts[7] = "internal error, not yet resolved"; |
63 |
|
16 |
texts[8] = "unknown name resolving error"; |
64 |
|
16 |
texts[9] = "no text"; |
65 |
|
16 |
return texts[error]; |
66 |
|
|
} |
67 |
|
|
|
68 |
|
|
|
69 |
|
|
std::string ExtractHost(const std::string &url); |
70 |
|
|
std::string ExtractPort(const std::string &url); |
71 |
|
|
std::string RewriteUrl(const std::string &url, const std::string &ip); |
72 |
|
|
std::string StripIp(const std::string &decorated_ip); |
73 |
|
|
std::string AddDefaultScheme(const std::string &proxy); |
74 |
|
|
|
75 |
|
|
|
76 |
|
|
/** |
77 |
|
|
* Stores the resolved IPv4 and IPv6 addresses of a host name including their |
78 |
|
|
* time to live. Data in these objects are immutable. They are created by a |
79 |
|
|
* a Resolver. Once the TTL has expired, they become invalid and a new Host |
80 |
|
|
* object should be fetched from a resolver. |
81 |
|
|
* |
82 |
|
|
* A host object can be copied into a new object with an extended deadline. |
83 |
|
|
* This is useful if an attempt to resolve a name fails where it previously |
84 |
|
|
* succeeded. In this case, the extended deadline can be used to delay another |
85 |
|
|
* name resolution attempt for some grace period. |
86 |
|
|
* |
87 |
|
|
* For successful name resolution, the name is the fully qualified domain name, |
88 |
|
|
* even if a short name was given to the resolver. |
89 |
|
|
*/ |
90 |
|
|
class Host { |
91 |
|
|
FRIEND_TEST(T_Dns, HostEquivalent); |
92 |
|
|
FRIEND_TEST(T_Dns, HostExpired); |
93 |
|
|
FRIEND_TEST(T_Dns, HostValid); |
94 |
|
|
FRIEND_TEST(T_Dns, HostExtendDeadline); |
95 |
|
|
FRIEND_TEST(T_Dns, HostBestAddresses); |
96 |
|
|
friend class Resolver; |
97 |
|
|
|
98 |
|
|
public: |
99 |
|
|
static Host ExtendDeadline(const Host &original, unsigned seconds_from_now); |
100 |
|
|
Host(); |
101 |
|
|
Host(const Host &other); |
102 |
|
|
Host &operator= (const Host &other); |
103 |
|
|
bool IsEquivalent(const Host &other) const; |
104 |
|
|
bool IsExpired() const; |
105 |
|
|
bool IsValid() const; |
106 |
|
|
|
107 |
|
6 |
time_t deadline() const { return deadline_; } |
108 |
|
4 |
int64_t id() const { return id_; } |
109 |
|
56 |
bool HasIpv4() const { return !ipv4_addresses_.empty(); } |
110 |
|
52 |
bool HasIpv6() const { return !ipv6_addresses_.empty(); } |
111 |
|
66 |
const std::set<std::string> &ipv4_addresses() const { |
112 |
|
66 |
return ipv4_addresses_; |
113 |
|
|
} |
114 |
|
54 |
const std::set<std::string> &ipv6_addresses() const { |
115 |
|
54 |
return ipv6_addresses_; |
116 |
|
|
} |
117 |
|
|
const std::set<std::string> &ViewBestAddresses(IpPreference preference) const; |
118 |
|
48 |
const std::string &name() const { return name_; } |
119 |
|
60 |
Failures status() const { return status_; } |
120 |
|
|
|
121 |
|
|
private: |
122 |
|
|
void CopyFrom(const Host &other); |
123 |
|
|
|
124 |
|
|
/** |
125 |
|
|
* Counter that is increased with every creation of a host object. Allows to |
126 |
|
|
* distinguish two Host objects with the same host name. E.g. when the proxy |
127 |
|
|
* list in Download.cc reads "http://A:3128|http://A:3128". |
128 |
|
|
*/ |
129 |
|
|
static atomic_int64 global_id_; |
130 |
|
|
|
131 |
|
|
/** |
132 |
|
|
* When the name resolution becomes outdated, in UTC seconds since UNIX epoch. |
133 |
|
|
* Once the deadline is passed, IsValid returns false. |
134 |
|
|
*/ |
135 |
|
|
time_t deadline_; |
136 |
|
|
|
137 |
|
|
/** |
138 |
|
|
* The unique id of this instance of Host. |
139 |
|
|
*/ |
140 |
|
|
int64_t id_; |
141 |
|
|
|
142 |
|
|
/** |
143 |
|
|
* ASCII representation of IPv4 addresses (a.b.c.d), so that they can be |
144 |
|
|
* readily used by curl. |
145 |
|
|
*/ |
146 |
|
|
std::set<std::string> ipv4_addresses_; |
147 |
|
|
|
148 |
|
|
/** |
149 |
|
|
* ASCII representation of IPv6 addresses in the form "[a:b:c:d:e:f:g:h]", |
150 |
|
|
* so that they can be readily used by curl. |
151 |
|
|
*/ |
152 |
|
|
std::set<std::string> ipv6_addresses_; |
153 |
|
|
|
154 |
|
|
/** |
155 |
|
|
* The host name either fully qualified or within the search domain. |
156 |
|
|
*/ |
157 |
|
|
std::string name_; |
158 |
|
|
|
159 |
|
|
/** |
160 |
|
|
* Error code of the name resolution that led to this object. |
161 |
|
|
*/ |
162 |
|
|
Failures status_; |
163 |
|
|
}; |
164 |
|
|
|
165 |
|
|
|
166 |
|
|
/** |
167 |
|
|
* Abstract interface of a name resolver. Returns a Host object upon successful |
168 |
|
|
* name resolution. Also provides a vector interface to resolve multiple names |
169 |
|
|
* in parallel. Can be configured with DNS servers, with a timeout, and whether |
170 |
|
|
* to use IPv4 only or not. |
171 |
|
|
*/ |
172 |
|
|
class Resolver : SingleCopy { |
173 |
|
|
public: |
174 |
|
|
/** |
175 |
|
|
* Enlarge very small TTLs by default to 1 minute. |
176 |
|
|
*/ |
177 |
|
|
static const unsigned kDefaultMinTtl = 60; |
178 |
|
|
|
179 |
|
|
/** |
180 |
|
|
* Cut off very large TTLs by default to 1 day. |
181 |
|
|
*/ |
182 |
|
|
static const unsigned kDefaultMaxTtl = 84600; |
183 |
|
|
|
184 |
|
|
Resolver(const bool ipv4_only, |
185 |
|
|
const unsigned retries, |
186 |
|
|
const unsigned timeout_ms); |
187 |
|
1110 |
virtual ~Resolver() { } |
188 |
|
|
|
189 |
|
|
/** |
190 |
|
|
* A list of new resolvers in the form <IP address>[:port]. |
191 |
|
|
*/ |
192 |
|
|
virtual bool SetResolvers(const std::vector<std::string> &resolvers) = 0; |
193 |
|
|
virtual bool SetSearchDomains(const std::vector<std::string> &domains) = 0; |
194 |
|
|
virtual void SetSystemResolvers() = 0; |
195 |
|
|
virtual void SetSystemSearchDomains() = 0; |
196 |
|
|
Host Resolve(const std::string &name); |
197 |
|
|
void ResolveMany(const std::vector<std::string> &names, |
198 |
|
|
std::vector<Host> *hosts); |
199 |
|
|
|
200 |
|
320 |
const std::vector<std::string> &domains() const { return domains_; } |
201 |
|
506 |
bool ipv4_only() const { return ipv4_only_; } |
202 |
|
139 |
const std::vector<std::string> &resolvers() const { return resolvers_; } |
203 |
|
232 |
unsigned retries() const { return retries_; } |
204 |
|
3488489 |
unsigned timeout_ms() const { return timeout_ms_; } |
205 |
|
31 |
void set_throttle(const unsigned throttle) { throttle_ = throttle; } |
206 |
|
30 |
unsigned throttle() const { return throttle_; } |
207 |
|
64 |
void set_min_ttl(unsigned seconds) { min_ttl_ = seconds; } |
208 |
|
30 |
unsigned min_ttl() const { return min_ttl_; } |
209 |
|
64 |
void set_max_ttl(unsigned seconds) { max_ttl_ = seconds; } |
210 |
|
30 |
unsigned max_ttl() const { return max_ttl_; } |
211 |
|
|
|
212 |
|
|
protected: |
213 |
|
|
/** |
214 |
|
|
* Takes host names and returns the resolved lists of A and AAAA records in |
215 |
|
|
* the same order. To keep it simple, returns only a single TTL per host, |
216 |
|
|
* the lower value of both record types A/AAAA. The output vectors have |
217 |
|
|
* the same size as the input vector names. Names that are handled by the |
218 |
|
|
* base class are marked with skip[i] set to true. The input names are |
219 |
|
|
* completed to fully qualified domain names. |
220 |
|
|
*/ |
221 |
|
|
virtual void DoResolve(const std::vector<std::string> &names, |
222 |
|
|
const std::vector<bool> &skip, |
223 |
|
|
std::vector<std::vector<std::string> > *ipv4_addresses, |
224 |
|
|
std::vector<std::vector<std::string> > *ipv6_addresses, |
225 |
|
|
std::vector<Failures> *failures, |
226 |
|
|
std::vector<unsigned> *ttls, |
227 |
|
|
std::vector<std::string> *fqdns) = 0; |
228 |
|
|
bool IsIpv4Address(const std::string &address); |
229 |
|
|
bool IsIpv6Address(const std::string &address); |
230 |
|
|
|
231 |
|
|
/** |
232 |
|
|
* Currently active search domain list |
233 |
|
|
*/ |
234 |
|
|
std::vector<std::string> domains_; |
235 |
|
|
|
236 |
|
|
/** |
237 |
|
|
* Do not try to get AAAA records if true. |
238 |
|
|
*/ |
239 |
|
|
bool ipv4_only_; |
240 |
|
|
|
241 |
|
|
/** |
242 |
|
|
* Currently used resolver list in the form <ip address>:<port> |
243 |
|
|
*/ |
244 |
|
|
std::vector<std::string> resolvers_; |
245 |
|
|
|
246 |
|
|
/** |
247 |
|
|
* 1 + retries_ attempts to unresponsive servers, each attempt bounded by |
248 |
|
|
* timeout_ms_ |
249 |
|
|
*/ |
250 |
|
|
unsigned retries_; |
251 |
|
|
|
252 |
|
|
/** |
253 |
|
|
* Timeout in milliseconds for DNS queries. Zero means no timeout. |
254 |
|
|
*/ |
255 |
|
|
unsigned timeout_ms_; |
256 |
|
|
|
257 |
|
|
/** |
258 |
|
|
* Limit number of resolved IP addresses. If throttle_ is 0 it has no effect. |
259 |
|
|
* Otherwise, if more than thottle_ IPs are registered for a host, only |
260 |
|
|
* throttle_ randomly picked IPs are returned. |
261 |
|
|
*/ |
262 |
|
|
unsigned throttle_; |
263 |
|
|
|
264 |
|
|
/** |
265 |
|
|
* Effective minimum TTL, which by default is kDefaultMinTtl |
266 |
|
|
*/ |
267 |
|
|
unsigned min_ttl_; |
268 |
|
|
|
269 |
|
|
/** |
270 |
|
|
* Effective maximum TTL, which by default is kDefaultMaxTtl |
271 |
|
|
*/ |
272 |
|
|
unsigned max_ttl_; |
273 |
|
|
|
274 |
|
|
/** |
275 |
|
|
* Required for picking IP addresses in throttle_ |
276 |
|
|
*/ |
277 |
|
|
Prng prng_; |
278 |
|
|
}; |
279 |
|
|
|
280 |
|
|
|
281 |
|
|
/** |
282 |
|
|
* Implementation of the Resolver interface using the c-ares library. |
283 |
|
|
*/ |
284 |
|
|
class CaresResolver : public Resolver { |
285 |
|
|
friend class NormalResolver; |
286 |
|
|
public: |
287 |
|
|
/** |
288 |
|
|
* More IP addresses for a single name will be ignored. Due to c-ares |
289 |
|
|
* exponential backoff, the number of retries should be limited to 2. |
290 |
|
|
* That results in 2 apptempts with the given timeout and a third one with |
291 |
|
|
* the timeout doubled. |
292 |
|
|
*/ |
293 |
|
|
static const unsigned kMaxAddresses = 16; |
294 |
|
|
|
295 |
|
|
static CaresResolver *Create(const bool ipv4_only, |
296 |
|
|
const unsigned retries, |
297 |
|
|
const unsigned timeout_ms); |
298 |
|
|
virtual ~CaresResolver(); |
299 |
|
|
|
300 |
|
|
virtual bool SetResolvers(const std::vector<std::string> &resolvers); |
301 |
|
|
virtual bool SetSearchDomains(const std::vector<std::string> &domains); |
302 |
|
|
virtual void SetSystemResolvers(); |
303 |
|
|
virtual void SetSystemSearchDomains(); |
304 |
|
|
|
305 |
|
|
protected: |
306 |
|
|
CaresResolver(const bool ipv4_only, |
307 |
|
|
const unsigned retries, |
308 |
|
|
const unsigned timeout_ms); |
309 |
|
|
virtual void DoResolve(const std::vector<std::string> &names, |
310 |
|
|
const std::vector<bool> &skip, |
311 |
|
|
std::vector<std::vector<std::string> > *ipv4_addresses, |
312 |
|
|
std::vector<std::vector<std::string> > *ipv6_addresses, |
313 |
|
|
std::vector<Failures> *failures, |
314 |
|
|
std::vector<unsigned> *ttls, |
315 |
|
|
std::vector<std::string> *fqdns); |
316 |
|
|
|
317 |
|
|
private: |
318 |
|
|
void WaitOnCares(); |
319 |
|
|
ares_channel *channel_; |
320 |
|
|
char *lookup_options_; |
321 |
|
|
std::vector<std::string> system_resolvers_; |
322 |
|
|
std::vector<std::string> system_domains_; |
323 |
|
|
}; |
324 |
|
|
|
325 |
|
|
|
326 |
|
|
/** |
327 |
|
|
* Resolves against static name information like in /etc/hosts. Setting |
328 |
|
|
* resolver addresses is a no-op for this resolver. Search domains are not |
329 |
|
|
* automatically found but need to be set. Not the most efficient |
330 |
|
|
* implementation but in the context of cvmfs should be called at most every 5 |
331 |
|
|
* minutes. |
332 |
|
|
*/ |
333 |
|
|
class HostfileResolver : public Resolver { |
334 |
|
|
friend class NormalResolver; |
335 |
|
|
public: |
336 |
|
|
static HostfileResolver *Create(const std::string &path, bool ipv4_only); |
337 |
|
|
virtual ~HostfileResolver(); |
338 |
|
|
|
339 |
|
✗ |
virtual bool SetResolvers(const std::vector<std::string> & /* resolvers */) { |
340 |
|
✗ |
return true; |
341 |
|
|
} |
342 |
|
|
virtual bool SetSearchDomains(const std::vector<std::string> &domains); |
343 |
|
✗ |
virtual void SetSystemResolvers() { } |
344 |
|
|
virtual void SetSystemSearchDomains(); |
345 |
|
|
|
346 |
|
|
protected: |
347 |
|
|
explicit HostfileResolver(const bool ipv4_only); |
348 |
|
|
virtual void DoResolve(const std::vector<std::string> &names, |
349 |
|
|
const std::vector<bool> &skip, |
350 |
|
|
std::vector<std::vector<std::string> > *ipv4_addresses, |
351 |
|
|
std::vector<std::vector<std::string> > *ipv6_addresses, |
352 |
|
|
std::vector<Failures> *failures, |
353 |
|
|
std::vector<unsigned> *ttls, |
354 |
|
|
std::vector<std::string> *fqdns); |
355 |
|
|
|
356 |
|
|
private: |
357 |
|
|
struct HostEntry { |
358 |
|
|
std::vector<std::string> ipv4_addresses; |
359 |
|
|
std::vector<std::string> ipv6_addresses; |
360 |
|
|
}; |
361 |
|
|
static const int kIpMaxLength = 45; |
362 |
|
|
static const int kHostnameMaxLength = 253; |
363 |
|
|
void ParseHostFile(); |
364 |
|
|
/** |
365 |
|
|
* Host names to lists of IPv4 and IPv6 addresses. Reverse lookup in the |
366 |
|
|
* hosts file. |
367 |
|
|
*/ |
368 |
|
|
std::map<std::string, HostEntry> host_map_; |
369 |
|
|
|
370 |
|
|
/** |
371 |
|
|
* Open the file descriptor when the resolver is constructed and only release |
372 |
|
|
* on destruction. Thereby we can be relatively sure to not see I/O errors |
373 |
|
|
* once constructed. |
374 |
|
|
*/ |
375 |
|
|
FILE *fhosts_; |
376 |
|
|
}; |
377 |
|
|
|
378 |
|
|
|
379 |
|
|
/** |
380 |
|
|
* The normal resolver combines Hostfile and C-ares resolver. First looks up |
381 |
|
|
* host names in the host file. All non-matches are looked up by c-ares. |
382 |
|
|
*/ |
383 |
|
|
class NormalResolver : public Resolver { |
384 |
|
|
FRIEND_TEST(T_Dns, NormalResolverConstruct); |
385 |
|
|
|
386 |
|
|
public: |
387 |
|
|
static NormalResolver *Create(const bool ipv4_only, |
388 |
|
|
const unsigned retries, |
389 |
|
|
const unsigned timeout_ms); |
390 |
|
|
virtual bool SetResolvers(const std::vector<std::string> &resolvers); |
391 |
|
|
virtual bool SetSearchDomains(const std::vector<std::string> &domains); |
392 |
|
|
virtual void SetSystemResolvers(); |
393 |
|
|
virtual void SetSystemSearchDomains(); |
394 |
|
|
virtual ~NormalResolver(); |
395 |
|
|
|
396 |
|
|
protected: |
397 |
|
|
virtual void DoResolve(const std::vector<std::string> &names, |
398 |
|
|
const std::vector<bool> &skip, |
399 |
|
|
std::vector<std::vector<std::string> > *ipv4_addresses, |
400 |
|
|
std::vector<std::vector<std::string> > *ipv6_addresses, |
401 |
|
|
std::vector<Failures> *failures, |
402 |
|
|
std::vector<unsigned> *ttls, |
403 |
|
|
std::vector<std::string> *fqdns); |
404 |
|
|
NormalResolver(); |
405 |
|
|
|
406 |
|
|
private: |
407 |
|
|
CaresResolver *cares_resolver_; |
408 |
|
|
HostfileResolver *hostfile_resolver_; |
409 |
|
|
}; |
410 |
|
|
|
411 |
|
|
} // namespace dns |
412 |
|
|
|
413 |
|
|
#endif // CVMFS_DNS_H_ |
414 |
|
|
|