GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/dns.cc Lines: 512 587 87.2 %
Date: 2019-02-03 02:48:13 Branches: 332 433 76.7 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 *
4
 * The CernVM-FS name resolving uses objects that inherit from the Resolver
5
 * interface.  Resolvers implement a vector interface that resolves mutliple
6
 * names in parallel.  Common cases such as IP addresses as names are handled
7
 * by the base class -- Resolver implementations only have to resolve real host
8
 * names to IPv4/6 addresses, using given search domains if necessary.
9
 *
10
 * Name resolving information is stored in Host objects.  Host objects are
11
 * immutable.  They associate a hostname with sets of IPv4 and IPv6 addresses.
12
 * They also carry the result of the name resolution attempt (success, failure)
13
 * and the TTL in the form of a deadline.  They are only created by a resolver
14
 * and upon creation carry a unique id that corresponds to a particular name
15
 * resolving attempt.
16
 *
17
 * The NormalResolver uses both the CaresResolver for DNS queries and the
18
 * HostfileResolve for queries in /etc/hosts.  If an entry is found in
19
 * /etc/hosts, the CaresResolver is unused.
20
 */
21
22
#include "dns.h"
23
24
#include <arpa/inet.h>
25
#include <arpa/nameser.h>
26
#include <errno.h>
27
#include <netdb.h>
28
#include <poll.h>
29
#include <unistd.h>
30
31
#include <algorithm>
32
#include <cassert>
33
#include <cstdlib>
34
#include <cstring>
35
36
#include "logging.h"
37
#include "sanitizer.h"
38
#include "smalloc.h"
39
#include "util/string.h"
40
41
using namespace std;  // NOLINT
42
43
namespace dns {
44
45
/**
46
 * Sets pos_begin and pos_end to the first/last character of the host name in
47
 * a string like http://<hostname>:<port>.  Works also if the host name is an
48
 * IPv4/6 address.  Sets pos_begin and pos_end to 0 if url doesn't match the
49
 * format.
50
 */
51
635
static void PinpointHostSubstr(
52
  const std::string &url,
53
  unsigned *pos_begin,
54
  unsigned *pos_end)
55
{
56
635
  *pos_begin = *pos_end = 0;
57
635
  const unsigned len = url.size();
58
635
  unsigned i = 0;
59
60
  // Search '//' in the url string and jump behind
61
3981
  for (; i < len; ++i) {
62


3846
    if ((url[i] == '/') && (i < len-2) && (url[i+1] == '/')) {
63
500
      i += 2;
64
500
      *pos_begin = i;
65
500
      break;
66
    }
67
  }
68
69
  // Find the end of the hostname part
70
635
  if (*pos_begin > 0) {
71
500
    bool in_ipv6 = (url[i] == '[');
72
4792
    for (; i < len; ++i) {
73
4774
      if (in_ipv6) {
74
105
        if (url[i] != ']')
75
85
          continue;
76
20
        in_ipv6 = false;
77
      }
78
79

4689
      if ((url[i] == ':') || (url[i] == '/'))
80
482
        break;
81
    }
82
500
    if (!in_ipv6)
83
494
      *pos_end = i - 1;
84
85
500
    if (*pos_end < *pos_begin)
86
17
      *pos_end = *pos_begin = 0;
87
  }
88
635
}
89
90
91
/**
92
 * Returns the host name from a string in the format
93
 * http://<hostname>:<port>[/path]
94
 * or an empty string if url doesn't match the format.
95
 */
96
351
std::string ExtractHost(const std::string &url) {
97
  unsigned pos_begin;
98
  unsigned pos_end;
99
351
  PinpointHostSubstr(url, &pos_begin, &pos_end);
100
351
  if (pos_begin == 0)
101
120
    return "";
102
231
  return url.substr(pos_begin, (pos_end - pos_begin) + 1);
103
}
104
105
106
/**
107
 * Returns the port from a string in the format
108
 * http://<hostname>:<port>/path
109
 * or an empty string if url doesn't match the format.
110
 */
111
264
std::string ExtractPort(const std::string &url) {
112
  unsigned pos_begin;
113
  unsigned pos_end;
114
264
  PinpointHostSubstr(url, &pos_begin, &pos_end);
115


264
  if (pos_begin == 0 ||
116
      pos_end + 2 >= url.size() ||
117
      url.at(pos_end + 1) != ':')
118
31
      return "";
119
120
  // Do not include path
121
233
  std::size_t pos_port = url.find("/", pos_end);
122
233
  std::string retme;
123
233
  if (pos_port == std::string::npos)
124
227
    retme = url.substr(pos_end + 2);
125
  else
126
6
    retme = url.substr(pos_end + 2, pos_port - pos_end - 2);
127
128
  // Port is an integer
129
1120
  for (std::string::iterator it = retme.begin(); it != retme.end(); ++it)
130
891
    if (isdigit(*it) == 0)
131
4
      return "";
132
133
229
  return retme;
134
}
135
136
137
/**
138
 * Replaces the host name in the url with the given IP address.  If it is an
139
 * IPv6 address, it has to be in brackets.  If the input is not a valid URL,
140
 * it is returned unmodified.
141
 */
142
20
string RewriteUrl(const string &url, const string &ip) {
143
  unsigned pos_begin;
144
  unsigned pos_end;
145
20
  PinpointHostSubstr(url, &pos_begin, &pos_end);
146
20
  if (pos_begin == 0)
147
11
    return url;
148
149
9
  string result = url;
150
9
  result.replace(pos_begin, (pos_end - pos_begin) + 1, ip);
151
9
  return result;
152
}
153
154
155
/**
156
 * Removes the brackets from IPv6 addresses.  Leaves IPv4 addresses unchanged.
157
 */
158
8
string StripIp(const string &decorated_ip) {
159
8
  if (!decorated_ip.empty()) {
160

7
    if ((decorated_ip[0] == '[') &&
161
        (decorated_ip[decorated_ip.length()-1] == ']'))
162
    {
163
2
      return decorated_ip.substr(1, decorated_ip.length()-2);
164
    }
165
  }
166
6
  return decorated_ip;
167
}
168
169
170
/**
171
 * Adds http:// if it is missing from proxy
172
 */
173
235
std::string AddDefaultScheme(const std::string &proxy) {
174
235
  const bool ignore_case = true;
175







235
  if (HasPrefix(proxy, "http://", ignore_case) ||
176
      HasPrefix(proxy, "https://", ignore_case) ||
177
      (proxy == "DIRECT") ||
178
      proxy.empty())
179
  {
180
232
    return proxy;
181
  }
182
3
  return "http://" + proxy;
183
}
184
185
186
//------------------------------------------------------------------------------
187
188
189
atomic_int64 Host::global_id_ = 0;
190
191
9
const set<string> &Host::ViewBestAddresses(IpPreference preference) const {
192


9
  if (((preference == kIpPreferSystem) || (preference == kIpPreferV4)) &&
193
      HasIpv4())
194
  {
195
4
    return ipv4_addresses_;
196
  }
197

5
  if ((preference == kIpPreferV6) && !HasIpv6())
198
1
    return ipv4_addresses_;
199
4
  return ipv6_addresses_;
200
}
201
202
203
867
void Host::CopyFrom(const Host &other) {
204
867
  deadline_ = other.deadline_;
205
867
  id_ = other.id_;
206
867
  ipv4_addresses_ = other.ipv4_addresses_;
207
867
  ipv6_addresses_ = other.ipv6_addresses_;
208
867
  name_ = other.name_;
209
867
  status_ = other.status_;
210
867
}
211
212
213
/**
214
 * Creates a copy of the original host with a new ID and sets a new dealine
215
 * given in seconds from the current time.
216
 */
217
1
Host Host::ExtendDeadline(const Host &original, unsigned seconds_from_now) {
218
1
  Host new_host(original);
219
1
  new_host.id_ = atomic_xadd64(&global_id_, 1);
220
1
  new_host.deadline_ = time(NULL) + seconds_from_now;
221
1
  return new_host;
222
}
223
224
225
/**
226
 * All fields except the unique id_ are set by the resolver.  Host objects
227
 * can be copied around but only the resolver can create valid, new objects.
228
 */
229
491
Host::Host()
230
  : deadline_(0)
231
  , id_(atomic_xadd64(&global_id_, 1))
232
491
  , status_(kFailNotYetResolved)
233
{
234
491
}
235
236
237
766
Host::Host(const Host &other) {
238
766
  CopyFrom(other);
239
}
240
241
242
101
Host &Host::operator= (const Host &other) {
243
101
  if (&other == this)
244
    return *this;
245
101
  CopyFrom(other);
246
101
  return *this;
247
}
248
249
250
/**
251
 * Compares the name and the resolved addresses independent of deadlines.  Used
252
 * to decide if the current proxy list needs to be changed after re-resolving
253
 * a host name.
254
 */
255
22
bool Host::IsEquivalent(const Host &other) const {
256
  return (status_ == kFailOk) && (other.status_ == kFailOk) &&
257
      (name_ == other.name_) &&
258
      (ipv4_addresses_ == other.ipv4_addresses_) &&
259


22
      (ipv6_addresses_ == other.ipv6_addresses_);
260
}
261
262
263
/**
264
 * Compares the TTL from a provious call to time() with the current time.
265
 */
266
14
bool Host::IsExpired() const {
267
14
  time_t now = time(NULL);
268
14
  assert(now != static_cast<time_t>(-1));
269
14
  return deadline_ < now;
270
}
271
272
273
/**
274
 * A host object is valid after it has been successfully resolved and until the
275
 * DNS ttl expires.  Successful name resolution means that there is at least
276
 * one IP address.
277
 */
278
16
bool Host::IsValid() const {
279
16
  if (status_ != kFailOk)
280
6
    return false;
281
282

10
  assert(!ipv4_addresses_.empty() || !ipv6_addresses_.empty());
283
10
  return !IsExpired();
284
}
285
286
287
//------------------------------------------------------------------------------
288
289
290
/**
291
 * Basic input validation to ensure that this could syntactically represent a
292
 * valid IPv4 address.
293
 */
294
1384
bool Resolver::IsIpv4Address(const string &address) {
295
  // Are there any unexpected characters?
296
1384
  sanitizer::InputSanitizer sanitizer("09 .");
297
1384
  if (!sanitizer.IsValid(address))
298
920
    return false;
299
300
  // 4 octets in the range 0-255?
301
464
  vector<string> octets = SplitString(address, '.');
302
464
  if (octets.size() != 4)
303
1
    return false;
304
2314
  for (unsigned i = 0; i < 4; ++i) {
305
1852
    uint64_t this_octet = String2Uint64(octets[i]);
306
1852
    if (this_octet > 255)
307
1
      return false;
308
  }
309
310
462
  return true;
311
}
312
313
314
/**
315
 * Basic input validation to ensure that this could syntactically represent a
316
 * valid IPv6 address.
317
 */
318
43
bool Resolver::IsIpv6Address(const string &address) {
319
  // Are there any unexpected characters?
320
43
  sanitizer::InputSanitizer sanitizer("09 af AF :");
321
43
  return sanitizer.IsValid(address);
322
}
323
324
325
852
Resolver::Resolver(
326
  const bool ipv4_only,
327
  const unsigned retries,
328
  const unsigned timeout_ms)
329
  : ipv4_only_(ipv4_only)
330
  , retries_(retries)
331
  , timeout_ms_(timeout_ms)
332
  , throttle_(0)
333
  , min_ttl_(kDefaultMinTtl)
334
852
  , max_ttl_(kDefaultMaxTtl)
335
{
336
852
  prng_.InitLocaltime();
337
}
338
339
340
/**
341
 * Wrapper around the vector interface.
342
 */
343
154
Host Resolver::Resolve(const string &name) {
344
154
  vector<string> names;
345
154
  names.push_back(name);
346
154
  vector<Host> hosts;
347
154
  ResolveMany(names, &hosts);
348
154
  return hosts[0];
349
}
350
351
352
/**
353
 * Calls the overwritten concrete resolver, verifies the sanity of the returned
354
 * addresses and constructs the Host objects in the same order as the names.
355
 */
356
271
void Resolver::ResolveMany(const vector<string> &names, vector<Host> *hosts) {
357
271
  unsigned num = names.size();
358
271
  if (num == 0)
359
    return;
360
361
271
  vector<vector<string> > ipv4_addresses(num);
362
271
  vector<vector<string> > ipv6_addresses(num);
363
271
  vector<Failures> failures(num);
364
271
  vector<unsigned> ttls(num);
365
271
  vector<string> fqdns(num);
366
271
  vector<bool> skip(num);
367
368
  // Deal with special names: empty, IPv4, IPv6
369
567
  for (unsigned i = 0; i < num; ++i) {
370
296
    if (names[i].empty()) {
371
115
      LogCvmfs(kLogDns, kLogDebug, "empty hostname");
372
115
      Host invalid_host;
373
115
      invalid_host.name_ = "";
374
115
      invalid_host.status_ = kFailInvalidHost;
375
115
      hosts->push_back(invalid_host);
376
115
      skip[i] = true;
377
181
    } else if (IsIpv4Address(names[i])) {
378
104
      LogCvmfs(kLogDns, kLogDebug, "IPv4 address %s", names[i].c_str());
379
104
      Host ipv4_host;
380
104
      ipv4_host.name_ = names[i];
381
104
      ipv4_host.status_ = kFailOk;
382
104
      ipv4_host.ipv4_addresses_.insert(names[i]);
383
104
      ipv4_host.deadline_ = time(NULL) + max_ttl_;
384
104
      hosts->push_back(ipv4_host);
385
104
      skip[i] = true;
386


77
    } else if ((names[i].length() >= 3) &&
387
               (names[i][0] == '[') &&
388
               (names[i][names[i].length()-1] == ']'))
389
    {
390
4
      LogCvmfs(kLogDns, kLogDebug, "IPv6 address %s", names[i].c_str());
391
4
      Host ipv6_host;
392
4
      ipv6_host.name_ = names[i];
393
4
      ipv6_host.status_ = kFailOk;
394
4
      ipv6_host.ipv6_addresses_.insert(names[i]);
395
4
      ipv6_host.deadline_ = time(NULL) + max_ttl_;
396
4
      hosts->push_back(ipv6_host);
397
4
      skip[i] = true;
398
    } else {
399
73
      hosts->push_back(Host());
400
73
      skip[i] = false;
401
    }
402
  }
403
404
  DoResolve(
405
271
    names, skip, &ipv4_addresses, &ipv6_addresses, &failures, &ttls, &fqdns);
406
407
  // Construct host objects
408
271
  for (unsigned i = 0; i < num; ++i) {
409
296
    if (skip[i])
410
223
      continue;
411
412
73
    Host host;
413
73
    host.name_ = fqdns[i];
414
73
    host.status_ = failures[i];
415
416
73
    unsigned effective_ttl = ttls[i];
417
73
    if (effective_ttl < min_ttl_) {
418
13
      effective_ttl = min_ttl_;
419
60
    } else if (effective_ttl > max_ttl_) {
420
24
      effective_ttl = max_ttl_;
421
    }
422
73
    host.deadline_ = time(NULL) + effective_ttl;
423
424
73
    if (host.status_ != kFailOk) {
425
      LogCvmfs(kLogDns, kLogDebug, "failed to resolve %s - %d (%s), ttl %u",
426
               names[i].c_str(), host.status_, Code2Ascii(host.status_),
427
12
               effective_ttl);
428
12
      (*hosts)[i] = host;
429
12
      continue;
430
    }
431
432
    // Verify addresses and make them readily available for curl
433
131
    for (unsigned j = 0; j < ipv4_addresses[i].size(); ++j) {
434
70
      if (!IsIpv4Address(ipv4_addresses[i][j])) {
435
        LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn,
436
                 "host name %s resolves to invalid IPv4 address %s",
437
4
                 names[i].c_str(), ipv4_addresses[i][j].c_str());
438
4
        continue;
439
      }
440
      LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s",
441
66
               names[i].c_str(), ipv4_addresses[i][j].c_str());
442
66
      host.ipv4_addresses_.insert(ipv4_addresses[i][j]);
443
    }
444
445
104
    for (unsigned j = 0; j < ipv6_addresses[i].size(); ++j) {
446
43
      if (!IsIpv6Address(ipv6_addresses[i][j])) {
447
        LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn,
448
                 "host name %s resolves to invalid IPv6 address %s",
449
1
                 names[i].c_str(), ipv6_addresses[i][j].c_str());
450
1
        continue;
451
      }
452
      // For URLs we need brackets around IPv6 addresses
453
      LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s",
454
42
               names[i].c_str(), ipv6_addresses[i][j].c_str());
