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  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  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  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  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  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  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  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  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  int retval = ares_parse_a_reply(abuf, alen, &host_entry, records, &naddrttls);
600 
601  switch (retval) {
602  case ARES_SUCCESS:
603  if (host_entry == NULL)
604  return kFailMalformed;
605  if (host_entry->h_name == NULL) {
606  ares_free_hostent(host_entry);
607  return kFailMalformed;
608  }
609  *fqdn = string(host_entry->h_name);
610  ares_free_hostent(host_entry);
611 
612  if (naddrttls <= 0)
613  return kFailMalformed;
614  *ttl = unsigned(-1);
615  for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) {
616  if (records[i].ttl < 0)
617  continue;
618  *ttl = std::min(unsigned(records[i].ttl), *ttl);
619 
620  char addrstr[INET_ADDRSTRLEN];
621  const void *retval_p = inet_ntop(AF_INET, &(records[i].ipaddr), addrstr,
622  INET_ADDRSTRLEN);
623  if (!retval_p)
624  continue;
625  addresses->push_back(addrstr);
626  }
627  if (addresses->empty())
628  return kFailMalformed;
629  return kFailOk;
630  case ARES_EBADRESP:
631  // Fall through
632  case ARES_ENODATA:
633  return kFailMalformed;
634  default:
635  return kFailOther;
636  }
637 }
638 
639 
640 static Failures CaresExtractIpv6(const unsigned char *abuf,
641  int alen,
642  vector<string> *addresses,
643  unsigned *ttl,
644  string *fqdn) {
645  struct hostent *host_entry = NULL;
646  struct ares_addr6ttl records[CaresResolver::kMaxAddresses];
647  int naddrttls = CaresResolver::kMaxAddresses;
648  int retval = ares_parse_aaaa_reply(abuf, alen, &host_entry, records,
649  &naddrttls);
650 
651  switch (retval) {
652  case ARES_SUCCESS:
653  if (host_entry == NULL)
654  return kFailMalformed;
655  if (host_entry->h_name == NULL) {
656  ares_free_hostent(host_entry);
657  return kFailMalformed;
658  }
659  *fqdn = string(host_entry->h_name);
660  ares_free_hostent(host_entry);
661 
662  if (naddrttls <= 0)
663  return kFailMalformed;
664  *ttl = unsigned(-1);
665  for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) {
666  if (records[i].ttl < 0)
667  continue;
668  *ttl = std::min(unsigned(records[i].ttl), *ttl);
669 
670  char addrstr[INET6_ADDRSTRLEN];
671  const void *retval_p = inet_ntop(AF_INET6, &(records[i].ip6addr),
672  addrstr, INET6_ADDRSTRLEN);
673  if (!retval_p)
674  continue;
675  addresses->push_back(addrstr);
676  }
677  if (addresses->empty())
678  return kFailMalformed;
679  return kFailOk;
680  case ARES_EBADRESP:
681  // Fall through
682  case ARES_ENODATA:
683  return kFailMalformed;
684  default:
685  return kFailOther;
686  }
687 }
688 
689 
690 CaresResolver::CaresResolver(const bool ipv4_only,
691  const unsigned retries,
692  const unsigned timeout_ms)
693  : Resolver(ipv4_only, retries, timeout_ms)
694  , channel_(NULL)
695  , lookup_options_(strdup("b")) { }
696 
697 
699  if (channel_) {
700  ares_destroy(*channel_);
701  free(channel_);
702  }
703  free(lookup_options_);
704 }
705 
706 
710 CaresResolver *CaresResolver::Create(const bool ipv4_only,
711  const unsigned retries,
712  const unsigned timeout_ms) {
713  int retval;
714  if (getenv("HOSTALIASES") == NULL) {
715  retval = setenv("HOSTALIASES", "/etc/hosts", 1);
716  assert(retval == 0);
717  }
718 
719  CaresResolver *resolver = new CaresResolver(ipv4_only, retries, timeout_ms);
720  resolver->channel_ = reinterpret_cast<ares_channel *>(
721  smalloc(sizeof(ares_channel)));
722  memset(resolver->channel_, 0, sizeof(ares_channel));
723 
724  struct ares_addr_node *addresses;
725  struct ares_addr_node *iter;
726  struct ares_options options;
727  int optmask;
728  memset(&options, 0, sizeof(options));
729  options.timeout = timeout_ms;
730  options.tries = 1 + retries;
731  options.lookups = resolver->lookup_options_;
732  optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_LOOKUPS;
733  retval = ares_init_options(resolver->channel_, &options, optmask);
734  if (retval != ARES_SUCCESS)
735  goto create_fail;
736 
737  // Save search domains
738  retval = ares_save_options(*resolver->channel_, &options, &optmask);
739  if (retval != ARES_SUCCESS)
740  goto create_fail;
741  for (int i = 0; i < options.ndomains; ++i) {
742  resolver->domains_.push_back(options.domains[i]);
743  }
744  ares_destroy_options(&options);
745  resolver->system_domains_ = resolver->domains_;
746 
747  // Save the system default resolvers
748  addresses = NULL;
749  retval = ares_get_servers(*resolver->channel_, &addresses);
750  if (retval != ARES_SUCCESS)
751  goto create_fail;
752  iter = addresses;
753  while (iter) {
754  switch (iter->family) {
755  case AF_INET: {
756  char addrstr[INET_ADDRSTRLEN];
757  const void *retval_p = inet_ntop(AF_INET, &(iter->addr), addrstr,
758  INET_ADDRSTRLEN);
759  if (!retval_p) {
761  "invalid system name resolver");
762  } else {
763  resolver->resolvers_.push_back(string(addrstr) + ":53");
764  }
765  break;
766  }
767  case AF_INET6: {
768  char addrstr[INET6_ADDRSTRLEN];
769  const void *retval_p = inet_ntop(AF_INET6, &(iter->addr), addrstr,
770  INET6_ADDRSTRLEN);
771  if (!retval_p) {
773  "invalid system name resolver");
774  } else {
775  resolver->resolvers_.push_back("[" + string(addrstr) + "]:53");
776  }
777  break;
778  }
779  default:
780  // Never here.
781  PANIC(NULL);
782  }
783  iter = iter->next;
784  }
785  ares_free_data(addresses);
786  resolver->system_resolvers_ = resolver->resolvers_;
787 
788  return resolver;
789 
790 create_fail:
792  "failed to initialize c-ares resolver (%d - %s)", retval,
793  ares_strerror(retval));
794  free(resolver->channel_);
795  resolver->channel_ = NULL;
796  delete resolver;
797  return NULL;
798 }
799 
800 
805 void CaresResolver::DoResolve(const vector<string> &names,
806  const vector<bool> &skip,
807  vector<vector<string> > *ipv4_addresses,
808  vector<vector<string> > *ipv6_addresses,
809  vector<Failures> *failures,
810  vector<unsigned> *ttls,
811  vector<string> *fqdns) {
812  unsigned num = names.size();
813  if (num == 0)
814  return;
815 
816  vector<QueryInfo *> infos_ipv4(num, NULL);
817  vector<QueryInfo *> infos_ipv6(num, NULL);
818 
819  for (unsigned i = 0; i < num; ++i) {
820  if (skip[i])
821  continue;
822 
823  if (!ipv4_only()) {
824  infos_ipv6[i] = new QueryInfo(&(*ipv6_addresses)[i], names[i], kRrAaaa);
825  ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_aaaa,
826  CallbackCares, infos_ipv6[i]);
827  }
828  infos_ipv4[i] = new QueryInfo(&(*ipv4_addresses)[i], names[i], kRrA);
829  ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_a, CallbackCares,
830  infos_ipv4[i]);
831  }
832 
833  bool all_complete;
834  do {
835  all_complete = true;
836  WaitOnCares();
837  for (unsigned i = 0; i < num; ++i) {
838  if ((infos_ipv4[i] && !infos_ipv4[i]->complete)
839  || (infos_ipv6[i] && !infos_ipv6[i]->complete)) {
840  all_complete = false;
841  break;
842  }
843  }
844  } while (!all_complete);
845 
846  // Silently ignore errors with IPv4/6 if there are at least some usable IP
847  // addresses.
848  for (unsigned i = 0; i < num; ++i) {
849  if (skip[i])
850  continue;
851 
852  Failures status = kFailOther;
853  (*ttls)[i] = unsigned(-1);
854  (*fqdns)[i] = "";
855  if (infos_ipv6[i]) {
856  status = infos_ipv6[i]->status;
857  if (status == kFailOk) {
858  (*ttls)[i] = std::min(infos_ipv6[i]->ttl, (*ttls)[i]);
859  (*fqdns)[i] = infos_ipv6[i]->fqdn;
860  }
861  }
862  if (infos_ipv4[i]) {
863  (*ttls)[i] = std::min(infos_ipv4[i]->ttl, (*ttls)[i]);
864  if ((*fqdns)[i] == "")
865  (*fqdns)[i] = infos_ipv4[i]->fqdn;
866  if (status != kFailOk)
867  status = infos_ipv4[i]->status;
868  }
869  (*failures)[i] = status;
870  }
871 
872  for (unsigned i = 0; i < num; ++i) {
873  delete infos_ipv4[i];
874  delete infos_ipv6[i];
875  }
876 }
877 
878 
879 bool CaresResolver::SetResolvers(const vector<string> &resolvers) {
880  string address_list = JoinStrings(resolvers, ",");
881  int retval = ares_set_servers_csv(*channel_, address_list.c_str());
882  if (retval != ARES_SUCCESS)
883  return false;
884 
886  return true;
887 }
888 
889 
895 bool CaresResolver::SetSearchDomains(const vector<string> &domains) {
896  // From ares_private.h
897  struct {
898  int flags;
899  int timeout;
900  int tries;
901  int ndots;
902  int rotate;
903  int udp_port;
904  int tcp_port;
905  int socket_send_buffer_size;
906  int socket_receive_buffer_size;
907  char **domains;
908  int ndomains;
909  // More fields come in the original data structure
910  } ares_channelhead;
911 
912  memcpy(&ares_channelhead, *channel_, sizeof(ares_channelhead));
913  if (ares_channelhead.domains) {
914  for (int i = 0; i < ares_channelhead.ndomains; ++i) {
915  free(ares_channelhead.domains[i]);
916  }
917  free(ares_channelhead.domains);
918  ares_channelhead.domains = NULL;
919  }
920 
921  ares_channelhead.ndomains = static_cast<int>(domains.size());
922  if (ares_channelhead.ndomains > 0) {
923  ares_channelhead.domains = reinterpret_cast<char **>(
924  smalloc(ares_channelhead.ndomains * sizeof(char *)));
925  for (int i = 0; i < ares_channelhead.ndomains; ++i) {
926  ares_channelhead.domains[i] = strdup(domains[i].c_str());
927  }
928  }
929 
930  memcpy(*channel_, &ares_channelhead, sizeof(ares_channelhead));
931 
932  domains_ = domains;
933  return true;
934 }
935 
936 
938  int retval = SetResolvers(system_resolvers_);
939  assert(retval == true);
940 }
941 
942 
944  int retval = SetSearchDomains(system_domains_);
945  assert(retval == true);
946 }
947 
948 
954  // Adapted from libcurl
955  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
956  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
957  int bitmask = ares_getsock(*channel_, socks, ARES_GETSOCK_MAXNUM);
958  unsigned num = 0;
959  for (unsigned i = 0; i < ARES_GETSOCK_MAXNUM; ++i) {
960  pfd[i].events = 0;
961  pfd[i].revents = 0;
962  if (ARES_GETSOCK_READABLE(bitmask, i)) {
963  pfd[i].fd = socks[i];
964  pfd[i].events |= POLLRDNORM | POLLIN;
965  }
966  if (ARES_GETSOCK_WRITABLE(bitmask, i)) {
967  pfd[i].fd = socks[i];
968  pfd[i].events |= POLLWRNORM | POLLOUT;
969  }
970  if (pfd[i].events != 0)
971  num++;
972  else
973  break;
974  }
975 
976  int nfds = 0;
977  if (num > 0) {
978  do {
979  nfds = poll(pfd, num, timeout_ms());
980  if (nfds == -1) {
981  // poll must not fail for other reasons
982  if ((errno != EAGAIN) && (errno != EINTR))
983  PANIC(NULL);
984  }
985  } while (nfds == -1);
986  }
987 
988  if (nfds == 0) {
989  // Call ares_process() unconditionally here, even if we simply timed out
990  // above, as otherwise the ares name resolve won't timeout.
991  ares_process_fd(*channel_, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
992  } else {
993  // Go through the descriptors and ask for executing the callbacks.
994  for (unsigned i = 0; i < num; ++i) {
995  ares_process_fd(
996  *channel_,
997  pfd[i].revents & (POLLRDNORM | POLLIN) ? pfd[i].fd : ARES_SOCKET_BAD,
998  pfd[i].revents & (POLLWRNORM | POLLOUT) ? pfd[i].fd
999  : ARES_SOCKET_BAD);
1000  }
1001  }
1002 }
1003 
1004 
1005 //------------------------------------------------------------------------------
1006 
1007 
1013 HostfileResolver *HostfileResolver::Create(const string &path, bool ipv4_only) {
1014  HostfileResolver *resolver = new HostfileResolver(ipv4_only);
1015 
1016  string hosts_file = path;
1017  if (hosts_file == "") {
1018  char *hosts_env = getenv("HOST_ALIASES");
1019  if (hosts_env != NULL) {
1020  hosts_file = string(hosts_env);
1021  } else {
1022  hosts_file = "/etc/hosts";
1023  }
1024  }
1025  resolver->fhosts_ = fopen(hosts_file.c_str(), "r");
1026  if (!resolver->fhosts_) {
1027  LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn, "failed to read host file %s",
1028  hosts_file.c_str());
1029  delete resolver;
1030  return NULL;
1031  }
1032  return resolver;
1033 }
1034 
1035 
1041 static bool SortNameLength(const string &a, const string &b) {
1042  unsigned len_a = a.length();
1043  unsigned len_b = b.length();
1044  if (len_a != len_b)
1045  return len_a > len_b;
1046  return a > b;
1047 }
1048 
1049 
1053 void HostfileResolver::DoResolve(const vector<string> &names,
1054  const vector<bool> &skip,
1055  vector<vector<std::string> > *ipv4_addresses,
1056  vector<vector<std::string> > *ipv6_addresses,
1057  vector<Failures> *failures,
1058  vector<unsigned> *ttls,
1059  vector<string> *fqdns) {
1060  unsigned num = names.size();
1061  if (num == 0)
1062  return;
1063 
1064  ParseHostFile();
1065  for (unsigned i = 0; i < num; ++i) {
1066  if (skip[i])
1067  continue;
1068 
1069  vector<string> effective_names;
1070  if (!names[i].empty() && (names[i][names[i].length() - 1] == '.')) {
1071  effective_names.push_back(names[i].substr(0, names[i].length() - 1));
1072  } else {
1073  effective_names.push_back(names[i]);
1074  for (unsigned j = 0; j < domains().size(); ++j) {
1075  effective_names.push_back(names[i] + "." + domains()[j]);
1076  }
1077  }
1078 
1079  // Use the longest matching name as fqdn
1080  std::sort(effective_names.begin(), effective_names.end(), SortNameLength);
1081 
1082  (*failures)[i] = kFailUnknownHost;
1083  (*fqdns)[i] = names[i];
1084  for (unsigned j = 0; j < effective_names.size(); ++j) {
1085  map<string, HostEntry>::iterator iter = host_map_.find(
1086  effective_names[j]);
1087  if (iter != host_map_.end()) {
1088  (*ipv4_addresses)[i].insert((*ipv4_addresses)[i].end(),
1089  iter->second.ipv4_addresses.begin(),
1090  iter->second.ipv4_addresses.end());
1091  (*ipv6_addresses)[i].insert((*ipv6_addresses)[i].end(),
1092  iter->second.ipv6_addresses.begin(),
1093  iter->second.ipv6_addresses.end());
1094  (*ttls)[i] = min_ttl_;
1095  (*fqdns)[i] = effective_names[j];
1096  (*failures)[i] = kFailOk;
1097  break;
1098  } // Host name found
1099  } // All possible names (search domains added)
1100  }
1101 }
1102 
1103 
1105  : Resolver(ipv4_only, 0, 0), fhosts_(NULL) { }
1106 
1107 
1109  if (fhosts_)
1110  fclose(fhosts_);
1111 }
1112 
1113 
1118  assert(fhosts_);
1119  rewind(fhosts_);
1120  host_map_.clear();
1121 
1122  string line;
1123  while (GetLineFile(fhosts_, &line)) {
1124  char address[kIpMaxLength + 1];
1125  char hostname[kHostnameMaxLength + 1];
1126  int bytes_read;
1127  size_t str_offset = 0;
1128 
1129  // strip comments
1130  size_t hash_pos = line.find_first_of('#');
1131  if (hash_pos != string::npos)
1132  line = line.substr(0, hash_pos);
1133 
1134  // First token is an IP address
1135  int ip_start_pos = -1, ip_end_pos = -1, scan_result;
1136  scan_result = sscanf(line.c_str(), " %n%*s%n", &ip_start_pos, &ip_end_pos);
1137  if (scan_result == EOF)
1138  continue;
1139  assert(ip_start_pos != -1);
1140  assert(ip_end_pos != -1);
1141  if (ip_start_pos == ip_end_pos)
1142  continue;
1143  if (ip_end_pos - ip_start_pos > kIpMaxLength) {
1144  LogCvmfs(
1146  "Skipping line in hosts file due to invalid IP address format: %s",
1147  line.c_str());
1148  continue;
1149  }
1150 
1151  bytes_read = -1;
1152  scan_result = sscanf(line.c_str(), " %s%n", address, &bytes_read);
1153  assert(scan_result == 1);
1154  assert(bytes_read != -1);
1155  str_offset += bytes_read;
1156 
1157  // Next tokens are hostnames and aliases (we treat them as equal)
1158  while (str_offset < line.length()) {
1159  // check hostname length
1160  int hostname_start_pos = -1, hostname_end_pos = -1;
1161  scan_result = sscanf(line.c_str() + str_offset, " %n%*s%n",
1162  &hostname_start_pos, &hostname_end_pos);
1163  if (scan_result == EOF)
1164  break;
1165  assert(hostname_start_pos != -1);
1166  assert(hostname_end_pos != -1);
1167  if (hostname_start_pos == hostname_end_pos)
1168  break;
1169 
1170  if (hostname_end_pos - hostname_start_pos > kHostnameMaxLength) {
1171  LogCvmfs(
1173  "Skipping invalid (too long) hostname in hosts file on line: %s",
1174  line.c_str());
1175  str_offset += hostname_end_pos;
1176  continue;
1177  }
1178 
1179  bytes_read = -1;
1180  scan_result = sscanf(line.c_str() + str_offset, " %s%n", hostname,
1181  &bytes_read);
1182  assert(scan_result == 1);
1183  assert(bytes_read != -1);
1184  str_offset += bytes_read;
1185 
1186  if (hostname[strlen(hostname) - 1] == '.')
1187  hostname[strlen(hostname) - 1] = 0; // strip the last character
1188 
1189  map<string, HostEntry>::iterator iter = host_map_.find(hostname);
1190  if (iter == host_map_.end()) {
1191  HostEntry entry;
1192  if (IsIpv4Address(address))
1193  entry.ipv4_addresses.push_back(address);
1194  else if (!ipv4_only())
1195  entry.ipv6_addresses.push_back(address);
1196  host_map_[hostname] = entry;
1197  } else {
1198  if (IsIpv4Address(address))
1199  iter->second.ipv4_addresses.push_back(address);
1200  else if (!ipv4_only())
1201  iter->second.ipv6_addresses.push_back(address);
1202  }
1203  } // Current line
1204  } // Hosts file
1205 }
1206 
1207 
1208 bool HostfileResolver::SetSearchDomains(const vector<string> &domains) {
1209  domains_ = domains;
1210  return true;
1211 }
1212 
1213 
1215  // TODO(jblomer)
1216  PANIC(NULL);
1217 }
1218 
1219 
1220 //------------------------------------------------------------------------------
1221 
1222 
1228  const unsigned retries,
1229  const unsigned timeout_ms) {
1230  CaresResolver *cares_resolver = CaresResolver::Create(ipv4_only, retries,
1231  timeout_ms);
1232  if (!cares_resolver)
1233  return NULL;
1234  HostfileResolver *hostfile_resolver = HostfileResolver::Create("", ipv4_only);
1235  if (!hostfile_resolver) {
1236  delete cares_resolver;
1237  return NULL;
1238  }
1239  bool retval = hostfile_resolver->SetSearchDomains(cares_resolver->domains());
1240  assert(retval);
1241 
1242  NormalResolver *normal_resolver = new NormalResolver();
1243  normal_resolver->cares_resolver_ = cares_resolver;
1244  normal_resolver->hostfile_resolver_ = hostfile_resolver;
1245  normal_resolver->domains_ = cares_resolver->domains();
1246  normal_resolver->resolvers_ = cares_resolver->resolvers();
1247  normal_resolver->retries_ = cares_resolver->retries();
1248  normal_resolver->timeout_ms_ = cares_resolver->timeout_ms();
1249  return normal_resolver;
1250 }
1251 
1252 
1256 bool NormalResolver::SetResolvers(const vector<string> &resolvers) {
1257  return cares_resolver_->SetResolvers(resolvers);
1258 }
1259 
1260 
1264 bool NormalResolver::SetSearchDomains(const vector<string> &domains) {
1265  vector<string> old_domains = hostfile_resolver_->domains();
1266  bool retval = hostfile_resolver_->SetSearchDomains(domains);
1267  if (!retval)
1268  return false;
1269  retval = cares_resolver_->SetSearchDomains(domains);
1270  if (!retval) {
1271  retval = hostfile_resolver_->SetSearchDomains(old_domains);
1272  assert(retval);
1273  return false;
1274  }
1275  return true;
1276 }
1277 
1278 
1281 }
1282 
1283 
1286  bool retval = hostfile_resolver_->SetSearchDomains(
1288  assert(retval);
1289 }
1290 
1291 
1296 void NormalResolver::DoResolve(const vector<string> &names,
1297  const vector<bool> &skip,
1298  vector<vector<string> > *ipv4_addresses,
1299  vector<vector<string> > *ipv6_addresses,
1300  vector<Failures> *failures,
1301  vector<unsigned> *ttls,
1302  vector<string> *fqdns) {
1303  unsigned num = names.size();
1304  hostfile_resolver_->DoResolve(names, skip, ipv4_addresses, ipv6_addresses,
1305  failures, ttls, fqdns);
1306  vector<bool> skip_cares = skip;
1307  for (unsigned i = 0; i < num; ++i) {
1308  if ((*failures)[i] == kFailOk)
1309  skip_cares[i] = true;
1310  }
1311  cares_resolver_->DoResolve(names, skip_cares, ipv4_addresses, ipv6_addresses,
1312  failures, ttls, fqdns);
1313 }
1314 
1315 
1317  : Resolver(false, 0, 0), cares_resolver_(NULL), hostfile_resolver_(NULL) { }
1318 
1319 
1321  delete cares_resolver_;
1322  delete hostfile_resolver_;
1323 }
1324 
1325 } // 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:895
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:1227
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:1208
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:1279
CaresResolver(const bool ipv4_only, const unsigned retries, const unsigned timeout_ms)
Definition: dns.cc:690
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:1013
string StripIp(const string &decorated_ip)
Definition: dns.cc:168
bool IsValid() const
Definition: dns.cc:279
virtual ~NormalResolver()
Definition: dns.cc:1320
static Failures CaresExtractIpv6(const unsigned char *abuf, int alen, vector< string > *addresses, unsigned *ttl, string *fqdn)
Definition: dns.cc:640
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:1284
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:937
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:1104
Definition: dns.h:90
virtual bool SetSearchDomains(const std::vector< std::string > &domains)
Definition: dns.cc:1264
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:1053
bool IsEquivalent(const Host &other) const
Definition: dns.cc:257
virtual bool SetResolvers(const std::vector< std::string > &resolvers)
Definition: dns.cc:879
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:805
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:1108
char * lookup_options_
Definition: dns.h:321
IpPreference
Definition: dns.h:46
static bool SortNameLength(const string &a, const string &b)
Definition: dns.cc:1041
unsigned retries() const
Definition: dns.h:203
void ParseHostFile()
Definition: dns.cc:1117
void WaitOnCares()
Definition: dns.cc:953
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:710
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:1296
virtual ~CaresResolver()
Definition: dns.cc:698
virtual void SetSystemSearchDomains()
Definition: dns.cc:943
unsigned max_ttl_
Definition: dns.h:272
std::set< std::string > ipv4_addresses_
Definition: dns.h:146
virtual void SetSystemSearchDomains()
Definition: dns.cc:1214
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:1256
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