GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/dns.h Lines: 36 39 92.3 %
Date: 2019-02-03 02:48:13 Branches: 1 3 33.3 %

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 "atomic.h"
18
#include "duplex_cares.h"
19
#include "gtest/gtest_prod.h"
20
#include "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
12
inline const char *Code2Ascii(const Failures error) {
54
  const char *texts[kFailNumEntries + 1];
55
12
  texts[0] = "OK";
56
12
  texts[1] = "invalid resolver addresses";
57
12
  texts[2] = "DNS query timeout";
58
12
  texts[3] = "invalid host name to resolve";
59
12
  texts[4] = "unknown host name";
60
12
  texts[5] = "malformed DNS request";
61
12
  texts[6] = "no IP address for host";
62
12
  texts[7] = "internal error, not yet resolved";
63
12
  texts[8] = "unknown name resolving error";
64
12
  texts[9] = "no text";
65
12
  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
1257
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
52
  bool HasIpv4() const { return !ipv4_addresses_.empty(); }
110
52
  bool HasIpv6() const { return !ipv6_addresses_.empty(); }
111
152
  const std::set<std::string> &ipv4_addresses() const {
112
152
    return ipv4_addresses_;
113
  }
114
52
  const std::set<std::string> &ipv6_addresses() const {
115
52
    return ipv6_addresses_;
116
  }
117
  const std::set<std::string> &ViewBestAddresses(IpPreference preference) const;
118
48
  const std::string &name() const { return name_; }
119
45
  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
750
  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
449
  const std::vector<std::string> &domains() const { return domains_; }
201
873
  bool ipv4_only() const { return ipv4_only_; }
202
207
  const std::vector<std::string> &resolvers() const { return resolvers_; }
203
382
  unsigned retries() const { return retries_; }
204
1418322
  unsigned timeout_ms() const { return timeout_ms_; }
205
59
  void set_throttle(const unsigned throttle) { throttle_ = throttle; }
206
58
  unsigned throttle() const { return throttle_; }
207
118
  void set_min_ttl(unsigned seconds) { min_ttl_ = seconds; }
208
58
  unsigned min_ttl() const { return min_ttl_; }
209
118
  void set_max_ttl(unsigned seconds) { max_ttl_ = seconds; }
210
58
  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.
289
   */
290
  static const unsigned kMaxAddresses = 16;
291
292
  static CaresResolver *Create(const bool ipv4_only,
293
                               const unsigned retries,
294
                               const unsigned timeout_ms);
295
  virtual ~CaresResolver();
296
297
  virtual bool SetResolvers(const std::vector<std::string> &resolvers);
298
  virtual bool SetSearchDomains(const std::vector<std::string> &domains);
299
  virtual void SetSystemResolvers();
300
  virtual void SetSystemSearchDomains();
301
302
 protected:
303
  CaresResolver(const bool ipv4_only,
304
                const unsigned retries,
305
                const unsigned timeout_ms);
306
  virtual void DoResolve(const std::vector<std::string> &names,
307
                         const std::vector<bool> &skip,
308
                         std::vector<std::vector<std::string> > *ipv4_addresses,
309
                         std::vector<std::vector<std::string> > *ipv6_addresses,
310
                         std::vector<Failures> *failures,
311
                         std::vector<unsigned> *ttls,
312
                         std::vector<std::string> *fqdns);
313
314
 private:
315
  void WaitOnCares();
316
  ares_channel *channel_;
317
  char *lookup_options_;
318
  std::vector<std::string> system_resolvers_;
319
  std::vector<std::string> system_domains_;
320
};
321
322
323
/**
324
 * Resolves against static name information like in /etc/hosts.  Setting
325
 * resolver addresses is a no-op for this resolver.  Search domains are not
326
 * automatically found but need to be set.  Not the most efficient
327
 * implementation but in the context of cvmfs should be called at most every 5
328
 * minutes.
329
 */
330
class HostfileResolver : public Resolver {
331
  friend class NormalResolver;
332
 public:
333
  static HostfileResolver *Create(const std::string &path, bool ipv4_only);
334
  virtual ~HostfileResolver();
335
336
  virtual bool SetResolvers(const std::vector<std::string> &resolvers) {
337
    return true;
338
  }
339
  virtual bool SetSearchDomains(const std::vector<std::string> &domains);
340
  virtual void SetSystemResolvers() { }
341
  virtual void SetSystemSearchDomains();
342
343
 protected:
344
  explicit HostfileResolver(const bool ipv4_only);
345
  virtual void DoResolve(const std::vector<std::string> &names,
346
                         const std::vector<bool> &skip,
347
                         std::vector<std::vector<std::string> > *ipv4_addresses,
348
                         std::vector<std::vector<std::string> > *ipv6_addresses,
349
                         std::vector<Failures> *failures,
350
                         std::vector<unsigned> *ttls,
351
                         std::vector<std::string> *fqdns);
352
353
 private:
354
9045
  struct HostEntry {
355
    std::vector<std::string> ipv4_addresses;
356
    std::vector<std::string> ipv6_addresses;
357
  };
358
  void ParseHostFile();
359
360
  /**
361
   * Host names to lists of IPv4 and IPv6 addresses.  Reverse lookup in the
362
   * hosts file.
363
   */
364
  std::map<std::string, HostEntry> host_map_;
365
366
  /**
367
   * Open the file descriptor when the resolver is constructed and only release
368
   * on destruction.  Thereby we can be relatively sure to not see I/O errors
369
   * once constructed.
370
   */
371
  FILE *fhosts_;
372
};
373
374
375
/**
376
 * The normal resolver combines Hostfile and C-ares resolver.  First looks up
377
 * host names in the host file.  All non-matches are looked up by c-ares.
378
 */
379
class NormalResolver : public Resolver {
380
  FRIEND_TEST(T_Dns, NormalResolverConstruct);
381
382
 public:
383
  static NormalResolver *Create(const bool ipv4_only,
384
                                const unsigned retries,
385
                                const unsigned timeout_ms);
386
  virtual bool SetResolvers(const std::vector<std::string> &resolvers);
387
  virtual bool SetSearchDomains(const std::vector<std::string> &domains);
388
  virtual void SetSystemResolvers();
389
  virtual void SetSystemSearchDomains();
390
  virtual ~NormalResolver();
391
392
 protected:
393
  virtual void DoResolve(const std::vector<std::string> &names,
394
                         const std::vector<bool> &skip,
395
                         std::vector<std::vector<std::string> > *ipv4_addresses,
396
                         std::vector<std::vector<std::string> > *ipv6_addresses,
397
                         std::vector<Failures> *failures,
398
                         std::vector<unsigned> *ttls,
399
                         std::vector<std::string> *fqdns);
400
  NormalResolver();
401
402
 private:
403
  CaresResolver *cares_resolver_;
404
  HostfileResolver *hostfile_resolver_;
405
};
406
407
}  // namespace dns
408
409
#endif  // CVMFS_DNS_H_