455
42
      host.ipv6_addresses_.insert("[" + ipv6_addresses[i][j] + "]");
456
    }
457
458

61
    if (host.ipv4_addresses_.empty() && host.ipv6_addresses_.empty()) {
459
      LogCvmfs(kLogDns, kLogDebug, "no addresses returned for %s",
460
2
               names[i].c_str());
461
2
      host.status_ = kFailNoAddress;
462
    }
463
464
    // Remove surplus IP addresses
465
61
    if (throttle_ > 0) {
466
4
      while (host.ipv4_addresses_.size() > throttle_) {
467
2
        unsigned random = prng_.Next(host.ipv4_addresses_.size());
468
2
        set<string>::iterator rnd_itr = host.ipv4_addresses_.begin();
469
2
        std::advance(rnd_itr, random);
470
2
        host.ipv4_addresses_.erase(rnd_itr);
471
      }
472
4
      while (host.ipv6_addresses_.size() > throttle_) {
473
2
        unsigned random = prng_.Next(host.ipv6_addresses_.size());
474
2
        set<string>::iterator rnd_itr = host.ipv6_addresses_.begin();
475
2
        std::advance(rnd_itr, random);
476
2
        host.ipv6_addresses_.erase(rnd_itr);
477
      }
478
    }
479
480
61
    (*hosts)[i] = host;
