CernVM-FS  2.10.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 "logging.h"
38 #include "sanitizer.h"
39 #include "smalloc.h"
40 #include "util/exception.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  LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s",
455  names[i].c_str(), ipv4_addresses[i][j].c_str());
456  host.ipv4_addresses_.insert(ipv4_addresses[i][j]);
457  }
458 
459  for (unsigned j = 0; j < ipv6_addresses[i].size(); ++j) {
460  if (!IsIpv6Address(ipv6_addresses[i][j])) {
462  "host name %s resolves to invalid IPv6 address %s",
463  names[i].c_str(), ipv6_addresses[i][j].c_str());
464  continue;
465  }
466  // For URLs we need brackets around IPv6 addresses
467  LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s",
468  names[i].c_str(), ipv6_addresses[i][j].c_str());
469  host.ipv6_addresses_.insert("[" + ipv6_addresses[i][j] + "]");
470  }
471 
472  if (host.ipv4_addresses_.empty() && host.ipv6_addresses_.empty()) {
473  LogCvmfs(kLogDns, kLogDebug, "no addresses returned for %s",
474  names[i].c_str());
475  host.status_ = kFailNoAddress;
476  }
477 
478  // Remove surplus IP addresses
479  if (throttle_ > 0) {
480  while (host.ipv4_addresses_.size() > throttle_) {
481  unsigned random = prng_.Next(host.ipv4_addresses_.size());
482  set<string>::iterator rnd_itr = host.ipv4_addresses_.begin();
483  std::advance(rnd_itr, random);
484  host.ipv4_addresses_.erase(rnd_itr);
485  }
486  while (host.ipv6_addresses_.size() > throttle_) {
487  unsigned random = prng_.Next(host.ipv6_addresses_.size());
488  set<string>::iterator rnd_itr = host.ipv6_addresses_.begin();
489  std::advance(rnd_itr, random);
490  host.ipv6_addresses_.erase(rnd_itr);
491  }
492  }
493 
494  (*hosts)[i] = host;
495  }
496 }
497 
498 
499 //------------------------------------------------------------------------------
500 
501 
502 namespace {
503 
505  kRrA = 0,
507 };
508 
515 struct QueryInfo {
517  vector<string> *a,
518  const string &n,
519  const ResourceRecord r)
520  : addresses(a)
521  , complete(false)
522  , fqdn(n)
523  , name(n)
524  , record(r)
525  , status(kFailOther)
526  , ttl(0)
527  { }
528 
529  vector<string> *addresses;
530  bool complete;
531  string fqdn;
532  string name;
535  unsigned ttl;
536 };
537 
538 } // namespace
539 
540 
541 static Failures CaresExtractIpv4(const unsigned char *abuf, int alen,
542  vector<string> *addresses,
543  unsigned *ttl,
544  string *fqdn);
545 static Failures CaresExtractIpv6(const unsigned char *abuf, int alen,
546  vector<string> *addresses,
547  unsigned *ttl,
548  string *fqdn);
549 
554 static void CallbackCares(
555  void *arg,
556  int status,
557  int timeouts_ms,
558  unsigned char *abuf,
559  int alen)
560 {
561  QueryInfo *info = reinterpret_cast<QueryInfo *>(arg);
562 
563  info->complete = true;
564  switch (status) {
565  case ARES_SUCCESS:
566  Failures retval;
567  switch (info->record) {
568  case kRrA:
569  retval = CaresExtractIpv4(
570  abuf, alen, info->addresses, &info->ttl, &info->fqdn);
571  break;
572  case kRrAaaa:
573  retval = CaresExtractIpv6(
574  abuf, alen, info->addresses, &info->ttl, &info->fqdn);
575  break;
576  default:
577  // Never here.
578  PANIC(NULL);
579  }
580  info->status = retval;
581  break;
582  case ARES_ENODATA:
583  info->status = kFailUnknownHost;
584  break;
585  case ARES_EFORMERR:
586  info->status = kFailMalformed;
587  break;
588  case ARES_ENOTFOUND:
589  info->status = kFailUnknownHost;
590  break;
591  case ARES_ETIMEOUT:
592  info->status = kFailTimeout;
593  break;
594  case ARES_ECONNREFUSED:
595  info->status = kFailInvalidResolvers;
596  break;
597  default:
598  info->status = kFailOther;
599  }
600 }
601 
602 
608  const unsigned char *abuf,
609  int alen,
610  vector<string> *addresses,
611  unsigned *ttl,
612  string *fqdn)
613 {
614  struct hostent *host_entry = NULL;
615  struct ares_addrttl records[CaresResolver::kMaxAddresses];
616  int naddrttls = CaresResolver::kMaxAddresses;
617  int retval = ares_parse_a_reply(abuf, alen, &host_entry, records, &naddrttls);
618 
619  switch (retval) {
620  case ARES_SUCCESS:
621  if (host_entry == NULL)
622  return kFailMalformed;
623  if (host_entry->h_name == NULL) {
624  ares_free_hostent(host_entry);
625  return kFailMalformed;
626  }
627  *fqdn = string(host_entry->h_name);
628  ares_free_hostent(host_entry);
629 
630  if (naddrttls <= 0)
631  return kFailMalformed;
632  *ttl = unsigned(-1);
633  for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) {
634  if (records[i].ttl < 0)
635  continue;
636  *ttl = std::min(unsigned(records[i].ttl), *ttl);
637 
638  char addrstr[INET_ADDRSTRLEN];
639  const void *retval_p =
640  inet_ntop(AF_INET, &(records[i].ipaddr), addrstr, INET_ADDRSTRLEN);
641  if (!retval_p)
642  continue;
643  addresses->push_back(addrstr);
644  }
645  if (addresses->empty())
646  return kFailMalformed;
647  return kFailOk;
648  case ARES_EBADRESP:
649  // Fall through
650  case ARES_ENODATA:
651  return kFailMalformed;
652  default:
653  return kFailOther;
654  }
655 }
656 
657 
659  const unsigned char *abuf,
660  int alen,
661  vector<string> *addresses,
662  unsigned *ttl,
663  string *fqdn)
664 {
665  struct hostent *host_entry = NULL;
666  struct ares_addr6ttl records[CaresResolver::kMaxAddresses];
667  int naddrttls = CaresResolver::kMaxAddresses;
668  int retval =
669  ares_parse_aaaa_reply(abuf, alen, &host_entry, records, &naddrttls);
670 
671  switch (retval) {
672  case ARES_SUCCESS:
673  if (host_entry == NULL)
674  return kFailMalformed;
675  if (host_entry->h_name == NULL) {
676  ares_free_hostent(host_entry);
677  return kFailMalformed;
678  }
679  *fqdn = string(host_entry->h_name);
680  ares_free_hostent(host_entry);
681 
682  if (naddrttls <= 0)
683  return kFailMalformed;
684  *ttl = unsigned(-1);
685  for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) {
686  if (records[i].ttl < 0)
687  continue;
688  *ttl = std::min(unsigned(records[i].ttl), *ttl);
689 
690  char addrstr[INET6_ADDRSTRLEN];
691  const void *retval_p =
692  inet_ntop(AF_INET6, &(records[i].ip6addr), addrstr, INET6_ADDRSTRLEN);
693  if (!retval_p)
694  continue;
695  addresses->push_back(addrstr);
696  }
697  if (addresses->empty())
698  return kFailMalformed;
699  return kFailOk;
700  case ARES_EBADRESP:
701  // Fall through
702  case ARES_ENODATA:
703  return kFailMalformed;
704  default:
705  return kFailOther;
706  }
707 }
708 
709 
711  const bool ipv4_only,
712  const unsigned retries,
713  const unsigned timeout_ms)
714  : Resolver(ipv4_only, retries, timeout_ms)
715  , channel_(NULL)
716  , lookup_options_(strdup("b"))
717 {
718 }
719 
720 
722  if (channel_) {
723  ares_destroy(*channel_);
724  free(channel_);
725  }
726  free(lookup_options_);
727 }
728 
729 
734  const bool ipv4_only,
735  const unsigned retries,
736  const unsigned timeout_ms)
737 {
738  int retval;
739  if (getenv("HOSTALIASES") == NULL) {
740  retval = setenv("HOSTALIASES", "/etc/hosts", 1);
741  assert(retval == 0);
742  }
743 
744  CaresResolver *resolver = new CaresResolver(ipv4_only, retries, timeout_ms);
745  resolver->channel_ = reinterpret_cast<ares_channel *>(
746  smalloc(sizeof(ares_channel)));
747  memset(resolver->channel_, 0, sizeof(ares_channel));
748 
749  struct ares_addr_node *addresses;
750  struct ares_addr_node *iter;
751  struct ares_options options;
752  int optmask;
753  memset(&options, 0, sizeof(options));
754  options.timeout = timeout_ms;
755  options.tries = 1 + retries;
756  options.lookups = resolver->lookup_options_;
757  optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_LOOKUPS;
758  retval = ares_init_options(resolver->channel_, &options, optmask);
759  if (retval != ARES_SUCCESS)
760  goto create_fail;
761 
762  // Save search domains
763  retval = ares_save_options(*resolver->channel_, &options, &optmask);
764  if (retval != ARES_SUCCESS)
765  goto create_fail;
766  for (int i = 0; i < options.ndomains; ++i) {
767  resolver->domains_.push_back(options.domains[i]);
768  }
769  ares_destroy_options(&options);
770  resolver->system_domains_ = resolver->domains_;
771 
772  // Save the system default resolvers
773  addresses = NULL;
774  retval = ares_get_servers(*resolver->channel_, &addresses);
775  if (retval != ARES_SUCCESS)
776  goto create_fail;
777  iter = addresses;
778  while (iter) {
779  switch (iter->family) {
780  case AF_INET: {
781  char addrstr[INET_ADDRSTRLEN];
782  const void *retval_p =
783  inet_ntop(AF_INET, &(iter->addr), addrstr, INET_ADDRSTRLEN);
784  if (!retval_p) {
786  "invalid system name resolver");
787  } else {
788  resolver->resolvers_.push_back(string(addrstr) + ":53");
789  }
790  break;
791  }
792  case AF_INET6: {
793  char addrstr[INET6_ADDRSTRLEN];
794  const void *retval_p =
795  inet_ntop(AF_INET6, &(iter->addr), addrstr, INET6_ADDRSTRLEN);
796  if (!retval_p) {
798  "invalid system name resolver");
799  } else {
800  resolver->resolvers_.push_back("[" + string(addrstr) + "]:53");
801  }
802  break;
803  }
804  default:
805  // Never here.
806  PANIC(NULL);
807  }
808  iter = iter->next;
809  }
810  ares_free_data(addresses);
811  resolver->system_resolvers_ = resolver->resolvers_;
812 
813  return resolver;
814 
815  create_fail:
817  "failed to initialize c-ares resolver (%d - %s)",
818  retval, ares_strerror(retval));
819  free(resolver->channel_);
820  resolver->channel_ = NULL;
821  delete resolver;
822  return NULL;
823 }
824 
825 
831  const vector<string> &names,
832  const vector<bool> &skip,
833  vector<vector<string> > *ipv4_addresses,
834  vector<vector<string> > *ipv6_addresses,
835  vector<Failures> *failures,
836  vector<unsigned> *ttls,
837  vector<string> *fqdns)
838 {
839  unsigned num = names.size();
840  if (num == 0)
841  return;
842 
843  vector<QueryInfo *> infos_ipv4(num, NULL);
844  vector<QueryInfo *> infos_ipv6(num, NULL);
845 
846  for (unsigned i = 0; i < num; ++i) {
847  if (skip[i])
848  continue;
849 
850  if (!ipv4_only()) {
851  infos_ipv6[i] = new QueryInfo(&(*ipv6_addresses)[i], names[i], kRrAaaa);
852  ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_aaaa,
853  CallbackCares, infos_ipv6[i]);
854  }
855  infos_ipv4[i] = new QueryInfo(&(*ipv4_addresses)[i], names[i], kRrA);
856  ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_a,
857  CallbackCares, infos_ipv4[i]);
858  }
859 
860  bool all_complete;
861  do {
862  all_complete = true;
863  WaitOnCares();
864  for (unsigned i = 0; i < num; ++i) {
865  if ((infos_ipv4[i] && !infos_ipv4[i]->complete) ||
866  (infos_ipv6[i] && !infos_ipv6[i]->complete))
867  {
868  all_complete = false;
869  break;
870  }
871  }
872  } while (!all_complete);
873 
874  // Silently ignore errors with IPv4/6 if there are at least some usable IP
875  // addresses.
876  for (unsigned i = 0; i < num; ++i) {
877  if (skip[i])
878  continue;
879 
880  Failures status = kFailOther;
881  (*ttls)[i] = unsigned(-1);
882  (*fqdns)[i] = "";
883  if (infos_ipv6[i]) {
884  status = infos_ipv6[i]->status;
885  if (status == kFailOk) {
886  (*ttls)[i] = std::min(infos_ipv6[i]->ttl, (*ttls)[i]);
887  (*fqdns)[i] = infos_ipv6[i]->fqdn;
888  }
889  }
890  if (infos_ipv4[i]) {
891  (*ttls)[i] = std::min(infos_ipv4[i]->ttl, (*ttls)[i]);
892  if ((*fqdns)[i] == "")
893  (*fqdns)[i] = infos_ipv4[i]->fqdn;
894  if (status != kFailOk)
895  status = infos_ipv4[i]->status;
896  }
897  (*failures)[i] = status;
898  }
899 
900  for (unsigned i = 0; i < num; ++i) {
901  delete infos_ipv4[i];
902  delete infos_ipv6[i];
903  }
904 }
905 
906 
907 bool CaresResolver::SetResolvers(const vector<string> &resolvers) {
908  string address_list = JoinStrings(resolvers, ",");
909  int retval = ares_set_servers_csv(*channel_, address_list.c_str());
910  if (retval != ARES_SUCCESS)
911  return false;
912 
914  return true;
915 }
916 
917 
923 bool CaresResolver::SetSearchDomains(const vector<string> &domains) {
924  // From ares_private.h
925  struct {
926  int flags;
927  int timeout;
928  int tries;
929  int ndots;
930  int rotate;
931  int udp_port;
932  int tcp_port;
933  int socket_send_buffer_size;
934  int socket_receive_buffer_size;
935  char **domains;
936  int ndomains;
937  // More fields come in the original data structure
938  } ares_channelhead;
939 
940  memcpy(&ares_channelhead, *channel_, sizeof(ares_channelhead));
941  if (ares_channelhead.domains) {
942  for (int i = 0; i < ares_channelhead.ndomains; ++i) {
943  free(ares_channelhead.domains[i]);
944  }
945  free(ares_channelhead.domains);
946  ares_channelhead.domains = NULL;
947  }
948 
949  ares_channelhead.ndomains = static_cast<int>(domains.size());
950  if (ares_channelhead.ndomains > 0) {
951  ares_channelhead.domains = reinterpret_cast<char **>(
952  smalloc(ares_channelhead.ndomains * sizeof(char *)));
953  for (int i = 0; i < ares_channelhead.ndomains; ++i) {
954  ares_channelhead.domains[i] = strdup(domains[i].c_str());
955  }
956  }
957 
958  memcpy(*channel_, &ares_channelhead, sizeof(ares_channelhead));
959 
960  domains_ = domains;
961  return true;
962 }
963 
964 
966  int retval = SetResolvers(system_resolvers_);
967  assert(retval == true);
968 }
969 
970 
972  int retval = SetSearchDomains(system_domains_);
973  assert(retval == true);
974 }
975 
976 
982  // Adapted from libcurl
983  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
984  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
985  int bitmask = ares_getsock(*channel_, socks, ARES_GETSOCK_MAXNUM);
986  unsigned num = 0;
987  for (unsigned i = 0; i < ARES_GETSOCK_MAXNUM; ++i) {
988  pfd[i].events = 0;
989  pfd[i].revents = 0;
990  if (ARES_GETSOCK_READABLE(bitmask, i)) {
991  pfd[i].fd = socks[i];
992  pfd[i].events |= POLLRDNORM|POLLIN;
993  }
994  if (ARES_GETSOCK_WRITABLE(bitmask, i)) {
995  pfd[i].fd = socks[i];
996  pfd[i].events |= POLLWRNORM|POLLOUT;
997  }
998  if (pfd[i].events != 0)
999  num++;
1000  else
1001  break;
1002  }
1003 
1004  int nfds = 0;
1005  if (num > 0) {
1006  do {
1007  nfds = poll(pfd, num, timeout_ms());
1008  if (nfds == -1) {
1009  // poll must not fail for other reasons
1010  if ((errno != EAGAIN) && (errno != EINTR))
1011  PANIC(NULL);
1012  }
1013  } while (nfds == -1);
1014  }
1015 
1016  if (nfds == 0) {
1017  // Call ares_process() unconditonally here, even if we simply timed out
1018  // above, as otherwise the ares name resolve won't timeout.
1019  ares_process_fd(*channel_, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
1020  } else {
1021  // Go through the descriptors and ask for executing the callbacks.
1022  for (unsigned i = 0; i < num; ++i) {
1023  ares_process_fd(*channel_,
1024  pfd[i].revents & (POLLRDNORM|POLLIN) ?
1025  pfd[i].fd : ARES_SOCKET_BAD,
1026  pfd[i].revents & (POLLWRNORM|POLLOUT) ?
1027  pfd[i].fd : ARES_SOCKET_BAD);
1028  }
1029  }
1030 }
1031 
1032 
1033 //------------------------------------------------------------------------------
1034 
1035 
1042  const string &path,
1043  bool ipv4_only)
1044 {
1045  HostfileResolver *resolver = new HostfileResolver(ipv4_only);
1046 
1047  string hosts_file = path;
1048  if (hosts_file == "") {
1049  char *hosts_env = getenv("HOST_ALIASES");
1050  if (hosts_env != NULL) {
1051  hosts_file = string(hosts_env);
1052  } else {
1053  hosts_file = "/etc/hosts";
1054  }
1055  }
1056  resolver->fhosts_ = fopen(hosts_file.c_str(), "r");
1057  if (!resolver->fhosts_) {
1058  LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn, "failed to read host file %s",
1059  hosts_file.c_str());
1060  delete resolver;
1061  return NULL;
1062  }
1063  return resolver;
1064 }
1065 
1066 
1072 static bool SortNameLength(const string &a, const string &b) {
1073  unsigned len_a = a.length();
1074  unsigned len_b = b.length();
1075  if (len_a != len_b)
1076  return len_a > len_b;
1077  return a > b;
1078 }
1079 
1080 
1085  const vector<string> &names,
1086  const vector<bool> &skip,
1087  vector< vector<std::string> > *ipv4_addresses,
1088  vector< vector<std::string> > *ipv6_addresses,
1089  vector<Failures> *failures,
1090  vector<unsigned> *ttls,
1091  vector<string> *fqdns)
1092 {
1093  unsigned num = names.size();
1094  if (num == 0)
1095  return;
1096 
1097  ParseHostFile();
1098  for (unsigned i = 0; i < num; ++i) {
1099  if (skip[i])
1100  continue;
1101 
1102  vector<string> effective_names;
1103  if (!names[i].empty() && (names[i][names[i].length()-1] == '.')) {
1104  effective_names.push_back(names[i].substr(0, names[i].length()-1));
1105  } else {
1106  effective_names.push_back(names[i]);
1107  for (unsigned j = 0; j < domains().size(); ++j) {
1108  effective_names.push_back(names[i] + "." + domains()[j]);
1109  }
1110  }
1111 
1112  // Use the longest matching name as fqdn
1113  std::sort(effective_names.begin(), effective_names.end(), SortNameLength);
1114 
1115  (*failures)[i] = kFailUnknownHost;
1116  (*fqdns)[i] = names[i];
1117  for (unsigned j = 0; j < effective_names.size(); ++j) {
1118  map<string, HostEntry>::iterator iter =
1119  host_map_.find(effective_names[j]);
1120  if (iter != host_map_.end()) {
1121  (*ipv4_addresses)[i].insert((*ipv4_addresses)[i].end(),
1122  iter->second.ipv4_addresses.begin(),
1123  iter->second.ipv4_addresses.end());
1124  (*ipv6_addresses)[i].insert((*ipv6_addresses)[i].end(),
1125  iter->second.ipv6_addresses.begin(),
1126  iter->second.ipv6_addresses.end());
1127  (*ttls)[i] = min_ttl_;
1128  (*fqdns)[i] = effective_names[j];
1129  (*failures)[i] = kFailOk;
1130  break;
1131  } // Host name found
1132  } // All possible names (search domains added)
1133  }
1134 }
1135 
1136 
1138  : Resolver(ipv4_only, 0, 0)
1139  , fhosts_(NULL)
1140 { }
1141 
1142 
1144  if (fhosts_)
1145  fclose(fhosts_);
1146 }
1147 
1148 
1153  assert(fhosts_);
1154  rewind(fhosts_);
1155  host_map_.clear();
1156 
1157  string line;
1158  while (GetLineFile(fhosts_, &line)) {
1159  char address[kIpMaxLength + 1];
1160  char hostname[kHostnameMaxLength + 1];
1161  int bytes_read;
1162  size_t str_offset = 0;
1163 
1164  // strip comments
1165  size_t hash_pos = line.find_first_of('#');
1166  if (hash_pos != string::npos) line = line.substr(0, hash_pos);
1167 
1168  // First token is an IP address
1169  int ip_start_pos = -1, ip_end_pos = -1, scan_result;
1170  scan_result = sscanf(line.c_str(), " %n%*s%n", &ip_start_pos, &ip_end_pos);
1171  if (scan_result == EOF)
1172  continue;
1173  assert(ip_start_pos != -1);
1174  assert(ip_end_pos != -1);
1175  if (ip_start_pos == ip_end_pos)
1176  continue;
1177  if (ip_end_pos - ip_start_pos > kIpMaxLength) {
1179  "Skipping line in hosts file due to invalid IP address format: %s",
1180  line.c_str());
1181  continue;
1182  }
1183 
1184  bytes_read = -1;
1185  scan_result = sscanf(line.c_str(), " %s%n", address, &bytes_read);
1186  assert(scan_result == 1);
1187  assert(bytes_read != -1);
1188  str_offset += bytes_read;
1189 
1190  // Next tokens are hostnames and aliases (we treat them as equal)
1191  while (str_offset < line.length()) {
1192  // check hostname length
1193  int hostname_start_pos = -1, hostname_end_pos = -1;
1194  scan_result = sscanf(line.c_str() + str_offset, " %n%*s%n",
1195  &hostname_start_pos, &hostname_end_pos);
1196  if (scan_result == EOF)
1197  break;
1198  assert(hostname_start_pos != -1);
1199  assert(hostname_end_pos != -1);
1200  if (hostname_start_pos == hostname_end_pos)
1201  break;
1202 
1203  if (hostname_end_pos - hostname_start_pos > kHostnameMaxLength) {
1205  "Skipping invalid (too long) hostname in hosts file on line: %s",
1206  line.c_str());
1207  str_offset += hostname_end_pos;
1208  continue;
1209  }
1210 
1211  bytes_read = -1;
1212  scan_result = sscanf(line.c_str() + str_offset, " %s%n", hostname,
1213  &bytes_read);
1214  assert(scan_result == 1);
1215  assert(bytes_read != -1);
1216  str_offset += bytes_read;
1217 
1218  if (hostname[strlen(hostname)-1] == '.')
1219  hostname[strlen(hostname)-1] = 0; // strip the last character
1220 
1221  map<string, HostEntry>::iterator iter = host_map_.find(hostname);
1222  if (iter == host_map_.end()) {
1223  HostEntry entry;
1224  if (IsIpv4Address(address))
1225  entry.ipv4_addresses.push_back(address);
1226  else
1227  if (!ipv4_only()) entry.ipv6_addresses.push_back(address);
1228  host_map_[hostname] = entry;
1229  } else {
1230  if (IsIpv4Address(address))
1231  iter->second.ipv4_addresses.push_back(address);
1232  else
1233  if (!ipv4_only()) iter->second.ipv6_addresses.push_back(address);
1234  }
1235  } // Current line
1236  } // Hosts file
1237 }
1238 
1239 
1240 bool HostfileResolver::SetSearchDomains(const vector<string> &domains) {
1241  domains_ = domains;
1242  return true;
1243 }
1244 
1245 
1247  // TODO(jblomer)
1248  PANIC(NULL);
1249 }
1250 
1251 
1252 //------------------------------------------------------------------------------
1253 
1254 
1260  const bool ipv4_only,
1261  const unsigned retries,
1262  const unsigned timeout_ms)
1263 {
1264  CaresResolver *cares_resolver =
1265  CaresResolver::Create(ipv4_only, retries, timeout_ms);
1266  if (!cares_resolver)
1267  return NULL;
1268  HostfileResolver *hostfile_resolver = HostfileResolver::Create("", ipv4_only);
1269  if (!hostfile_resolver) {
1270  delete cares_resolver;
1271  return NULL;
1272  }
1273  bool retval = hostfile_resolver->SetSearchDomains(cares_resolver->domains());
1274  assert(retval);
1275 
1276  NormalResolver *normal_resolver = new NormalResolver();
1277  normal_resolver->cares_resolver_ = cares_resolver;
1278  normal_resolver->hostfile_resolver_ = hostfile_resolver;
1279  normal_resolver->domains_ = cares_resolver->domains();
1280  normal_resolver->resolvers_ = cares_resolver->resolvers();
1281  normal_resolver->retries_ = cares_resolver->retries();
1282  normal_resolver->timeout_ms_ = cares_resolver->timeout_ms();
1283  return normal_resolver;
1284 }
1285 
1286 
1290 bool NormalResolver::SetResolvers(const vector<string> &resolvers) {
1291  return cares_resolver_->SetResolvers(resolvers);
1292 }
1293 
1294 
1298 bool NormalResolver::SetSearchDomains(const vector<string> &domains) {
1299  vector<string> old_domains = hostfile_resolver_->domains();
1300  bool retval = hostfile_resolver_->SetSearchDomains(domains);
1301  if (!retval)
1302  return false;
1303  retval = cares_resolver_->SetSearchDomains(domains);
1304  if (!retval) {
1305  retval = hostfile_resolver_->SetSearchDomains(old_domains);
1306  assert(retval);
1307  return false;
1308  }
1309  return true;
1310 }
1311 
1312 
1315 }
1316 
1317 
1320  bool retval =
1322  assert(retval);
1323 }
1324 
1325 
1331  const vector<string> &names,
1332  const vector<bool> &skip,
1333  vector< vector<string> > *ipv4_addresses,
1334  vector< vector<string> > *ipv6_addresses,
1335  vector<Failures> *failures,
1336  vector<unsigned> *ttls,
1337  vector<string> *fqdns)
1338 {
1339  unsigned num = names.size();
1340  hostfile_resolver_->DoResolve(names, skip, ipv4_addresses, ipv6_addresses,
1341  failures, ttls, fqdns);
1342  vector<bool> skip_cares = skip;
1343  for (unsigned i = 0; i < num; ++i) {
1344  if ((*failures)[i] == kFailOk)
1345  skip_cares[i] = true;
1346  }
1347  cares_resolver_->DoResolve(names, skip_cares, ipv4_addresses, ipv6_addresses,
1348  failures, ttls, fqdns);
1349 }
1350 
1351 
1353  : Resolver(false, 0, 0)
1354  , cares_resolver_(NULL)
1355  , hostfile_resolver_(NULL)
1356 {
1357 }
1358 
1359 
1361  delete cares_resolver_;
1362  delete hostfile_resolver_;
1363 }
1364 
1365 } // namespace dns
Failures
Definition: dns.h:28
static const int kHostnameMaxLength
Definition: dns.h:362
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
int64_t id_
Definition: dns.h:140
virtual bool SetSearchDomains(const std::vector< std::string > &domains)
Definition: dns.cc:923
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:1259
Definition: dns.h:357
vector< string > SplitString(const string &str, const char delim, const unsigned max_chunks)
Definition: string.cc:288
#define PANIC(...)
Definition: exception.h:26
void CopyFrom(const Host &other)
Definition: dns.cc:217
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:318
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:1240
std::vector< std::string > ipv4_addresses
Definition: dns.h:358
void InitLocaltime()
Definition: prng.h:36
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:516
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:554
virtual void SetSystemResolvers()
Definition: dns.cc:1313
CaresResolver(const bool ipv4_only, const unsigned retries, const unsigned timeout_ms)
Definition: dns.cc:710
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:1041
string StripIp(const string &decorated_ip)
Definition: dns.cc:172
bool IsValid() const
Definition: dns.cc:292
virtual ~NormalResolver()
Definition: dns.cc:1360
static Failures CaresExtractIpv6(const unsigned char *abuf, int alen, vector< string > *addresses, unsigned *ttl, string *fqdn)
Definition: dns.cc:658
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:1318
Prng prng_
Definition: dns.h:277
std::string name_
Definition: dns.h:157
bool GetLineFile(FILE *f, std::string *line)
Definition: string.cc:379
virtual void SetSystemResolvers()
Definition: dns.cc:965
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
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:1137
Definition: dns.h:90
virtual bool SetSearchDomains(const std::vector< std::string > &domains)
Definition: dns.cc:1298
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:1084
bool IsEquivalent(const Host &other) const
Definition: dns.cc:269
virtual bool SetResolvers(const std::vector< std::string > &resolvers)
Definition: dns.cc:907
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:607
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:830
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:265
std::string ExtractHost(const std::string &url)
Definition: dns.cc:110
time_t deadline_
Definition: dns.h:135
virtual ~HostfileResolver()
Definition: dns.cc:1143
char * lookup_options_
Definition: dns.h:320
IpPreference
Definition: dns.h:46
static bool SortNameLength(const string &a, const string &b)
Definition: dns.cc:1072
unsigned retries() const
Definition: dns.h:203
void ParseHostFile()
Definition: dns.cc:1152
void WaitOnCares()
Definition: dns.cc:981
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:733
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:1330
virtual ~CaresResolver()
Definition: dns.cc:721
virtual void SetSystemSearchDomains()
Definition: dns.cc:971
unsigned max_ttl_
Definition: dns.h:272
std::set< std::string > ipv4_addresses_
Definition: dns.h:146
virtual void SetSystemSearchDomains()
Definition: dns.cc:1246
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:1290
string RewriteUrl(const string &url, const string &ip)
Definition: dns.cc:156
uint32_t Next(const uint64_t boundary)
Definition: prng.h:46
unsigned timeout_ms() const
Definition: dns.h:204