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