481
  }
482
}
483
484
485
//------------------------------------------------------------------------------
486
487
488
namespace {
489
490
enum ResourceRecord {
491
  kRrA = 0,
492
  kRrAaaa,
493
};
494
495
/**
496
 * Used to transport a name resolving request across the asynchronous c-ares
497
 * interface.  The QueryInfo objects are used for both IPv4 and IPv6 requests.
498
 * The addresses are entered directly via pointers, ttls and fqdns are later
499
 * merged into a single response (for IPv4/IPv6).
500
 */
501
63
struct QueryInfo {
502
63
  QueryInfo(
503
    vector<string> *a,
504
    const string &n,
505
    const ResourceRecord r)
506
    : addresses(a)
507
    , complete(false)
508
    , fqdn(n)
509
    , name(n)
510
    , record(r)
511
    , status(kFailOther)
512
63
    , ttl(0)
513
63
  { }
514
515
  vector<string> *addresses;
516
  bool complete;
517
  string fqdn;
518
  string name;
519
  ResourceRecord record;
520
  Failures status;
521
  unsigned ttl;
522
};
523
524
}  // namespace
525
526
527
static Failures CaresExtractIpv4(const unsigned char *abuf, int alen,
528
                                 vector<string> *addresses,
529
                                 unsigned *ttl,
530
                                 string *fqdn);
