GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/dns.h
Date: 2023-02-05 02:36:10
Exec Total Coverage
Lines: 34 37 91.9%
Branches: 0 0 -%

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