CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
dns.cc
Go to the documentation of this file.
1 
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 <cstdio>
34 #include <cstdlib>
35 #include <cstring>
36 
37 #include "sanitizer.h"
38 #include "util/exception.h"
39 #include "util/logging.h"
40 #include "util/smalloc.h"
41 #include "util/string.h"
42 
43 using namespace std; // NOLINT
44 
45 namespace dns {
46 
53 static void PinpointHostSubstr(const std::string &url,
54  unsigned *pos_begin,
55  unsigned *pos_end) {
56  *pos_begin = *pos_end = 0;
57  const unsigned len = url.size();
58  unsigned i = 0;
59 
60  // Search '//' in the url string and jump behind
61  for (; i < len; ++i) {
62  if ((url[i] == '/') && (i < len - 2) && (url[i + 1] == '/')) {
63  i += 2;
64  *pos_begin = i;
65  break;
66  }
67  }
68 
69  // Search '@' within the hostname part and jump behind if present
70  if (*pos_begin > 0) {
71  for (i = *pos_begin; i < len; ++i) {
72  if (url[i] == '/') {
73  break;
74  } else if (url[i] == '@') {
75  *pos_begin = ++i;
76  break;
77  }
78  }
79  }
80 
81  // Find the end of the hostname part
82  if (*pos_begin > 0) {
83  bool in_ipv6 = (url[*pos_begin] == '[');
84  for (i = *pos_begin; i < len; ++i) {
85  if (in_ipv6) {
86  if (url[i] != ']')
87  continue;
88  in_ipv6 = false;
89  }
90 
91  if ((url[i] == ':') || (url[i] == '/'))
92  break;
93  }
94  if (!in_ipv6)
95  *pos_end = i - 1;
96 
97  if (*pos_end < *pos_begin)
98  *pos_end = *pos_begin = 0;
99  }
100 }
101 
102 
108 std::string ExtractHost(const std::string &url) {
109  unsigned pos_begin;
110  unsigned pos_end;
111  PinpointHostSubstr(url, &pos_begin, &pos_end);
112  if (pos_begin == 0)
113  return "";
114  return url.substr(pos_begin, (pos_end - pos_begin) + 1);
115 }
116 
117 
123 std::string ExtractPort(const std::string &url) {
124  unsigned pos_begin;
125  unsigned pos_end;
126  PinpointHostSubstr(url, &pos_begin, &pos_end);
127  if (pos_begin == 0 || pos_end + 2 >= url.size() || url.at(pos_end + 1) != ':')
128  return "";
129 
130  // Do not include path
131  const std::size_t pos_port = url.find("/", pos_end);
132  std::string retme;
133  if (pos_port == std::string::npos)
134  retme = url.substr(pos_end + 2);
135  else
136  retme = url.substr(pos_end + 2, pos_port - pos_end - 2);
137 
138  // Port is an integer
139  for (std::string::iterator it = retme.begin(); it != retme.end(); ++it)
140  if (isdigit(*it) == 0)
141  return "";
142 
143  return retme;
144 }
145 
146 
152 string RewriteUrl(const string &url, const string &ip) {
153  unsigned pos_begin;
154  unsigned pos_end;
155  PinpointHostSubstr(url, &pos_begin, &pos_end);
156  if (pos_begin == 0)
157  return url;
158 
159  string result = url;
160  result.replace(pos_begin, (pos_end - pos_begin) + 1, ip);
161  return result;
162 }
163 
164 
168 string StripIp(const string &decorated_ip) {
169  if (!decorated_ip.empty()) {
170  if ((decorated_ip[0] == '[')
171  && (decorated_ip[decorated_ip.length() - 1] == ']')) {
172  return decorated_ip.substr(1, decorated_ip.length() - 2);
173  }
174  }
175  return decorated_ip;
176 }
177 
178 
182 std::string AddDefaultScheme(const std::string &proxy) {
183  const bool ignore_case = true;
184  if (HasPrefix(proxy, "http://", ignore_case)
185  || HasPrefix(proxy, "https://", ignore_case) || (proxy == "DIRECT")
186  || proxy.empty()) {
187  return proxy;
188  }
189  return "http://" + proxy;
190 }
191 
192 
193 //------------------------------------------------------------------------------
194 
195 
196 atomic_int64 Host::global_id_ = 0;
197 
198 const set<string> &Host::ViewBestAddresses(IpPreference preference) const {
199  if (((preference == kIpPreferSystem) || (preference == kIpPreferV4))
200  && HasIpv4()) {
201  return ipv4_addresses_;
202  }
203  if ((preference == kIpPreferV6) && !HasIpv6())
204  return ipv4_addresses_;
205  return ipv6_addresses_;
206 }
207 
208 
209 void Host::CopyFrom(const Host &other) {
210  deadline_ = other.deadline_;
211  id_ = other.id_;
212  ipv4_addresses_ = other.ipv4_addresses_;
213  ipv6_addresses_ = other.ipv6_addresses_;
214  name_ = other.name_;
215  status_ = other.status_;
216 }
217 
218 
223 Host Host::ExtendDeadline(const Host &original, unsigned seconds_from_now) {
224  Host new_host(original);
225  new_host.id_ = atomic_xadd64(&global_id_, 1);
226  new_host.deadline_ = time(NULL) + seconds_from_now;
227  return new_host;
228 }
229 
230 
235 Host::Host()
236  : deadline_(0)
237  , id_(atomic_xadd64(&global_id_, 1))
238  , status_(kFailNotYetResolved) { }
239 
240 
241 Host::Host(const Host &other) { CopyFrom(other); }
242 
243 
244 Host &Host::operator=(const Host &other) {
245  if (&other == this)
246  return *this;
247  CopyFrom(other);
248  return *this;
249 }
250 
251 
257 bool Host::IsEquivalent(const Host &other) const {
258  return (status_ == kFailOk) && (other.status_ == kFailOk)
259  && (name_ == other.name_) && (ipv4_addresses_ == other.ipv4_addresses_)
260  && (ipv6_addresses_ == other.ipv6_addresses_);
261 }
262 
263 
267 bool Host::IsExpired() const {
268  const time_t now = time(NULL);
269  assert(now != static_cast<time_t>(-1));
270  return deadline_ < now;
271 }
272 
273 
279 bool Host::IsValid() const {
280  if (status_ != kFailOk)
281  return false;
282 
283  assert(!ipv4_addresses_.empty() || !ipv6_addresses_.empty());
284  return !IsExpired();
285 }
286 
287 
288 //------------------------------------------------------------------------------
289 
290 
295 bool Resolver::IsIpv4Address(const string &address) {
296  // Are there any unexpected characters?
297  const sanitizer::InputSanitizer sanitizer("09 .");
298  if (!sanitizer.IsValid(address))
299  return false;
300 
301  // 4 octets in the range 0-255?
302  vector<string> octets = SplitString(address, '.');
303  if (octets.size() != 4)
304  return false;
305  for (unsigned i = 0; i < 4; ++i) {
306  const uint64_t this_octet = String2Uint64(octets[i]);
307  if (this_octet > 255)
308  return false;
309  }
310 
311  return true;
312 }
313 
314 
319 bool Resolver::IsIpv6Address(const string &address) {
320  // Are there any unexpected characters?
321  const sanitizer::InputSanitizer sanitizer("09 af AF :");
322  return sanitizer.IsValid(address);
323 }
324 
325 
326 Resolver::Resolver(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  , max_ttl_(kDefaultMaxTtl) {
336 }
337 
338 
342 Host Resolver::Resolve(const string &name) {
343  vector<string> names;
344  names.push_back(name);
345  vector<Host> hosts;
346  ResolveMany(names, &hosts);
347  return hosts[0];
348 }
349 
350 
355 void Resolver::ResolveMany(const vector<string> &names, vector<Host> *hosts) {
356  const unsigned num = names.size();
357  if (num == 0)
358  return;
359 
360  vector<vector<string> > ipv4_addresses(num);
361  vector<vector<string> > ipv6_addresses(num);
362  vector<Failures> failures(num);
363  vector<unsigned> ttls(num);
364  vector<string> fqdns(num);
365  vector<bool> skip(num);
366 
367  // Deal with special names: empty, IPv4, IPv6
368  for (unsigned i = 0; i < num; ++i) {
369  if (names[i].empty()) {
370  LogCvmfs(kLogDns, kLogDebug, "empty hostname");
371  Host invalid_host;
372  invalid_host.name_ = "";
373  invalid_host.status_ = kFailInvalidHost;
374  hosts->push_back(invalid_host);
375  skip[i] = true;
376  } else if (IsIpv4Address(names[i])) {
377  LogCvmfs(kLogDns, kLogDebug, "IPv4 address %s", names[i].c_str());
378  Host ipv4_host;
379  ipv4_host.name_ = names[i];
380  ipv4_host.status_ = kFailOk;
381  ipv4_host.ipv4_addresses_.insert(names[i]);
382  ipv4_host.deadline_ = time(NULL) + max_ttl_;
383  hosts->push_back(ipv4_host);
384  skip[i] = true;
385  } else if ((names[i].length() >= 3) && (names[i][0] == '[')
386  && (names[i][names[i].length() - 1] == ']')) {
387  LogCvmfs(kLogDns, kLogDebug, "IPv6 address %s", names[i].c_str());
388  Host ipv6_host;
389  ipv6_host.name_ = names[i];
390  ipv6_host.status_ = kFailOk;
391  ipv6_host.ipv6_addresses_.insert(names[i]);
392  ipv6_host.deadline_ = time(NULL) + max_ttl_;
393  hosts->push_back(ipv6_host);
394  skip[i] = true;
395  } else {
396  hosts->push_back(Host());
397  skip[i] = false;
398  }
399  }
400 
401  DoResolve(names, skip, &ipv4_addresses, &ipv6_addresses, &failures, &ttls,
402  &fqdns);
403 
404  // Construct host objects
405  for (unsigned i = 0; i < num; ++i) {
406  if (skip[i])
407  continue;
408 
409  Host host;
410  host.name_ = fqdns[i];
411  host.status_ = failures[i];
412 
413  unsigned effective_ttl = ttls[i];
414  if (effective_ttl < min_ttl_) {
415  effective_ttl = min_ttl_;
416  } else if (effective_ttl > max_ttl_) {
417  effective_ttl = max_ttl_;
418  }
419  host.deadline_ = time(NULL) + effective_ttl;
420 
421  if (host.status_ != kFailOk) {
422  LogCvmfs(kLogDns, kLogDebug, "failed to resolve %s - %d (%s), ttl %u",
423  names[i].c_str(), host.status_, Code2Ascii(host.status_),
424  effective_ttl);
425  (*hosts)[i] = host;
426  continue;
427  }
428 
429  // Verify addresses and make them readily available for curl
430  for (unsigned j = 0; j < ipv4_addresses[i].size(); ++j) {
431  if (!IsIpv4Address(ipv4_addresses[i][j])) {
433  "host name %s resolves to invalid IPv4 address %s",
434  names[i].c_str(), ipv4_addresses[i][j].c_str());
435  continue;
436  }
437  if (names[i] == host.name_) {
438  LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s", names[i].c_str(),
439  ipv4_addresses[i][j].c_str());
440  } else {
441  LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s -> %s",
442  names[i].c_str(), host.name_.c_str(),
443  ipv4_addresses[i][j].c_str());
444  }
445  host.ipv4_addresses_.insert(ipv4_addresses[i][j]);
446  }
447 
448  for (unsigned j = 0; j < ipv6_addresses[i].size(); ++j) {
449  if (!IsIpv6Address(ipv6_addresses[i][j])) {
451  "host name %s resolves to invalid IPv6 address %s",
452  names[i].c_str(), ipv6_addresses[i][j].c_str());
453  continue;
454  }
455  // For URLs we need brackets around IPv6 addresses
456  if (names[i] == host.name_) {
457  LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s", names[i].c_str(),
458  ipv6_addresses[i][j].c_str());
459  } else {
460  LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s -> %s",
461  names[i].c_str(), host.name_.c_str(),
462  ipv6_addresses[i][j].c_str());
463  }
464  host.ipv6_addresses_.insert("[" + ipv6_addresses[i][j] + "]");
465  }
466 
467  if (host.ipv4_addresses_.empty() && host.ipv6_addresses_.empty()) {
468  LogCvmfs(kLogDns, kLogDebug, "no addresses returned for %s",
469  names[i].c_str());
470  host.status_ = kFailNoAddress;
471  }
472 
473  // Remove surplus IP addresses
474  if (throttle_ > 0) {
475  while (host.ipv4_addresses_.size() > throttle_) {
476  const unsigned random = prng_.Next(host.ipv4_addresses_.size());
477  set<string>::iterator rnd_itr = host.ipv4_addresses_.begin();
478  std::advance(rnd_itr, random);
479  host.ipv4_addresses_.erase(rnd_itr);
480  }
481  while (host.ipv6_addresses_.size() > throttle_) {
482  const unsigned random = prng_.Next(host.ipv6_addresses_.size());
483  set<string>::iterator rnd_itr = host.ipv6_addresses_.begin();
484  std::advance(rnd_itr, random);
485  host.ipv6_addresses_.erase(rnd_itr);
486  }
487  }
488 
489  (*hosts)[i] = host;
490  }
491 }
492 
493 
494 //------------------------------------------------------------------------------
495 
496 
497 namespace {
498 
500  kRrA = 0,
502 };
503 
510 struct QueryInfo {
511  QueryInfo(vector<string> *a, const string &n, const ResourceRecord r)
512  : addresses(a)
513  , complete(false)
514  , fqdn(n)
515  , name(n)
516  , record(r)
517  , status(kFailOther)
518  , ttl(0) { }
519 
520  vector<string> *addresses;
521  bool complete;
522  string fqdn;
523  string name;
526  unsigned ttl;
527 };
528 
529 } // namespace
530 
531 
532 static Failures CaresExtractIpv4(const unsigned char *abuf, int alen,
533  vector<string> *addresses, unsigned *ttl,
534  string *fqdn);
535 static Failures CaresExtractIpv6(const unsigned char *abuf, int alen,
536  vector<string> *addresses, unsigned *ttl,
537  string *fqdn);
538 
543 static void CallbackCares(
544  void *arg, int status, int timeouts_ms, unsigned char *abuf, int alen) {
545  QueryInfo *info = reinterpret_cast<QueryInfo *>(arg);
546 
547  info->complete = true;
548  switch (status) {
549  case ARES_SUCCESS:
550  Failures retval;
551  switch (info->record) {
552  case kRrA:
553  retval = CaresExtractIpv4(abuf, alen, info->addresses, &info->ttl,
554  &info->fqdn);
555  break;
556  case kRrAaaa:
557  retval = CaresExtractIpv6(abuf, alen, info->addresses, &info->ttl,
558  &info->fqdn);
559  break;
560  default:
561  // Never here.
562  PANIC(NULL);
563  }
564  info->status = retval;
565  break;
566  case ARES_ENODATA:
567  info->status = kFailUnknownHost;
568  break;
569  case ARES_EFORMERR:
570  info->status = kFailMalformed;
571  break;
572  case ARES_ENOTFOUND:
573  info->status = kFailUnknownHost;
574  break;
575  case ARES_ETIMEOUT:
576  info->status = kFailTimeout;
577  break;
578  case ARES_ECONNREFUSED:
579  info->status = kFailInvalidResolvers;
580  break;
581  default:
582  info->status = kFailOther;
583  }
584 }
585 
586 
591 static Failures CaresExtractIpv4(const unsigned char *abuf,
592  int alen,
593  vector<string> *addresses,
594  unsigned *ttl,
595  string *fqdn) {
596  struct hostent *host_entry = NULL;
597  struct ares_addrttl records[CaresResolver::kMaxAddresses];
598  int naddrttls = CaresResolver::kMaxAddresses;
599  const int retval =
600  ares_parse_a_reply(abuf, alen, &host_entry, records, &naddrttls);
601 
602  switch (retval) {
603  case ARES_SUCCESS:
604  if (host_entry == NULL)
605  return kFailMalformed;
606  if (host_entry->h_name == NULL) {
607  ares_free_hostent(host_entry);
608  return kFailMalformed;
609  }
610  *fqdn = string(host_entry->h_name);
611  ares_free_hostent(host_entry);
612 
613  if (naddrttls <= 0)
614  return kFailMalformed;
615  *ttl = unsigned(-1);
616  for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) {
617  if (records[i].ttl < 0)
618  continue;
619  *ttl = std::min(unsigned(records[i].ttl), *ttl);
620 
621  char addrstr[INET_ADDRSTRLEN];
622  const void *retval_p = inet_ntop(AF_INET, &(records[i].ipaddr), addrstr,
623  INET_ADDRSTRLEN);
624  if (!retval_p)
625  continue;
626  addresses->push_back(addrstr);
627  }
628  if (addresses->empty())
629  return kFailMalformed;
630  return kFailOk;
631  case ARES_EBADRESP:
632  // Fall through
633  case ARES_ENODATA:
634  return kFailMalformed;
635  default:
636  return kFailOther;
637  }
638 }
639 
640 
641 static Failures CaresExtractIpv6(const unsigned char *abuf,
642  int alen,
643  vector<string> *addresses,
644  unsigned *ttl,
645  string *fqdn) {
646  struct hostent *host_entry = NULL;
647  struct ares_addr6ttl records[CaresResolver::kMaxAddresses];
648  int naddrttls = CaresResolver::kMaxAddresses;
649  const int retval =
650  ares_parse_aaaa_reply(abuf, alen, &host_entry, records, &naddrttls);
651 
652  switch (retval) {
653  case ARES_SUCCESS:
654  if (host_entry == NULL)
655  return kFailMalformed;
656  if (host_entry->h_name == NULL) {
657  ares_free_hostent(host_entry);
658  return kFailMalformed;
659  }
660  *fqdn = string(host_entry->h_name);
661  ares_free_hostent(host_entry);
662 
663  if (naddrttls <= 0)
664  return kFailMalformed;
665  *ttl = unsigned(-1);
666  for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) {
667  if (records[i].ttl < 0)
668  continue;
669  *ttl = std::min(unsigned(records[i].ttl), *ttl);
670 
671  char addrstr[INET6_ADDRSTRLEN];
672  const void *retval_p = inet_ntop(AF_INET6, &(records[i].ip6addr),
673  addrstr, INET6_ADDRSTRLEN);
674  if (!retval_p)
675  continue;
676  addresses->push_back(addrstr);
677  }
678  if (addresses->empty())
679  return kFailMalformed;
680  return kFailOk;
681  case ARES_EBADRESP:
682  // Fall through
683  case ARES_ENODATA:
684  return kFailMalformed;
685  default:
686  return kFailOther;
687  }
688 }
689 
690 
691 CaresResolver::CaresResolver(const bool ipv4_only,
692  const unsigned retries,
693  const unsigned timeout_ms)
694  : Resolver(ipv4_only, retries, timeout_ms)
695  , channel_(NULL)
696  , lookup_options_(strdup("b")) { }
697 
698 
700  if (channel_) {
701  ares_destroy(*channel_);
702  free(channel_);
703  }
704  free(lookup_options_);
705 }
706 
707 
711 CaresResolver *CaresResolver::Create(const bool ipv4_only,
712  const unsigned retries,
713  const unsigned timeout_ms) {
714  int retval;
715  if (getenv("HOSTALIASES") == NULL) {
716  retval = setenv("HOSTALIASES", "/etc/hosts", 1);
717  assert(retval == 0);
718  }
719 
720  CaresResolver *resolver = new CaresResolver(ipv4_only, retries, timeout_ms);
721  resolver->channel_ = reinterpret_cast<ares_channel *>(
722  smalloc(sizeof(ares_channel)));
723  memset(resolver->channel_, 0, sizeof(ares_channel));
724 
725  struct ares_addr_node *addresses;
726  struct ares_addr_node *iter;
727  struct ares_options options;
728  int optmask;
729  memset(&options, 0, sizeof(options));
730  options.timeout = timeout_ms;
731  options.tries = 1 + retries;
732  options.lookups = resolver->lookup_options_;
733  optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_LOOKUPS;
734  retval = ares_init_options(resolver->channel_, &options, optmask);
735  if (retval != ARES_SUCCESS)
736  goto create_fail;
737 
738  // Save search domains
739  retval = ares_save_options(*resolver->channel_, &options, &optmask);
740  if (retval != ARES_SUCCESS)
741  goto create_fail;
742  for (int i = 0; i < options.ndomains; ++i) {
743  resolver->domains_.push_back(options.domains[i]);
744  }
745  ares_destroy_options(&options);
746  resolver->system_domains_ = resolver->domains_;
747 
748  // Save the system default resolvers
749  addresses = NULL;
750  retval = ares_get_servers(*resolver->channel_, &addresses);
751  if (retval != ARES_SUCCESS)
752  goto create_fail;
753  iter = addresses;
754  while (iter) {
755  switch (iter->family) {
756  case AF_INET: {
757  char addrstr[INET_ADDRSTRLEN];
758  const void *retval_p = inet_ntop(AF_INET, &(iter->addr), addrstr,
759  INET_ADDRSTRLEN);
760  if (!retval_p) {
762  "invalid system name resolver");
763  } else {
764  resolver->resolvers_.push_back(string(addrstr) + ":53");
765  }
766  break;
767  }
768  case AF_INET6: {
769  char addrstr[INET6_ADDRSTRLEN];
770  const void *retval_p = inet_ntop(AF_INET6, &(iter->addr), addrstr,
771  INET6_ADDRSTRLEN);
772  if (!retval_p) {
774  "invalid system name resolver");
775  } else {
776  resolver->resolvers_.push_back("[" + string(addrstr) + "]:53");
777  }
778  break;
779  }
780  default:
781  // Never here.
782  PANIC(NULL);
783  }
784  iter = iter->next;
785  }
786  ares_free_data(addresses);
787  resolver->system_resolvers_ = resolver->resolvers_;
788 
789  return resolver;
790 
791 create_fail:
793  "failed to initialize c-ares resolver (%d - %s)", retval,
794  ares_strerror(retval));
795  free(resolver->channel_);
796  resolver->channel_ = NULL;
797  delete resolver;
798  return NULL;
799 }
800 
801 
806 void CaresResolver::DoResolve(const vector<string> &names,
807  const vector<bool> &skip,
808  vector<vector<string> > *ipv4_addresses,
809  vector<vector<string> > *ipv6_addresses,
810  vector<Failures> *failures,
811  vector<unsigned> *ttls,
812  vector<string> *fqdns) {
813  const unsigned num = names.size();
814  if (num == 0)
815  return;
816 
817  vector<QueryInfo *> infos_ipv4(num, NULL);
818  vector<QueryInfo *> infos_ipv6(num, NULL);
819 
820  for (unsigned i = 0; i < num; ++i) {
821  if (skip[i])
822  continue;
823 
824  if (!ipv4_only()) {
825  infos_ipv6[i] = new QueryInfo(&(*ipv6_addresses)[i], names[i], kRrAaaa);
826  ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_aaaa,
827  CallbackCares, infos_ipv6[i]);
828  }
829  infos_ipv4[i] = new QueryInfo(&(*ipv4_addresses)[i], names[i], kRrA);
830  ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_a, CallbackCares,
831  infos_ipv4[i]);
832  }
833 
834  bool all_complete;
835  do {
836  all_complete = true;
837  WaitOnCares();
838  for (unsigned i = 0; i < num; ++i) {
839  if ((infos_ipv4[i] && !infos_ipv4[i]->complete)
840  || (infos_ipv6[i] && !infos_ipv6[i]->complete)) {
841  all_complete = false;
842  break;
843  }
844  }
845  } while (!all_complete);
846 
847  // Silently ignore errors with IPv4/6 if there are at least some usable IP
848  // addresses.
849  for (unsigned i = 0; i < num; ++i) {
850  if (skip[i])
851  continue;
852 
853  Failures status = kFailOther;
854  (*ttls)[i] = unsigned(-1);
855  (*fqdns)[i] = "";
856  if (infos_ipv6[i]) {
857  status = infos_ipv6[i]->status;
858  if (status == kFailOk) {
859  (*ttls)[i] = std::min(infos_ipv6[i]->ttl, (*ttls)[i]);
860  (*fqdns)[i] = infos_ipv6[i]->fqdn;
861  }
862  }
863  if (infos_ipv4[i]) {
864  (*ttls)[i] = std::min(infos_ipv4[i]->ttl, (*ttls)[i]);
865  if ((*fqdns)[i] == "")
866  (*fqdns)[i] = infos_ipv4[i]->fqdn;
867  if (status != kFailOk)
868  status = infos_ipv4[i]->status;
869  }
870  (*failures)[i] = status;
871  }
872 
873  for (unsigned i = 0; i < num; ++i) {
874  delete infos_ipv4[i];
875  delete infos_ipv6[i];
876  }
877 }
878 
879 
880 bool CaresResolver::SetResolvers(const vector<string> &resolvers) {
881  const string address_list = JoinStrings(resolvers, ",");
882  const int retval = ares_set_servers_csv(*channel_, address_list.c_str());
883  if (retval != ARES_SUCCESS)
884  return false;
885 
887  return true;
888 }
889 
890 
896 bool CaresResolver::SetSearchDomains(const vector<string> &domains) {
897  // From ares_private.h
898  struct {
899  int flags;
900  int timeout;
901  int tries;
902  int ndots;
903  int rotate;
904  int udp_port;
905  int tcp_port;
906  int socket_send_buffer_size;
907  int socket_receive_buffer_size;
908  char **domains;
909  int ndomains;
910  // More fields come in the original data structure
911  } ares_channelhead;
912 
913  memcpy(&ares_channelhead, *channel_, sizeof(ares_channelhead));
914  if (ares_channelhead.domains) {
915  for (int i = 0; i < ares_channelhead.ndomains; ++i) {
916  free(ares_channelhead.domains[i]);
917  }
918  free(ares_channelhead.domains);
919  ares_channelhead.domains = NULL;
920  }
921 
922  ares_channelhead.ndomains = static_cast<int>(domains.size());
923  if (ares_channelhead.ndomains > 0) {
924  ares_channelhead.domains = reinterpret_cast<char **>(
925  smalloc(ares_channelhead.ndomains * sizeof(char *)));
926  for (int i = 0; i < ares_channelhead.ndomains; ++i) {
927  ares_channelhead.domains[i] = strdup(domains[i].c_str());
928  }
929  }
930 
931  memcpy(*channel_, &ares_channelhead, sizeof(ares_channelhead));
932 
933  domains_ = domains;
934  return true;
935 }
936 
937 
939  const int retval = SetResolvers(system_resolvers_);
940  assert(retval == true);
941 }
942 
943 
945  const int retval = SetSearchDomains(system_domains_);
946  assert(retval == true);
947 }
948 
949 
955  // Adapted from libcurl
956  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
957  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
958  const int bitmask = ares_getsock(*channel_, socks, ARES_GETSOCK_MAXNUM);
959  unsigned num = 0;
960  for (unsigned i = 0; i < ARES_GETSOCK_MAXNUM; ++i) {
961  pfd[i].events = 0;
962  pfd[i].revents = 0;
963  if (ARES_GETSOCK_READABLE(bitmask, i)) {
964  pfd[i].fd = socks[i];
965  pfd[i].events |= POLLRDNORM | POLLIN;
966  }
967  if (ARES_GETSOCK_WRITABLE(bitmask, i)) {
968  pfd[i].fd = socks[i];
969  pfd[i].events |= POLLWRNORM | POLLOUT;
970  }
971  if (pfd[i].events != 0)
972  num++;
973  else
974  break;
975  }
976 
977  int nfds = 0;
978  if (num > 0) {
979  do {
980  nfds = poll(pfd, num, timeout_ms());
981  if (nfds == -1) {
982  // poll must not fail for other reasons
983  if ((errno != EAGAIN) && (errno != EINTR))
984  PANIC(NULL);
985  }
986  } while (nfds == -1);
987  }
988 
989  if (nfds == 0) {
990  // Call ares_process() unconditionally here, even if we simply timed out
991  // above, as otherwise the ares name resolve won't timeout.
992  ares_process_fd(*channel_, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
993  } else {
994  // Go through the descriptors and ask for executing the callbacks.
995  for (unsigned i = 0; i < num; ++i) {
996  ares_process_fd(
997  *channel_,
998  pfd[i].revents & (POLLRDNORM | POLLIN) ? pfd[i].fd : ARES_SOCKET_BAD,
999  pfd[i].revents & (POLLWRNORM | POLLOUT) ? pfd[i].fd
1000  : ARES_SOCKET_BAD);
1001  }
1002  }
1003 }
1004 
1005 
1006 //------------------------------------------------------------------------------
1007 
1008 
1014 HostfileResolver *HostfileResolver::Create(const string &path, bool ipv4_only) {
1015  HostfileResolver *resolver = new HostfileResolver(ipv4_only);
1016 
1017  string hosts_file = path;
1018  if (hosts_file == "") {
1019  char *hosts_env = getenv("HOST_ALIASES");
1020  if (hosts_env != NULL) {
1021  hosts_file = string(hosts_env);
1022  } else {
1023  hosts_file = "/etc/hosts";
1024  }
1025  }
1026  resolver->fhosts_ = fopen(hosts_file.c_str(), "r");
1027  if (!resolver->fhosts_) {
1028  LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn, "failed to read host file %s",
1029  hosts_file.c_str());
1030  delete resolver;
1031  return NULL;
1032  }
1033  return resolver;
1034 }
1035 
1036 
1042 static bool SortNameLength(const string &a, const string &b) {
1043  const unsigned len_a = a.length();
1044  const unsigned len_b = b.length();
1045  if (len_a != len_b)
1046  return len_a > len_b;
1047  return a > b;
1048 }
1049 
1050 
1054 void HostfileResolver::DoResolve(const vector<string> &names,
1055  const vector<bool> &skip,
1056  vector<vector<std::string> > *ipv4_addresses,
1057  vector<vector<std::string> > *ipv6_addresses,
1058  vector<Failures> *failures,
1059  vector<unsigned> *ttls,
1060  vector<string> *fqdns) {
1061  const unsigned num = names.size();
1062  if (num == 0)
1063  return;
1064 
1065  ParseHostFile();
1066  for (unsigned i = 0; i < num; ++i) {
1067  if (skip[i])
1068  continue;
1069 
1070  vector<string> effective_names;
1071  if (!names[i].empty() && (names[i][names[i].length() - 1] == '.')) {
1072  effective_names.push_back(names[i].substr(0, names[i].length() - 1));
1073  } else {
1074  effective_names.push_back(names[i]);
1075  for (unsigned j = 0; j < domains().size(); ++j) {
1076  effective_names.push_back(names[i] + "." + domains()[j]);
1077  }
1078  }
1079 
1080  // Use the longest matching name as fqdn
1081  std::sort(effective_names.begin(), effective_names.end(), SortNameLength);
1082 
1083  (*failures)[i] = kFailUnknownHost;
1084  (*fqdns)[i] = names[i];
1085  for (unsigned j = 0; j < effective_names.size(); ++j) {
1086  const map<string, HostEntry>::iterator iter =
1087  host_map_.find(effective_names[j]);
1088  if (iter != host_map_.end()) {
1089  (*ipv4_addresses)[i].insert((*ipv4_addresses)[i].end(),
1090  iter->second.ipv4_addresses.begin(),
1091  iter->second.ipv4_addresses.end());
1092  (*ipv6_addresses)[i].insert((*ipv6_addresses)[i].end(),
1093  iter->second.ipv6_addresses.begin(),
1094  iter->second.ipv6_addresses.end());
1095  (*ttls)[i] = min_ttl_;
1096  (*fqdns)[i] = effective_names[j];
1097  (*failures)[i] = kFailOk;
1098  break;
1099  } // Host name found
1100  } // All possible names (search domains added)
1101  }
1102 }
1103 
1104 
1106  : Resolver(ipv4_only, 0, 0), fhosts_(NULL) { }
1107 
1108 
1110  if (fhosts_)
1111  fclose(fhosts_);
1112 }
1113 
1114 
1119  assert(fhosts_);
1120  rewind(fhosts_);
1121  host_map_.clear();
1122 
1123  string line;
1124  while (GetLineFile(fhosts_, &line)) {
1125  char address[kIpMaxLength + 1];
1126  char hostname[kHostnameMaxLength + 1];
1127  int bytes_read;
1128  size_t str_offset = 0;
1129 
1130  // strip comments
1131  const size_t hash_pos = line.find_first_of('#');
1132  if (hash_pos != string::npos)
1133  line = line.substr(0, hash_pos);
1134 
1135  // First token is an IP address
1136  int ip_start_pos = -1, ip_end_pos = -1, scan_result;
1137  scan_result = sscanf(line.c_str(), " %n%*s%n", &ip_start_pos, &ip_end_pos);
1138  if (scan_result == EOF)
1139  continue;
1140  assert(ip_start_pos != -1);
1141  assert(ip_end_pos != -1);
1142  if (ip_start_pos == ip_end_pos)
1143  continue;
1144  if (ip_end_pos - ip_start_pos > kIpMaxLength) {
1145  LogCvmfs(
1147  "Skipping line in hosts file due to invalid IP address format: %s",
1148  line.c_str());
1149  continue;
1150  }
1151 
1152  bytes_read = -1;
1153  scan_result = sscanf(line.c_str(), " %s%n", address, &bytes_read);
1154  assert(scan_result == 1);
1155  assert(bytes_read != -1);
1156  str_offset += bytes_read;
1157 
1158  // Next tokens are hostnames and aliases (we treat them as equal)
1159  while (str_offset < line.length()) {
1160  // check hostname length
1161  int hostname_start_pos = -1, hostname_end_pos = -1;
1162  scan_result = sscanf(line.c_str() + str_offset, " %n%*s%n",
1163  &hostname_start_pos, &hostname_end_pos);
1164  if (scan_result == EOF)
1165  break;
1166  assert(hostname_start_pos != -1);
1167  assert(hostname_end_pos != -1);
1168  if (hostname_start_pos == hostname_end_pos)
1169  break;
1170 
1171  if (hostname_end_pos - hostname_start_pos > kHostnameMaxLength) {
1172  LogCvmfs(
1174  "Skipping invalid (too long) hostname in hosts file on line: %s",
1175  line.c_str());
1176  str_offset += hostname_end_pos;
1177  continue;
1178  }
1179 
1180  bytes_read = -1;
1181  scan_result = sscanf(line.c_str() + str_offset, " %s%n", hostname,
1182  &bytes_read);
1183  assert(scan_result == 1);
1184  assert(bytes_read != -1);
1185  str_offset += bytes_read;
1186 
1187  if (hostname[strlen(hostname) - 1] == '.')
1188  hostname[strlen(hostname) - 1] = 0; // strip the last character
1189 
1190  const map<string, HostEntry>::iterator iter = host_map_.find(hostname);
1191  if (iter == host_map_.end()) {
1192  HostEntry entry;
1193  if (IsIpv4Address(address))
1194  entry.ipv4_addresses.push_back(address);
1195  else if (!ipv4_only())
1196  entry.ipv6_addresses.push_back(address);
1197  host_map_[hostname] = entry;
1198  } else {
1199  if (IsIpv4Address(address))
1200  iter->second.ipv4_addresses.push_back(address);
1201  else if (!ipv4_only())
1202  iter->second.ipv6_addresses.push_back(address);
1203  }
1204  } // Current line
1205  } // Hosts file
1206 }
1207 
1208 
1209 bool HostfileResolver::SetSearchDomains(const vector<string> &domains) {
1210  domains_ = domains;
1211  return true;
1212 }
1213 
1214 
1216  // TODO(jblomer)
1217  PANIC(NULL);
1218 }
1219 
1220 
1221 //------------------------------------------------------------------------------
1222 
1223 
1229  const unsigned retries,
1230  const unsigned timeout_ms) {
1231  CaresResolver *cares_resolver = CaresResolver::Create(ipv4_only, retries,
1232  timeout_ms);
1233  if (!cares_resolver)
1234  return NULL;
1235  HostfileResolver *hostfile_resolver = HostfileResolver::Create("", ipv4_only);
1236  if (!hostfile_resolver) {
1237  delete cares_resolver;
1238  return NULL;
1239  }
1240  const bool retval =
1241  hostfile_resolver->SetSearchDomains(cares_resolver->domains());
1242  assert(retval);
1243 
1244  NormalResolver *normal_resolver = new NormalResolver();
1245  normal_resolver->cares_resolver_ = cares_resolver;
1246  normal_resolver->hostfile_resolver_ = hostfile_resolver;
1247  normal_resolver->domains_ = cares_resolver->domains();
1248  normal_resolver->resolvers_ = cares_resolver->resolvers();
1249  normal_resolver->retries_ = cares_resolver->retries();
1250  normal_resolver->timeout_ms_ = cares_resolver->timeout_ms();
1251  return normal_resolver;
1252 }
1253 
1254 
1258 bool NormalResolver::SetResolvers(const vector<string> &resolvers) {
1259  return cares_resolver_->SetResolvers(resolvers);
1260 }
1261 
1262 
1266 bool NormalResolver::SetSearchDomains(const vector<string> &domains) {
1267  const vector<string> old_domains = hostfile_resolver_->domains();
1268  bool retval = hostfile_resolver_->SetSearchDomains(domains);
1269  if (!retval)
1270  return false;
1271  retval = cares_resolver_->SetSearchDomains(domains);
1272  if (!retval) {
1273  retval = hostfile_resolver_->SetSearchDomains(old_domains);
1274  assert(retval);
1275  return false;
1276  }
1277  return true;
1278 }
1279 
1280 
1283 }
1284 
1285 
1288  const bool retval =
1290  assert(retval);
1291 }
1292 
1293 
1298 void NormalResolver::DoResolve(const vector<string> &names,
1299  const vector<bool> &skip,
1300  vector<vector<string> > *ipv4_addresses,
1301  vector<vector<string> > *ipv6_addresses,
1302  vector<Failures> *failures,
1303  vector<unsigned> *ttls,
1304  vector<string> *fqdns) {
1305  const unsigned num = names.size();
1306  hostfile_resolver_->DoResolve(names, skip, ipv4_addresses, ipv6_addresses,
1307  failures, ttls, fqdns);
1308  vector<bool> skip_cares = skip;
1309  for (unsigned i = 0; i < num; ++i) {
1310  if ((*failures)[i] == kFailOk)
1311  skip_cares[i] = true;
1312  }
1313  cares_resolver_->DoResolve(names, skip_cares, ipv4_addresses, ipv6_addresses,
1314  failures, ttls, fqdns);
1315 }
1316 
1317 
1319  : Resolver(false, 0, 0), cares_resolver_(NULL), hostfile_resolver_(NULL) { }
1320 
1321 
1323  delete cares_resolver_;
1324  delete hostfile_resolver_;
1325 }
1326 
1327 } // namespace dns
Failures
Definition: dns.h:28
static const int kHostnameMaxLength
Definition: dns.h:364
int64_t id_
Definition: dns.h:140
virtual bool SetSearchDomains(const std::vector< std::string > &domains)
Definition: dns.cc:896
const char * Code2Ascii(const Failures error)
Definition: dns.h:53
int64_t atomic_int64
Definition: atomic.h:18
void ResolveMany(const std::vector< std::string > &names, std::vector< Host > *hosts)
Definition: dns.cc:355
Resolver returned a negative reply.
Definition: dns.h:33
HostfileResolver * hostfile_resolver_
Definition: dns.h:410
static NormalResolver * Create(const bool ipv4_only, const unsigned retries, const unsigned timeout_ms)
Definition: dns.cc:1228
Definition: dns.h:359
#define PANIC(...)
Definition: exception.h:29
void CopyFrom(const Host &other)
Definition: dns.cc:209
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:356
CaresResolver * cares_resolver_
Definition: dns.h:409
unsigned retries_
Definition: dns.h:250
bool IsIpv4Address(const std::string &address)
Definition: dns.cc:295
static void PinpointHostSubstr(const std::string &url, unsigned *pos_begin, unsigned *pos_end)
Definition: dns.cc:53
virtual bool SetSearchDomains(const std::vector< std::string > &domains)
Definition: dns.cc:1209
std::vector< std::string > ipv4_addresses
Definition: dns.h:360
void InitLocaltime()
Definition: prng.h:34
FILE * fhosts_
Definition: dns.h:377
Resolver returned a positive reply but without IPs.
Definition: dns.h:35
bool IsIpv6Address(const std::string &address)
Definition: dns.cc:319
std::set< std::string > ipv6_addresses_
Definition: dns.h:152
QueryInfo(vector< string > *a, const string &n, const ResourceRecord r)
Definition: dns.cc:511
bool ipv4_only() const
Definition: dns.h:201
static void CallbackCares(void *arg, int status, int timeouts_ms, unsigned char *abuf, int alen)
Definition: dns.cc:543
virtual void SetSystemResolvers()
Definition: dns.cc:1281
CaresResolver(const bool ipv4_only, const unsigned retries, const unsigned timeout_ms)
Definition: dns.cc:691
assert((mem||(size==0))&&"Out Of Memory")
Resolver(const bool ipv4_only, const unsigned retries, const unsigned timeout_ms)
Definition: dns.cc:326
static const int kIpMaxLength
Definition: dns.h:363
Failures status_
Definition: dns.h:162
Host()
Definition: dns.cc:235
static HostfileResolver * Create(const std::string &path, bool ipv4_only)
Definition: dns.cc:1014
string StripIp(const string &decorated_ip)
Definition: dns.cc:168
bool IsValid() const
Definition: dns.cc:279
virtual ~NormalResolver()
Definition: dns.cc:1322
static Failures CaresExtractIpv6(const unsigned char *abuf, int alen, vector< string > *addresses, unsigned *ttl, string *fqdn)
Definition: dns.cc:641
Host & operator=(const Host &other)
Definition: dns.cc:244
unsigned throttle_
Definition: dns.h:262
std::vector< std::string > system_resolvers_
Definition: dns.h:322
bool IsValid(const std::string &input) const
Definition: sanitizer.cc:112
virtual void SetSystemSearchDomains()
Definition: dns.cc:1286
Prng prng_
Definition: dns.h:277
std::string name_
Definition: dns.h:157
bool GetLineFile(FILE *f, std::string *line)
Definition: string.cc:422
virtual void SetSystemResolvers()
Definition: dns.cc:938
std::vector< std::string > domains_
Definition: dns.h:234
const std::vector< std::string > & resolvers() const
Definition: dns.h:202
std::string AddDefaultScheme(const std::string &proxy)
Definition: dns.cc:182
vector< string > SplitString(const string &str, char delim)
Definition: string.cc:306
std::map< std::string, HostEntry > host_map_
Definition: dns.h:370
unsigned min_ttl_
Definition: dns.h:267
HostfileResolver(const bool ipv4_only)
Definition: dns.cc:1105
Definition: dns.h:90
virtual bool SetSearchDomains(const std::vector< std::string > &domains)
Definition: dns.cc:1266
virtual void DoResolve(const std::vector< std::string > &names, const std::vector< bool > &skip, std::vector< std::vector< std::string > > *ipv4_addresses, std::vector< std::vector< std::string > > *ipv6_addresses, std::vector< Failures > *failures, std::vector< unsigned > *ttls, std::vector< std::string > *fqdns)
Definition: dns.cc:1054
bool IsEquivalent(const Host &other) const
Definition: dns.cc:257
virtual bool SetResolvers(const std::vector< std::string > &resolvers)
Definition: dns.cc:880
bool IsExpired() const
Definition: dns.cc:267
ares_channel * channel_
Definition: dns.h:320
static Failures CaresExtractIpv4(const unsigned char *abuf, int alen, vector< string > *addresses, unsigned *ttl, string *fqdn)
Definition: dns.cc:591
std::string ExtractPort(const std::string &url)
Definition: dns.cc:123
virtual void DoResolve(const std::vector< std::string > &names, const std::vector< bool > &skip, std::vector< std::vector< std::string > > *ipv4_addresses, std::vector< std::vector< std::string > > *ipv6_addresses, std::vector< Failures > *failures, std::vector< unsigned > *ttls, std::vector< std::string > *fqdns)
Definition: dns.cc:806
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:279
std::string ExtractHost(const std::string &url)
Definition: dns.cc:108
time_t deadline_
Definition: dns.h:135
virtual ~HostfileResolver()
Definition: dns.cc:1109
char * lookup_options_
Definition: dns.h:321
IpPreference
Definition: dns.h:46
static bool SortNameLength(const string &a, const string &b)
Definition: dns.cc:1042
unsigned retries() const
Definition: dns.h:203
void ParseHostFile()
Definition: dns.cc:1118
void WaitOnCares()
Definition: dns.cc:954
Host Resolve(const std::string &name)
Definition: dns.cc:342
uint64_t String2Uint64(const string &value)
Definition: string.cc:240
std::vector< std::string > resolvers_
Definition: dns.h:244
static CaresResolver * Create(const bool ipv4_only, const unsigned retries, const unsigned timeout_ms)
Definition: dns.cc:711
virtual void DoResolve(const std::vector< std::string > &names, const std::vector< bool > &skip, std::vector< std::vector< std::string > > *ipv4_addresses, std::vector< std::vector< std::string > > *ipv6_addresses, std::vector< Failures > *failures, std::vector< unsigned > *ttls, std::vector< std::string > *fqdns)=0
unsigned timeout_ms_
Definition: dns.h:255
std::vector< std::string > system_domains_
Definition: dns.h:323
virtual void DoResolve(const std::vector< std::string > &names, const std::vector< bool > &skip, std::vector< std::vector< std::string > > *ipv4_addresses, std::vector< std::vector< std::string > > *ipv6_addresses, std::vector< Failures > *failures, std::vector< unsigned > *ttls, std::vector< std::string > *fqdns)
Definition: dns.cc:1298
virtual ~CaresResolver()
Definition: dns.cc:699
virtual void SetSystemSearchDomains()
Definition: dns.cc:944
unsigned max_ttl_
Definition: dns.h:272
std::set< std::string > ipv4_addresses_
Definition: dns.h:146
virtual void SetSystemSearchDomains()
Definition: dns.cc:1215
const std::vector< std::string > & domains() const
Definition: dns.h:200
static const unsigned kMaxAddresses
Definition: dns.h:294
std::vector< std::string > ipv6_addresses
Definition: dns.h:361
virtual bool SetResolvers(const std::vector< std::string > &resolvers)
Definition: dns.cc:1258
string RewriteUrl(const string &url, const string &ip)
Definition: dns.cc:152
uint32_t Next(const uint64_t boundary)
Definition: prng.h:44
unsigned timeout_ms() const
Definition: dns.h:204
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545