531
static Failures CaresExtractIpv6(const unsigned char *abuf, int alen,
532
                                 vector<string> *addresses,
533
                                 unsigned *ttl,
534
                                 string *fqdn);
535
536
/**
537
 * Called when a DNS query returns or times out.  Sets the return status and the
538
 * IP addresses (if successful) in the QueryInfo object.
539
 */
540
63
static void CallbackCares(
541
  void *arg,
542
  int status,
543
  int timeouts_ms,
544
  unsigned char *abuf,
545
  int alen)
546
{
547
63
  QueryInfo *info = reinterpret_cast<QueryInfo *>(arg);
548
549
63
  info->complete = true;
550

63
  switch (status) {
551
    case ARES_SUCCESS:
552
      Failures retval;
553
49
      switch (info->record) {
554
        case kRrA:
555
          retval = CaresExtractIpv4(
556
26
            abuf, alen, info->addresses, &info->ttl, &info->fqdn);
557
26
          break;
558
        case kRrAaaa:
559
          retval = CaresExtractIpv6(
560
23
            abuf, alen, info->addresses, &info->ttl, &info->fqdn);
561
23
          break;
562
        default:
563
          // Never here.
564
          abort();
565
      }
566
49
      info->status = retval;
567
49
      break;
568
    case ARES_ENODATA:
569
2
      info->status = kFailUnknownHost;
570
2
      break;
571
    case ARES_EFORMERR:
572
      info->status = kFailMalformed;
573
      break;
574
    case ARES_ENOTFOUND:
575
8
      info->status = kFailUnknownHost;
576
8
      break;
577
    case ARES_ETIMEOUT:
578
3
      info->status = kFailTimeout;
579
3
      break;
580
    case ARES_ECONNREFUSED:
581
1
      info->status = kFailInvalidResolvers;
582
1
      break;
583
    default:
584
      info->status = kFailOther;
585
  }
586
63
}
587
588
589
/**
590
 * Extracts IPv4 addresses from an A record return in c-ares.  TTLs are
591
 * merged to a single one, representing the minimum.
592
 */
593
26
static Failures CaresExtractIpv4(
594
  const unsigned char *abuf,
595
  int alen,
596
  vector<string> *addresses,
597
  unsigned *ttl,
598
  string *fqdn)
599
{
600
26
  struct hostent *host_entry = NULL;
601
  struct ares_addrttl records[CaresResolver::kMaxAddresses];
602
26
  int naddrttls = CaresResolver::kMaxAddresses;
603
26
  int retval = ares_parse_a_reply(abuf, alen, &host_entry, records, &naddrttls);
604
605
26
  switch (retval) {
606
    case ARES_SUCCESS:
607
26
      if (host_entry == NULL)
608
        return kFailMalformed;
609
26
      if (host_entry->h_name == NULL) {
610
        ares_free_hostent(host_entry);
611
        return kFailMalformed;
612
      }
613
26
      *fqdn = string(host_entry->h_name);
614
26
      ares_free_hostent(host_entry);
615
616
26
      if (naddrttls <= 0)
617
        return kFailMalformed;
618
26
      *ttl = unsigned(-1);
619
52
      for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) {
620
26
        if (records[i].ttl < 0)
621
          continue;
622
26
        *ttl = std::min(unsigned(records[i].ttl), *ttl);
623
624
        char addrstr[INET_ADDRSTRLEN];
625
        const void *retval_p =
626
26
          inet_ntop(AF_INET, &(records[i].ipaddr), addrstr, INET_ADDRSTRLEN);
627
26
        if (!retval_p)
628
          continue;
629
26
        addresses->push_back(addrstr);
630
      }
631
26
      if (addresses->empty())
632
        return kFailMalformed;
633
26
      return kFailOk;
634
    case ARES_EBADRESP:
635
      // Fall through
636
    case ARES_ENODATA:
637
      return kFailMalformed;
638
    default:
639
      return kFailOther;
640
  }
641
}
642
643
644
23
static Failures CaresExtractIpv6(
645
  const unsigned char *abuf,
646
  int alen,
647
  vector<string> *addresses,
648
  unsigned *ttl,
649
  string *fqdn)
650
{
651
23
  struct hostent *host_entry = NULL;
652
  struct ares_addr6ttl records[CaresResolver::kMaxAddresses];
653
23
  int naddrttls = CaresResolver::kMaxAddresses;
654
  int retval =
655
23
    ares_parse_aaaa_reply(abuf, alen, &host_entry, records, &naddrttls);
656
657
23
  switch (retval) {
658
    case ARES_SUCCESS:
659
23
      if (host_entry == NULL)
660
        return kFailMalformed;
661
23
      if (host_entry->h_name == NULL) {
662
        ares_free_hostent(host_entry);
663
        return kFailMalformed;
664
      }
665
23
      *fqdn = string(host_entry->h_name);
666
23
      ares_free_hostent(host_entry);
667
668
23
      if (naddrttls <= 0)
669
        return kFailMalformed;
670
23
      *ttl = unsigned(-1);
671
46
      for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) {
672
23
        if (records[i].ttl < 0)
673
          continue;
674
23
        *ttl = std::min(unsigned(records[i].ttl), *ttl);
675
676
        char addrstr[INET6_ADDRSTRLEN];
677
        const void *retval_p =
678
23
          inet_ntop(AF_INET6, &(records[i].ip6addr), addrstr, INET6_ADDRSTRLEN);
679
23
        if (!retval_p)
680
          continue;
681
23
        addresses->push_back(addrstr);
682
      }
683
23
      if (addresses->empty())
684
        return kFailMalformed;
685
23
      return kFailOk;
686
    case ARES_EBADRESP:
687
      // Fall through
688
    case ARES_ENODATA:
689
      return kFailMalformed;
690
    default:
691
      return kFailOther;
692
  }
693
}
694
695
696
391
CaresResolver::CaresResolver(
697
  const bool ipv4_only,
698
  const unsigned retries,
699
  const unsigned timeout_ms)
700
  : Resolver(ipv4_only, retries, timeout_ms)
701
  , channel_(NULL)
702
391
  , lookup_options_(strdup("b"))
703
{
704
}
705
706
707
582
CaresResolver::~CaresResolver() {
708
291
  if (channel_) {
709
291
    ares_destroy(*channel_);
710
291
    free(channel_);
711
  }
712
291
  free(lookup_options_);
713

582
}
714
715
716
/**
717
 * Returns a CaresResolver readily initialized, or NULL if an error occurs.
718
 */
719
391
CaresResolver *CaresResolver::Create(
720
  const bool ipv4_only,
721
  const unsigned retries,
722
  const unsigned timeout_ms)
723
{
724
  int retval;
725
391
  if (getenv("HOSTALIASES") == NULL) {
726
14
    retval = setenv("HOSTALIASES", "/etc/hosts", 1);
727
14
    assert(retval == 0);
728
  }
729
730
391
  CaresResolver *resolver = new CaresResolver(ipv4_only, retries, timeout_ms);
731
  resolver->channel_ = reinterpret_cast<ares_channel *>(
732
391
    smalloc(sizeof(ares_channel)));
733
391
  memset(resolver->channel_, 0, sizeof(ares_channel));
734
735
  struct ares_addr_node *addresses;
736
  struct ares_addr_node *iter;
737
  struct ares_options options;
738
  int optmask;
739
391
  memset(&options, 0, sizeof(options));
740
391
  options.timeout = timeout_ms;
741
391
  options.tries = 1 + retries;
742
391
  options.lookups = resolver->lookup_options_;
743
391
  optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_LOOKUPS;
744
391
  retval = ares_init_options(resolver->channel_, &options, optmask);
745
391
  if (retval != ARES_SUCCESS)
746
    goto create_fail;
747
748
  // Save search domains
749
391
  retval = ares_save_options(*resolver->channel_, &options, &optmask);
750
391
  if (retval != ARES_SUCCESS)
751
    goto create_fail;
752
782
  for (int i = 0; i < options.ndomains; ++i) {
753
391
    resolver->domains_.push_back(options.domains[i]);
754
  }
755
391
  ares_destroy_options(&options);
756
391
  resolver->system_domains_ = resolver->domains_;
757
758
  // Save the system default resolvers
759
391
  addresses = NULL;
760
391
  retval = ares_get_servers(*resolver->channel_, &addresses);
761
391
  if (retval != ARES_SUCCESS)
762
    goto create_fail;
763
391
  iter = addresses;
764
1564
  while (iter) {
765
782
    switch (iter->family) {
766
      case AF_INET: {
767
        char addrstr[INET_ADDRSTRLEN];
768
        const void *retval_p =
769
782
          inet_ntop(AF_INET, &(iter->addr), addrstr, INET_ADDRSTRLEN);
770
782
        if (!retval_p) {
771
          LogCvmfs(kLogDns, kLogDebug | kLogSyslogErr,
772
                   "invalid system name resolver");
773
        } else {
774
782
          resolver->resolvers_.push_back(string(addrstr) + ":53");
775
        }
776
782
        break;
777
      }
778
      case AF_INET6: {
779
        char addrstr[INET6_ADDRSTRLEN];
780
        const void *retval_p =
781
          inet_ntop(AF_INET6, &(iter->addr), addrstr, INET6_ADDRSTRLEN);
782
        if (!retval_p) {
783
          LogCvmfs(kLogDns, kLogDebug | kLogSyslogErr,
784
                   "invalid system name resolver");
785
        } else {
786
          resolver->resolvers_.push_back("[" + string(addrstr) + "]:53");
787
        }
788
        break;
789
      }
790
      default:
791
        // Never here.
792
        abort();
793
    }
794
782
    iter = iter->next;
795
  }
796
391
  ares_free_data(addresses);
797
391
  resolver->system_resolvers_ = resolver->resolvers_;
798
799
391
  return resolver;
800
801
 create_fail:
802
  LogCvmfs(kLogDns, kLogDebug | kLogSyslogErr,
803
           "failed to initialize c-ares resolver (%d - %s)",
804
           retval, ares_strerror(retval));
805
  free(resolver->channel_);
806
  resolver->channel_ = NULL;
807
  delete resolver;
808
  return NULL;
809
}
810
811
812
/**
813
 * Pushes all the DNS queries into the c-ares channel and waits for the results
814
 * on the file descriptors.
815
 */
816
231
void CaresResolver::DoResolve(
817
  const vector<string> &names,
818
  const vector<bool> &skip,
819
  vector<vector<string> > *ipv4_addresses,
820
  vector<vector<string> > *ipv6_addresses,
821
  vector<Failures> *failures,
822
  vector<unsigned> *ttls,
823
  vector<string> *fqdns)
824
{
825
231
  unsigned num = names.size();
826
231
  if (num == 0)
827
    return;
828
829
231
  vector<QueryInfo *> infos_ipv4(num, NULL);
830
231
  vector<QueryInfo *> infos_ipv6(num, NULL);
831
832
481
  for (unsigned i = 0; i < num; ++i) {
833
250
    if (skip[i])
834
218
      continue;
835
836
32
    if (!ipv4_only()) {
837
31
      infos_ipv6[i] = new QueryInfo(&(*ipv6_addresses)[i], names[i], kRrAaaa);
838
      ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_aaaa,
839
31
                  CallbackCares, infos_ipv6[i]);
840
    }
841
32
    infos_ipv4[i] = new QueryInfo(&(*ipv4_addresses)[i], names[i], kRrA);
842
    ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_a,
843
32
                CallbackCares, infos_ipv4[i]);
844
  }
845
846
  bool all_complete;
847
1418156
  do {
848
1418156
    all_complete = true;
849
1418156
    WaitOnCares();
850
1418476
    for (unsigned i = 0; i < num; ++i) {
851


1418245
      if ((infos_ipv4[i] && !infos_ipv4[i]->complete) ||
852
          (infos_ipv6[i] && !infos_ipv6[i]->complete))
853
      {
854
1417925
        all_complete = false;
855
1417925
        break;
856
      }
857
    }
858
  } while (!all_complete);
859
860
  // Silently ignore errors with IPv4/6 if there are at least some usable IP
861
  // addresses.
862
481
  for (unsigned i = 0; i < num; ++i) {
863
250
    if (skip[i])
864
218
      continue;
865
866
32
    Failures status = kFailOther;
867
32
    (*ttls)[i] = unsigned(-1);
868
32
    (*fqdns)[i] = "";
869
32
    if (infos_ipv6[i]) {
870
31
      status = infos_ipv6[i]->status;
871
31
      if (status == kFailOk) {
872
23
        (*ttls)[i] = std::min(infos_ipv6[i]->ttl, (*ttls)[i]);
873
23
        (*fqdns)[i] = infos_ipv6[i]->fqdn;
874
      }
875
    }
876
32
    if (infos_ipv4[i]) {
877
32
      (*ttls)[i] = std::min(infos_ipv4[i]->ttl, (*ttls)[i]);
878
32
      if ((*fqdns)[i] == "")
879
9
        (*fqdns)[i] = infos_ipv4[i]->fqdn;
880
32
      if (status != kFailOk)
881
9
        status = infos_ipv4[i]->status;
882
    }
883
32
    (*failures)[i] = status;
884
  }
885
886
481
  for (unsigned i = 0; i < num; ++i) {
887
250
    delete infos_ipv4[i];
888
250
    delete infos_ipv6[i];
889
  }
890
}
891
892
893
3
bool CaresResolver::SetResolvers(const vector<string> &resolvers) {
894
3
  string address_list = JoinStrings(resolvers, ",");
895
3
  int retval = ares_set_servers_csv(*channel_, address_list.c_str());
896
3
  if (retval != ARES_SUCCESS)
897
    return false;
898
899
3
  resolvers_ = resolvers;
900
3
  return true;
901
}
902
903
904
/**
905
 * Changes the options of the active channel.  This is hacky and deals with
906
 * c-ares internal data structures because there is no way to do it via public
907
 * APIs.
908
 */
909
2
bool CaresResolver::SetSearchDomains(const vector<string> &domains) {
910
  // From ares_private.h
911
  struct {
912
    int flags;
913
    int timeout;
914
    int tries;
915
    int ndots;
916
    int rotate;
917
    int udp_port;
918
    int tcp_port;
919
    int socket_send_buffer_size;
920
    int socket_receive_buffer_size;
921
    char **domains;
922
    int ndomains;
923
    // More fields come in the original data structure
924
  } ares_channelhead;
925
926
2
  memcpy(&ares_channelhead, *channel_, sizeof(ares_channelhead));
927
2
  if (ares_channelhead.domains) {
928
5
    for (int i = 0; i < ares_channelhead.ndomains; ++i) {
929
3
      free(ares_channelhead.domains[i]);
930
    }
931
2
    free(ares_channelhead.domains);
932
2
    ares_channelhead.domains = NULL;
933
  }
934
935
2
  ares_channelhead.ndomains = static_cast<int>(domains.size());
936
2
  if (ares_channelhead.ndomains > 0) {
937
    ares_channelhead.domains = reinterpret_cast<char **>(
938
1
      smalloc(ares_channelhead.ndomains * sizeof(char *)));
939
3
    for (int i = 0; i < ares_channelhead.ndomains; ++i) {
940
2
      ares_channelhead.domains[i] = strdup(domains[i].c_str());
941
    }
942
  }
943
944
2
  memcpy(*channel_, &ares_channelhead, sizeof(ares_channelhead));
945
946
2
  domains_ = domains;
947
2
  return true;
948
}
949
950
951
void CaresResolver::SetSystemResolvers() {
952
  int retval = SetResolvers(system_resolvers_);
953
  assert(retval == true);
954
}
955
956
957
void CaresResolver::SetSystemSearchDomains() {
958
  int retval = SetSearchDomains(system_domains_);
959
  assert(retval == true);
960
}
961
962
963
/**
964
 * Polls on c-ares sockets and triggers call-backs execution.  Might be
965
 * necessary to call this repeatadly.
966
 */
967
1418156
void CaresResolver::WaitOnCares() {
968
  // Adapted from libcurl
969
  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
970
  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
971
1418156
  int bitmask = ares_getsock(*channel_, socks, ARES_GETSOCK_MAXNUM);
972
1418156
  unsigned num = 0;
973
5672194
  for (unsigned i = 0; i < ARES_GETSOCK_MAXNUM; ++i) {
974
2836097
    pfd[i].events = 0;
975
2836097
    pfd[i].revents = 0;
976
2836097
    if (ARES_GETSOCK_READABLE(bitmask, i)) {
977
1417941
      pfd[i].fd = socks[i];
978
1417941
      pfd[i].events |= POLLRDNORM|POLLIN;
979
    }
980
2836097
    if (ARES_GETSOCK_WRITABLE(bitmask, i)) {
981
      pfd[i].fd = socks[i];
982
      pfd[i].events |= POLLWRNORM|POLLOUT;
983
    }
984
2836097
    if (pfd[i].events != 0)
985
1417941
      num++;
986
    else
987
1418156
      break;
988
  }
989
990
1418156
  int nfds = 0;
991
1418156
  if (num > 0) {
992
1417941
    do {
993
1417941
      nfds = poll(pfd, num, timeout_ms());
994
1417941
      if (nfds == -1) {
995
        // poll must not fail for other reasons
996
        if ((errno != EAGAIN) && (errno != EINTR))
997
          abort();
998
      }
999
    } while (nfds == -1);
1000
  }
1001
1002
1418156
  if (nfds == 0) {
1003
    // Call ares_process() unconditonally here, even if we simply timed out
1004
    // above, as otherwise the ares name resolve won't timeout.
1005
221
    ares_process_fd(*channel_, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
1006
  } else {
1007
    // Go through the descriptors and ask for executing the callbacks.
1008
2835870
    for (unsigned i = 0; i < num; ++i) {
1009
      ares_process_fd(*channel_,
1010
                      pfd[i].revents & (POLLRDNORM|POLLIN) ?
1011
                        pfd[i].fd : ARES_SOCKET_BAD,
1012
                      pfd[i].revents & (POLLWRNORM|POLLOUT) ?
1013

1417935
                        pfd[i].fd : ARES_SOCKET_BAD);
1014
    }
1015
  }
1016
1418156
}
1017
1018
1019
//------------------------------------------------------------------------------
1020
1021
1022
/**
1023
 * Opens a file descriptor to the host file that stays open until destruction.
1024
 * If no path is given, the HOST_ALIASES environment variable is evaluated
1025
 * followed by /etc/hosts.
1026
 */
1027
252
HostfileResolver *HostfileResolver::Create(
1028
  const string &path,
1029
  bool ipv4_only)
1030
{
1031
252
  HostfileResolver *resolver = new HostfileResolver(ipv4_only);
1032
1033
252
  string hosts_file = path;
1034
252
  if (hosts_file == "") {
1035
208
    char *hosts_env = getenv("HOST_ALIASES");
1036
208
    if (hosts_env != NULL) {
1037
2
      hosts_file = string(hosts_env);
1038
    } else {
1039
206
      hosts_file = "/etc/hosts";
1040
    }
1041
  }
1042
252
  resolver->fhosts_ = fopen(hosts_file.c_str(), "r");
1043
252
  if (!resolver->fhosts_) {
1044
    LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn, "failed to read host file %s",
1045
2
             hosts_file.c_str());
1046
2
    delete resolver;
1047
2
    return NULL;
1048
  }
1049
250
  return resolver;
1050
}
1051
1052
1053
/**
1054
 * Used to process longer domain names before shorter ones, in order to get
1055
 * the correct fully qualified domain name.  Reversed return value in order to
1056
 * sort in descending order.
1057
 */
1058
6
static bool SortNameLength(const string &a, const string &b) {
1059
6
  unsigned len_a = a.length();
1060
6
  unsigned len_b = b.length();
1061
6
  if (len_a != len_b)
1062
6
    return len_a > len_b;
1063
  return a > b;
1064
}
1065
1066
1067
/**
1068
 * Creates a fresh reverse lookup map
1069
 */
1070
140
void HostfileResolver::DoResolve(
1071
  const vector<string> &names,
1072
  const vector<bool> &skip,
1073
  vector< vector<std::string> > *ipv4_addresses,
1074
  vector< vector<std::string> > *ipv6_addresses,
1075
  vector<Failures> *failures,
1076
  vector<unsigned> *ttls,
1077
  vector<string> *fqdns)
1078
{
1079
140
  unsigned num = names.size();
1080
140
  if (num == 0)
1081
    return;
1082
1083
140
  ParseHostFile();
1084
168
  for (unsigned i = 0; i < num; ++i) {
1085
146
    if (skip[i])
1086
118
      continue;
1087
1088
28
    vector<string> effective_names;
1089

28
    if (!names[i].empty() && (names[i][names[i].length()-1] == '.')) {
1090
2
      effective_names.push_back(names[i].substr(0, names[i].length()-1));
1091
    } else {
1092
26
      effective_names.push_back(names[i]);
1093
32
      for (unsigned j = 0; j < domains().size(); ++j) {
1094
6
        effective_names.push_back(names[i] + "." + domains()[j]);
1095
      }
1096
    }
1097
1098
    // Use the longest matching name as fqdn
1099
28
    std::sort(effective_names.begin(), effective_names.end(), SortNameLength);
1100
1101
28
    (*failures)[i] = kFailUnknownHost;
1102
28
    (*fqdns)[i] = names[i];
1103
37
    for (unsigned j = 0; j < effective_names.size(); ++j) {
1104
      map<string, HostEntry>::iterator iter =
1105
31
        host_map_.find(effective_names[j]);
1106
31
      if (iter != host_map_.end()) {
1107
        (*ipv4_addresses)[i].insert((*ipv4_addresses)[i].end(),
1108
                                    iter->second.ipv4_addresses.begin(),
1109
22
                                    iter->second.ipv4_addresses.end());
1110
        (*ipv6_addresses)[i].insert((*ipv6_addresses)[i].end(),
1111
                                    iter->second.ipv6_addresses.begin(),
1112
22
                                    iter->second.ipv6_addresses.end());
1113
22
        (*ttls)[i] = min_ttl_;
1114
22
        (*fqdns)[i] = effective_names[j];
1115
22
        (*failures)[i] = kFailOk;
1116
22
        break;
1117
      }  // Host name found
1118
    }  // All possible names (search domains added)
1119
  }
1120
}
1121
1122
1123
252
HostfileResolver::HostfileResolver(const bool ipv4_only)
1124
  : Resolver(ipv4_only, 0, 0)
1125
252
  , fhosts_(NULL)
1126
252
{ }
1127
1128
1129
502
HostfileResolver::~HostfileResolver() {
1130
251
  if (fhosts_)
1131
249
    fclose(fhosts_);
1132

251
}
1133
1134
1135
/**
1136
 * TODO: this should be only necessary when the modification timestamp changed.
1137
 */
1138
140
void HostfileResolver::ParseHostFile() {
1139
140
  assert(fhosts_);
1140
140
  rewind(fhosts_);
1141
140
  host_map_.clear();
1142
1143
140
  string line;
1144
891
  while (GetLineFile(fhosts_, &line)) {
1145
891
    const unsigned len = line.length();
1146
891
    unsigned i = 0;
1147
891
    string address;
1148
891
    while (i < len) {
1149
2024
      if (line[i] == '#')
1150
9
        break;
1151
1152


5181
      while (((line[i] == ' ') || (line[i] == '\t')) && (i < len))
1153
1151
        ++i;
1154
1155
2015
      string token;
1156


23890
      while ((line[i] != ' ') && (line[i] != '\t') && (line[i] != '#') &&
1157
             (i < len))
1158
      {
1159
19860
        token += line[i];
1160
19860
        ++i;
1161
      }
1162
1163
2015
      if (address == "") {
1164
882
        address = token;
1165
      } else {
1166
1133
        if (token[token.length()-1] == '.')
1167
          token = token.substr(0, token.length()-1);
1168
1169
1133
        map<string, HostEntry>::iterator iter = host_map_.find(token);
1170
1133
        if (iter == host_map_.end()) {
1171
1005
          HostEntry entry;
1172
1005
          if (IsIpv4Address(address))
1173
289
            entry.ipv4_addresses.push_back(address);
1174
          else
1175
716
            if (!ipv4_only()) entry.ipv6_addresses.push_back(address);
1176
          // printf("ADD %s -> %s\n", token.c_str(), address.c_str());
1177
1005
          host_map_[token] = entry;
1178
        } else {
1179
128
          if (IsIpv4Address(address))
1180
3
            iter->second.ipv4_addresses.push_back(address);
1181
          else
1182
125
            if (!ipv4_only()) iter->second.ipv6_addresses.push_back(address);
1183
          // printf("PUSHING %s -> %s\n", token.c_str(), address.c_str());
1184
        }
1185
      }
1186
    }  // Current line
1187
  }  // Hosts file
1188
140
}
1189
1190
1191
205
bool HostfileResolver::SetSearchDomains(const vector<string> &domains) {
1192
205
  domains_ = domains;
1193
205
  return true;
1194
}
1195
1196
1197
void HostfileResolver::SetSystemSearchDomains() {
1198
  // TODO(jblomer)
1199
  assert(false);
1200
}
1201
1202
1203
//------------------------------------------------------------------------------
1204
1205
1206
/**
1207
 * Creates hostfile and c-ares resolvers and uses c-ares resolvers search
1208
 * domains for the hostfile resolver.
1209
 */
1210
205
NormalResolver *NormalResolver::Create(
1211
  const bool ipv4_only,
1212
  const unsigned retries,
1213
  const unsigned timeout_ms)
1214
{
1215
  CaresResolver *cares_resolver =
1216
205
    CaresResolver::Create(ipv4_only, retries, timeout_ms);
1217
205
  if (!cares_resolver)
1218
    return NULL;
1219
205
  HostfileResolver *hostfile_resolver = HostfileResolver::Create("", ipv4_only);
1220
205
  if (!hostfile_resolver) {
1221
1
    delete cares_resolver;
1222
1
    return NULL;
1223
  }
1224
204
  bool retval = hostfile_resolver->SetSearchDomains(cares_resolver->domains());
1225
204
  assert(retval);
1226
1227
204
  NormalResolver *normal_resolver = new NormalResolver();
1228
204
  normal_resolver->cares_resolver_ = cares_resolver;
1229
204
  normal_resolver->hostfile_resolver_ = hostfile_resolver;
1230
204
  normal_resolver->domains_ = cares_resolver->domains();
1231
204
  normal_resolver->resolvers_ = cares_resolver->resolvers();
1232
204
  normal_resolver->retries_ = cares_resolver->retries();
1233
204
  normal_resolver->timeout_ms_ = cares_resolver->timeout_ms();
1234
204
  return normal_resolver;
1235
}
1236
1237
1238
/**
1239
 * Makes only sense for the c-ares resolver.
1240
 */
1241
1
bool NormalResolver::SetResolvers(const vector<string> &resolvers) {
1242
1
  return cares_resolver_->SetResolvers(resolvers);
1243
}
1244
1245
1246
/**
1247
 * Set new search domains for both resolvers or for none.
1248
 */
1249
bool NormalResolver::SetSearchDomains(const vector<string> &domains) {
1250
  vector<string> old_domains = hostfile_resolver_->domains();
1251
  bool retval = hostfile_resolver_->SetSearchDomains(domains);
1252
  if (!retval)
1253
    return false;
1254
  retval = cares_resolver_->SetSearchDomains(domains);
1255
  if (!retval) {
1256
    retval = hostfile_resolver_->SetSearchDomains(old_domains);
1257
    assert(retval);
1258
    return false;
1259
  }
1260
  return true;
1261
}
1262
1263
1264
void NormalResolver::SetSystemResolvers() {
1265
  cares_resolver_->SetSystemResolvers();
1266
}
1267
1268
1269
void NormalResolver::SetSystemSearchDomains() {
1270
  cares_resolver_->SetSystemSearchDomains();
1271
  bool retval =
1272
    hostfile_resolver_->SetSearchDomains(cares_resolver_->domains());
1273
  assert(retval);
1274
}
1275
1276
1277
/**
1278
 * First pass done by the hostfile resolver, all successfully resolved names
1279
 * are skipped by the c-ares resolver.
1280
 */
1281
117
void NormalResolver::DoResolve(
1282
  const vector<string> &names,
1283
  const vector<bool> &skip,
1284
  vector< vector<string> > *ipv4_addresses,
1285
  vector< vector<string> > *ipv6_addresses,
1286
  vector<Failures> *failures,
1287
  vector<unsigned> *ttls,
1288
  vector<string> *fqdns)
1289
{
1290
117
  unsigned num = names.size();
1291
  hostfile_resolver_->DoResolve(names, skip, ipv4_addresses, ipv6_addresses,
1292
117
                                failures, ttls, fqdns);
1293
117
  vector<bool> skip_cares = skip;
1294
234
  for (unsigned i = 0; i < num; ++i) {
1295
117
    if ((*failures)[i] == kFailOk)
1296
116
      skip_cares[i] = true;
1297
  }
1298
  cares_resolver_->DoResolve(names, skip_cares, ipv4_addresses, ipv6_addresses,
1299
117
                             failures, ttls, fqdns);
1300
117
}
1301
1302
1303
204
NormalResolver::NormalResolver()
1304
  : Resolver(false, 0, 0)
1305
  , cares_resolver_(NULL)
1306
204
  , hostfile_resolver_(NULL)
1307
{
1308
204
}
1309
1310
1311
406
NormalResolver::~NormalResolver() {
1312
203
  delete cares_resolver_;
1313
203
  delete hostfile_resolver_;
1314
203
}
1315
1316
}  // namespace dns