GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/network/dns.cc
Date: 2026-11-01 02:41:19
Exec Total Coverage
Lines: 616 698 88.3%
Branches: 513 806 63.6%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * The CernVM-FS name resolving uses objects that inherit from the Resolver
5 * interface. Resolvers implement a vector interface that resolves multiple
6 * names in parallel. Common cases such as IP addresses as names are handled
7 * by the base class -- Resolver implementations only have to resolve real host
8 * names to IPv4/6 addresses, using given search domains if necessary.
9 *
10 * Name resolving information is stored in Host objects. Host objects are
11 * immutable. They associate a hostname with sets of IPv4 and IPv6 addresses.
12 * They also carry the result of the name resolution attempt (success, failure)
13 * and the TTL in the form of a deadline. They are only created by a resolver
14 * and upon creation carry a unique id that corresponds to a particular name
15 * resolving attempt.
16 *
17 * The NormalResolver uses both the CaresResolver for DNS queries and the
18 * HostfileResolve for queries in /etc/hosts. If an entry is found in
19 * /etc/hosts, the CaresResolver is unused.
20 */
21
22 #include "dns.h"
23
24 #include <arpa/inet.h>
25 #include <arpa/nameser.h>
26 #include <errno.h>
27 #include <netdb.h>
28 #include <poll.h>
29 #include <unistd.h>
30
31 #include <algorithm>
32 #include <cassert>
33 #include <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 // c-ares 1.28.0 deprecates the ares_parse_* / ares_seach /
44 // / ares_getsock / ares_get_servers functions in favor of the new
45 // new ares_dns_record API (ares_dns_parse, ares_search_dnsrec,
46 // ares_get_servers_csv, ARES_OPT_EVENT_THREAD/ARES_OPT_SOCK_STATE_CB).
47 // The new API is not supported yet by AlmaLinux 8's system c-ares (1.13)
48 // though, so we will wait with the migration until Almalinux 8 is EOL (2029).
49 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
50
51 using namespace std; // NOLINT
52
53 namespace dns {
54
55 /**
56 * Sets pos_begin and pos_end to the first/last character of the host name in
57 * a string like http://<hostname>:<port>. Works also if the host name is an
58 * IPv4/6 address. Sets pos_begin and pos_end to 0 if url doesn't match the
59 * format.
60 */
61 12112 static void PinpointHostSubstr(const std::string &url,
62 unsigned *pos_begin,
63 unsigned *pos_end) {
64 12112 *pos_begin = *pos_end = 0;
65 12112 const unsigned len = url.size();
66 12112 unsigned i = 0;
67
68 // Search '//' in the url string and jump behind
69
2/2
✓ Branch 0 taken 74436 times.
✓ Branch 1 taken 3385 times.
77821 for (; i < len; ++i) {
70
8/8
✓ Branch 1 taken 9364 times.
✓ Branch 2 taken 65072 times.
✓ Branch 3 taken 8874 times.
✓ Branch 4 taken 490 times.
✓ Branch 6 taken 8727 times.
✓ Branch 7 taken 147 times.
✓ Branch 8 taken 8727 times.
✓ Branch 9 taken 65709 times.
74436 if ((url[i] == '/') && (i < len - 2) && (url[i + 1] == '/')) {
71 8727 i += 2;
72 8727 *pos_begin = i;
73 8727 break;
74 }
75 }
76
77 // Search '@' within the hostname part and jump behind if present
78
2/2
✓ Branch 0 taken 8727 times.
✓ Branch 1 taken 3385 times.
12112 if (*pos_begin > 0) {
79
2/2
✓ Branch 0 taken 101255 times.
✓ Branch 1 taken 7355 times.
108610 for (i = *pos_begin; i < len; ++i) {
80
2/2
✓ Branch 1 taken 1127 times.
✓ Branch 2 taken 100128 times.
101255 if (url[i] == '/') {
81 1127 break;
82
2/2
✓ Branch 1 taken 245 times.
✓ Branch 2 taken 99883 times.
100128 } else if (url[i] == '@') {
83 245 *pos_begin = ++i;
84 245 break;
85 }
86 }
87 }
88
89 // Find the end of the hostname part
90
2/2
✓ Branch 0 taken 8727 times.
✓ Branch 1 taken 3385 times.
12112 if (*pos_begin > 0) {
91 8727 bool in_ipv6 = (url[*pos_begin] == '[');
92
2/2
✓ Branch 0 taken 75510 times.
✓ Branch 1 taken 931 times.
76441 for (i = *pos_begin; i < len; ++i) {
93
2/2
✓ Branch 0 taken 5390 times.
✓ Branch 1 taken 70120 times.
75510 if (in_ipv6) {
94
2/2
✓ Branch 1 taken 4361 times.
✓ Branch 2 taken 1029 times.
5390 if (url[i] != ']')
95 4361 continue;
96 1029 in_ipv6 = false;
97 }
98
99
6/6
✓ Branch 1 taken 64039 times.
✓ Branch 2 taken 7110 times.
✓ Branch 4 taken 686 times.
✓ Branch 5 taken 63353 times.
✓ Branch 6 taken 7796 times.
✓ Branch 7 taken 63353 times.
71149 if ((url[i] == ':') || (url[i] == '/'))
100 7796 break;
101 }
102
2/2
✓ Branch 0 taken 8433 times.
✓ Branch 1 taken 294 times.
8727 if (!in_ipv6)
103 8433 *pos_end = i - 1;
104
105
2/2
✓ Branch 0 taken 882 times.
✓ Branch 1 taken 7845 times.
8727 if (*pos_end < *pos_begin)
106 882 *pos_end = *pos_begin = 0;
107 }
108 12112 }
109
110
111 /**
112 * Returns the host name from a string in the format
113 * http://<hostname>:<port>[/path]
114 * or an empty string if url doesn't match the format.
115 */
116 6205 std::string ExtractHost(const std::string &url) {
117 unsigned pos_begin;
118 unsigned pos_end;
119 6205 PinpointHostSubstr(url, &pos_begin, &pos_end);
120
2/2
✓ Branch 0 taken 2699 times.
✓ Branch 1 taken 3506 times.
6205 if (pos_begin == 0)
121
1/2
✓ Branch 2 taken 2699 times.
✗ Branch 3 not taken.
2699 return "";
122
1/2
✓ Branch 1 taken 3506 times.
✗ Branch 2 not taken.
3506 return url.substr(pos_begin, (pos_end - pos_begin) + 1);
123 }
124
125
126 /**
127 * Returns the port from a string in the format
128 * http://<hostname>:<port>/path
129 * or an empty string if url doesn't match the format.
130 */
131 4827 std::string ExtractPort(const std::string &url) {
132 unsigned pos_begin;
133 unsigned pos_end;
134 4827 PinpointHostSubstr(url, &pos_begin, &pos_end);
135
9/10
✓ Branch 0 taken 3798 times.
✓ Branch 1 taken 1029 times.
✓ Branch 3 taken 3455 times.
✓ Branch 4 taken 343 times.
✓ Branch 6 taken 3455 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 147 times.
✓ Branch 9 taken 3308 times.
✓ Branch 10 taken 1519 times.
✓ Branch 11 taken 3308 times.
4827 if (pos_begin == 0 || pos_end + 2 >= url.size() || url.at(pos_end + 1) != ':')
136
1/2
✓ Branch 2 taken 1519 times.
✗ Branch 3 not taken.
1519 return "";
137
138 // Do not include path
139 3308 const std::size_t pos_port = url.find("/", pos_end);
140 3308 std::string retme;
141
2/2
✓ Branch 0 taken 3014 times.
✓ Branch 1 taken 294 times.
3308 if (pos_port == std::string::npos)
142
1/2
✓ Branch 1 taken 3014 times.
✗ Branch 2 not taken.
3014 retme = url.substr(pos_end + 2);
143 else
144
1/2
✓ Branch 1 taken 294 times.
✗ Branch 2 not taken.
294 retme = url.substr(pos_end + 2, pos_port - pos_end - 2);
145
146 // Port is an integer
147
2/2
✓ Branch 4 taken 11223 times.
✓ Branch 5 taken 3112 times.
14335 for (std::string::iterator it = retme.begin(); it != retme.end(); ++it)
148
2/2
✓ Branch 1 taken 196 times.
✓ Branch 2 taken 11027 times.
11223 if (isdigit(*it) == 0)
149
1/2
✓ Branch 2 taken 196 times.
✗ Branch 3 not taken.
196 return "";
150
151 3112 return retme;
152 3308 }
153
154
155 /**
156 * Replaces the host name in the url with the given IP address. If it is an
157 * IPv6 address, it has to be in brackets. If the input is not a valid URL,
158 * it is returned unmodified.
159 */
160 1080 string RewriteUrl(const string &url, const string &ip) {
161 unsigned pos_begin;
162 unsigned pos_end;
163 1080 PinpointHostSubstr(url, &pos_begin, &pos_end);
164
2/2
✓ Branch 0 taken 539 times.
✓ Branch 1 taken 541 times.
1080 if (pos_begin == 0)
165
1/2
✓ Branch 1 taken 539 times.
✗ Branch 2 not taken.
539 return url;
166
167
1/2
✓ Branch 1 taken 541 times.
✗ Branch 2 not taken.
541 string result = url;
168
1/2
✓ Branch 1 taken 541 times.
✗ Branch 2 not taken.
541 result.replace(pos_begin, (pos_end - pos_begin) + 1, ip);
169 541 return result;
170 541 }
171
172
173 /**
174 * Removes the brackets from IPv6 addresses. Leaves IPv4 addresses unchanged.
175 */
176 392 string StripIp(const string &decorated_ip) {
177
2/2
✓ Branch 1 taken 343 times.
✓ Branch 2 taken 49 times.
392 if (!decorated_ip.empty()) {
178 343 if ((decorated_ip[0] == '[')
179
6/6
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 147 times.
✓ Branch 4 taken 98 times.
✓ Branch 5 taken 98 times.
✓ Branch 6 taken 98 times.
✓ Branch 7 taken 245 times.
343 && (decorated_ip[decorated_ip.length() - 1] == ']')) {
180 98 return decorated_ip.substr(1, decorated_ip.length() - 2);
181 }
182 }
183 294 return decorated_ip;
184 }
185
186
187 /**
188 * Adds http:// if it is missing from proxy
189 */
190 5255 std::string AddDefaultScheme(const std::string &proxy) {
191 5255 const bool ignore_case = true;
192
4/12
✓ Branch 1 taken 5255 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5255 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 5108 times.
✓ Branch 8 taken 147 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
10510 if (HasPrefix(proxy, "http://", ignore_case)
193
9/19
✓ Branch 2 taken 5006 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5006 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4957 times.
✓ Branch 8 taken 49 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 196 times.
✓ Branch 11 taken 4761 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 5006 times.
✓ Branch 14 taken 249 times.
✓ Branch 16 taken 5255 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
10261 || HasPrefix(proxy, "https://", ignore_case) || (proxy == "DIRECT")
194
7/9
✓ Branch 2 taken 5255 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5006 times.
✓ Branch 5 taken 249 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 49 times.
✓ Branch 8 taken 147 times.
✓ Branch 9 taken 5006 times.
✓ Branch 10 taken 249 times.
15516 || proxy.empty()) {
195 5108 return proxy;
196 }
197 147 return "http://" + proxy;
198 }
199
200
201 //------------------------------------------------------------------------------
202
203
204 std::atomic<int64_t> Host::global_id_(0);
205
206 541 const set<string> &Host::ViewBestAddresses(IpPreference preference) const {
207
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 147 times.
294 if (((preference == kIpPreferSystem) || (preference == kIpPreferV4))
208
6/6
✓ Branch 0 taken 294 times.
✓ Branch 1 taken 247 times.
✓ Branch 3 taken 296 times.
✓ Branch 4 taken 98 times.
✓ Branch 5 taken 296 times.
✓ Branch 6 taken 245 times.
835 && HasIpv4()) {
209 296 return ipv4_addresses_;
210 }
211
6/6
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 98 times.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 98 times.
✓ Branch 5 taken 49 times.
✓ Branch 6 taken 196 times.
245 if ((preference == kIpPreferV6) && !HasIpv6())
212 49 return ipv4_addresses_;
213 196 return ipv6_addresses_;
214 }
215
216
217 24119 void Host::CopyFrom(const Host &other) {
218 24119 deadline_ = other.deadline_;
219 24119 id_ = other.id_;
220 24119 ipv4_addresses_ = other.ipv4_addresses_;
221 24119 ipv6_addresses_ = other.ipv6_addresses_;
222 24119 name_ = other.name_;
223 24119 status_ = other.status_;
224 24119 }
225
226
227 /**
228 * Creates a copy of the original host with a new ID and sets a new dealine
229 * given in seconds from the current time.
230 */
231 49 Host Host::ExtendDeadline(const Host &original, unsigned seconds_from_now) {
232 49 Host new_host(original);
233 49 new_host.id_ = global_id_.fetch_add(1);
234 49 new_host.deadline_ = time(NULL) + seconds_from_now;
235 49 return new_host;
236 }
237
238
239 /**
240 * All fields except the unique id_ are set by the resolver. Host objects
241 * can be copied around but only the resolver can create valid, new objects.
242 */
243 13931 Host::Host()
244 13931 : deadline_(0)
245 13931 , id_(global_id_.fetch_add(1))
246 13931 , status_(kFailNotYetResolved) { }
247
248
249
1/2
✓ Branch 4 taken 18655 times.
✗ Branch 5 not taken.
18655 Host::Host(const Host &other) { CopyFrom(other); }
250
251
252 5489 Host &Host::operator=(const Host &other) {
253
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 5464 times.
5489 if (&other == this)
254 25 return *this;
255 5464 CopyFrom(other);
256 5464 return *this;
257 }
258
259
260 /**
261 * Compares the name and the resolved addresses independent of deadlines. Used
262 * to decide if the current proxy list needs to be changed after re-resolving
263 * a host name.
264 */
265 1078 bool Host::IsEquivalent(const Host &other) const {
266
2/2
✓ Branch 0 taken 882 times.
✓ Branch 1 taken 49 times.
931 return (status_ == kFailOk) && (other.status_ == kFailOk)
267
3/4
✓ Branch 1 taken 882 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 784 times.
✓ Branch 5 taken 98 times.
882 && (name_ == other.name_) && (ipv4_addresses_ == other.ipv4_addresses_)
268
4/4
✓ Branch 0 taken 931 times.
✓ Branch 1 taken 147 times.
✓ Branch 3 taken 588 times.
✓ Branch 4 taken 196 times.
2009 && (ipv6_addresses_ == other.ipv6_addresses_);
269 }
270
271
272 /**
273 * Compares the TTL from a provious call to time() with the current time.
274 */
275 786 bool Host::IsExpired() const {
276 786 const time_t now = time(NULL);
277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 786 times.
786 assert(now != static_cast<time_t>(-1));
278 786 return deadline_ < now;
279 }
280
281
282 /**
283 * A host object is valid after it has been successfully resolved and until the
284 * DNS ttl expires. Successful name resolution means that there is at least
285 * one IP address.
286 */
287 784 bool Host::IsValid() const {
288
2/2
✓ Branch 0 taken 294 times.
✓ Branch 1 taken 490 times.
784 if (status_ != kFailOk)
289 294 return false;
290
291
3/4
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 392 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 98 times.
490 assert(!ipv4_addresses_.empty() || !ipv6_addresses_.empty());
292 490 return !IsExpired();
293 }
294
295
296 //------------------------------------------------------------------------------
297
298
299 /**
300 * Basic input validation to ensure that this could syntactically represent a
301 * valid IPv4 address.
302 */
303 38331 bool Resolver::IsIpv4Address(const string &address) {
304 // Are there any unexpected characters?
305
2/4
✓ Branch 2 taken 38331 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 38331 times.
✗ Branch 6 not taken.
76662 const sanitizer::InputSanitizer sanitizer("09 .");
306
3/4
✓ Branch 1 taken 38331 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15457 times.
✓ Branch 4 taken 22874 times.
38331 if (!sanitizer.IsValid(address))
307 15457 return false;
308
309 // 4 octets in the range 0-255?
310
1/2
✓ Branch 1 taken 22874 times.
✗ Branch 2 not taken.
22874 vector<string> octets = SplitString(address, '.');
311
2/2
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 22825 times.
22874 if (octets.size() != 4)
312 49 return false;
313
2/2
✓ Branch 0 taken 91300 times.
✓ Branch 1 taken 22776 times.
114076 for (unsigned i = 0; i < 4; ++i) {
314
1/2
✓ Branch 2 taken 91300 times.
✗ Branch 3 not taken.
91300 const uint64_t this_octet = String2Uint64(octets[i]);
315
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 91251 times.
91300 if (this_octet > 255)
316 49 return false;
317 }
318
319 22776 return true;
320 38331 }
321
322
323 /**
324 * Basic input validation to ensure that this could syntactically represent a
325 * valid IPv6 address.
326 */
327 2058 bool Resolver::IsIpv6Address(const string &address) {
328 // Are there any unexpected characters?
329
2/4
✓ Branch 2 taken 2058 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2058 times.
✗ Branch 6 not taken.
4116 const sanitizer::InputSanitizer sanitizer("09 af AF :");
330
1/2
✓ Branch 1 taken 2058 times.
✗ Branch 2 not taken.
4116 return sanitizer.IsValid(address);
331 2058 }
332
333
334 25812 Resolver::Resolver(const bool ipv4_only,
335 const unsigned retries,
336 25812 const unsigned timeout_ms)
337 25812 : ipv4_only_(ipv4_only)
338 25812 , retries_(retries)
339 25812 , timeout_ms_(timeout_ms)
340 25812 , throttle_(0)
341 25812 , min_ttl_(kDefaultMinTtl)
342 25812 , max_ttl_(kDefaultMaxTtl) {
343 25812 prng_.InitLocaltime();
344 25812 }
345
346
347 /**
348 * Wrapper around the vector interface.
349 */
350 3484 Host Resolver::Resolve(const string &name) {
351 3484 vector<string> names;
352
1/2
✓ Branch 1 taken 3484 times.
✗ Branch 2 not taken.
3484 names.push_back(name);
353 3484 vector<Host> hosts;
354
1/2
✓ Branch 1 taken 3484 times.
✗ Branch 2 not taken.
3484 ResolveMany(names, &hosts);
355
1/2
✓ Branch 2 taken 3484 times.
✗ Branch 3 not taken.
6968 return hosts[0];
356 3484 }
357
358
359 /**
360 * Calls the overwritten concrete resolver, verifies the sanity of the returned
361 * addresses and constructs the Host objects in the same order as the names.
362 */
363 6062 void Resolver::ResolveMany(const vector<string> &names, vector<Host> *hosts) {
364 6062 const unsigned num = names.size();
365
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6062 times.
6062 if (num == 0)
366 ✗ return;
367
368
1/2
✓ Branch 2 taken 6062 times.
✗ Branch 3 not taken.
6062 vector<vector<string> > ipv4_addresses(num);
369
1/2
✓ Branch 2 taken 6062 times.
✗ Branch 3 not taken.
6062 vector<vector<string> > ipv6_addresses(num);
370
1/2
✓ Branch 2 taken 6062 times.
✗ Branch 3 not taken.
6062 vector<Failures> failures(num);
371
1/2
✓ Branch 2 taken 6062 times.
✗ Branch 3 not taken.
6062 vector<unsigned> ttls(num);
372
1/2
✓ Branch 2 taken 6062 times.
✗ Branch 3 not taken.
6062 vector<string> fqdns(num);
373
1/2
✓ Branch 2 taken 6062 times.
✗ Branch 3 not taken.
6062 vector<bool> skip(num);
374
375 // Deal with special names: empty, IPv4, IPv6
376
2/2
✓ Branch 0 taken 7312 times.
✓ Branch 1 taken 6062 times.
13374 for (unsigned i = 0; i < num; ++i) {
377
2/2
✓ Branch 2 taken 2405 times.
✓ Branch 3 taken 4907 times.
7312 if (names[i].empty()) {
378
1/2
✓ Branch 1 taken 2405 times.
✗ Branch 2 not taken.
2405 LogCvmfs(kLogDns, kLogDebug, "empty hostname");
379 2405 Host invalid_host;
380
1/2
✓ Branch 1 taken 2405 times.
✗ Branch 2 not taken.
2405 invalid_host.name_ = "";
381 2405 invalid_host.status_ = kFailInvalidHost;
382
1/2
✓ Branch 1 taken 2405 times.
✗ Branch 2 not taken.
2405 hosts->push_back(invalid_host);
383
1/2
✓ Branch 1 taken 2405 times.
✗ Branch 2 not taken.
2405 skip[i] = true;
384
3/4
✓ Branch 3 taken 4907 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 840 times.
✓ Branch 6 taken 4067 times.
7312 } else if (IsIpv4Address(names[i])) {
385
1/2
✓ Branch 3 taken 840 times.
✗ Branch 4 not taken.
840 LogCvmfs(kLogDns, kLogDebug, "IPv4 address %s", names[i].c_str());
386 840 Host ipv4_host;
387
1/2
✓ Branch 2 taken 840 times.
✗ Branch 3 not taken.
840 ipv4_host.name_ = names[i];
388 840 ipv4_host.status_ = kFailOk;
389
1/2
✓ Branch 2 taken 840 times.
✗ Branch 3 not taken.
840 ipv4_host.ipv4_addresses_.insert(names[i]);
390 840 ipv4_host.deadline_ = time(NULL) + max_ttl_;
391
1/2
✓ Branch 1 taken 840 times.
✗ Branch 2 not taken.
840 hosts->push_back(ipv4_host);
392
1/2
✓ Branch 1 taken 840 times.
✗ Branch 2 not taken.
840 skip[i] = true;
393
2/2
✓ Branch 5 taken 196 times.
✓ Branch 6 taken 3626 times.
8729 } else if ((names[i].length() >= 3) && (names[i][0] == '[')
394
5/6
✓ Branch 0 taken 3822 times.
✓ Branch 1 taken 245 times.
✓ Branch 6 taken 196 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 196 times.
✓ Branch 9 taken 3871 times.
7889 && (names[i][names[i].length() - 1] == ']')) {
395
1/2
✓ Branch 3 taken 196 times.
✗ Branch 4 not taken.
196 LogCvmfs(kLogDns, kLogDebug, "IPv6 address %s", names[i].c_str());
396 196 Host ipv6_host;
397
1/2
✓ Branch 2 taken 196 times.
✗ Branch 3 not taken.
196 ipv6_host.name_ = names[i];
398 196 ipv6_host.status_ = kFailOk;
399
1/2
✓ Branch 2 taken 196 times.
✗ Branch 3 not taken.
196 ipv6_host.ipv6_addresses_.insert(names[i]);
400 196 ipv6_host.deadline_ = time(NULL) + max_ttl_;
401
1/2
✓ Branch 1 taken 196 times.
✗ Branch 2 not taken.
196 hosts->push_back(ipv6_host);
402
1/2
✓ Branch 1 taken 196 times.
✗ Branch 2 not taken.
196 skip[i] = true;
403 196 } else {
404
1/2
✓ Branch 2 taken 3871 times.
✗ Branch 3 not taken.
3871 hosts->push_back(Host());
405
1/2
✓ Branch 1 taken 3871 times.
✗ Branch 2 not taken.
3871 skip[i] = false;
406 }
407 }
408
409
1/2
✓ Branch 1 taken 6062 times.
✗ Branch 2 not taken.
6062 DoResolve(names, skip, &ipv4_addresses, &ipv6_addresses, &failures, &ttls,
410 &fqdns);
411
412 // Construct host objects
413
2/2
✓ Branch 0 taken 7312 times.
✓ Branch 1 taken 6062 times.
13374 for (unsigned i = 0; i < num; ++i) {
414
3/5
✓ Branch 1 taken 7312 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3441 times.
✓ Branch 5 taken 3871 times.
7312 if (skip[i])
415 4372 continue;
416
417 3871 Host host;
418
1/2
✓ Branch 2 taken 3871 times.
✗ Branch 3 not taken.
3871 host.name_ = fqdns[i];
419 3871 host.status_ = failures[i];
420
421 3871 unsigned effective_ttl = ttls[i];
422
2/2
✓ Branch 0 taken 980 times.
✓ Branch 1 taken 2891 times.
3871 if (effective_ttl < min_ttl_) {
423 980 effective_ttl = min_ttl_;
424
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 2793 times.
2891 } else if (effective_ttl > max_ttl_) {
425 98 effective_ttl = max_ttl_;
426 }
427 3871 host.deadline_ = time(NULL) + effective_ttl;
428
429
2/2
✓ Branch 0 taken 931 times.
✓ Branch 1 taken 2940 times.
3871 if (host.status_ != kFailOk) {
430
1/2
✓ Branch 3 taken 931 times.
✗ Branch 4 not taken.
1862 LogCvmfs(kLogDns, kLogDebug, "failed to resolve %s - %d (%s), ttl %u",
431 931 names[i].c_str(), host.status_, Code2Ascii(host.status_),
432 effective_ttl);
433
1/2
✓ Branch 2 taken 931 times.
✗ Branch 3 not taken.
931 (*hosts)[i] = host;
434 931 continue;
435 }
436
437 // Verify addresses and make them readily available for curl
438
2/2
✓ Branch 2 taken 3332 times.
✓ Branch 3 taken 2940 times.
6272 for (unsigned j = 0; j < ipv4_addresses[i].size(); ++j) {
439
3/4
✓ Branch 3 taken 3332 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 196 times.
✓ Branch 6 taken 3136 times.
3332 if (!IsIpv4Address(ipv4_addresses[i][j])) {
440
1/4
✓ Branch 3 taken 196 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
392 LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn,
441 "host name %s resolves to invalid IPv4 address %s",
442 392 names[i].c_str(), ipv4_addresses[i][j].c_str());
443 196 continue;
444 }
445
2/2
✓ Branch 2 taken 2989 times.
✓ Branch 3 taken 147 times.
3136 if (names[i] == host.name_) {
446
1/2
✓ Branch 4 taken 2989 times.
✗ Branch 5 not taken.
2989 LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s", names[i].c_str(),
447 2989 ipv4_addresses[i][j].c_str());
448 } else {
449
1/2
✓ Branch 4 taken 147 times.
✗ Branch 5 not taken.
294 LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s -> %s",
450 147 names[i].c_str(), host.name_.c_str(),
451 147 ipv4_addresses[i][j].c_str());
452 }
453
1/2
✓ Branch 3 taken 3136 times.
✗ Branch 4 not taken.
3136 host.ipv4_addresses_.insert(ipv4_addresses[i][j]);
454 }
455
456
2/2
✓ Branch 2 taken 2058 times.
✓ Branch 3 taken 2940 times.
4998 for (unsigned j = 0; j < ipv6_addresses[i].size(); ++j) {
457
3/4
✓ Branch 3 taken 2058 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 98 times.
✓ Branch 6 taken 1960 times.
2058 if (!IsIpv6Address(ipv6_addresses[i][j])) {
458
1/4
✓ Branch 3 taken 98 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
196 LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn,
459 "host name %s resolves to invalid IPv6 address %s",
460 196 names[i].c_str(), ipv6_addresses[i][j].c_str());
461 98 continue;
462 }
463 // For URLs we need brackets around IPv6 addresses
464
2/2
✓ Branch 2 taken 1911 times.
✓ Branch 3 taken 49 times.
1960 if (names[i] == host.name_) {
465
1/2
✓ Branch 4 taken 1911 times.
✗ Branch 5 not taken.
1911 LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s", names[i].c_str(),
466 1911 ipv6_addresses[i][j].c_str());
467 } else {
468
1/2
✓ Branch 4 taken 49 times.
✗ Branch 5 not taken.
98 LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s -> %s",
469 49 names[i].c_str(), host.name_.c_str(),
470 49 ipv6_addresses[i][j].c_str());
471 }
472
3/6
✓ Branch 3 taken 1960 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1960 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1960 times.
✗ Branch 10 not taken.
1960 host.ipv6_addresses_.insert("[" + ipv6_addresses[i][j] + "]");
473 }
474
475
6/6
✓ Branch 1 taken 245 times.
✓ Branch 2 taken 2695 times.
✓ Branch 4 taken 147 times.
✓ Branch 5 taken 98 times.
✓ Branch 6 taken 147 times.
✓ Branch 7 taken 2793 times.
2940 if (host.ipv4_addresses_.empty() && host.ipv6_addresses_.empty()) {
476
1/2
✓ Branch 2 taken 147 times.
✗ Branch 3 not taken.
147 LogCvmfs(kLogDns, kLogDebug, "no addresses returned for %s",
477 147 names[i].c_str());
478 147 host.status_ = kFailNoAddress;
479 }
480
481 // Remove surplus IP addresses
482
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 2891 times.
2940 if (throttle_ > 0) {
483
2/2
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 49 times.
147 while (host.ipv4_addresses_.size() > throttle_) {
484 98 const unsigned random = prng_.Next(host.ipv4_addresses_.size());
485 98 set<string>::iterator rnd_itr = host.ipv4_addresses_.begin();
486
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 std::advance(rnd_itr, random);
487
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 host.ipv4_addresses_.erase(rnd_itr);
488 }
489
2/2
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 49 times.
147 while (host.ipv6_addresses_.size() > throttle_) {
490 98 const unsigned random = prng_.Next(host.ipv6_addresses_.size());
491 98 set<string>::iterator rnd_itr = host.ipv6_addresses_.begin();
492
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 std::advance(rnd_itr, random);
493
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 host.ipv6_addresses_.erase(rnd_itr);
494 }
495 }
496
497
1/2
✓ Branch 2 taken 2940 times.
✗ Branch 3 not taken.
2940 (*hosts)[i] = host;
498
2/2
✓ Branch 1 taken 2940 times.
✓ Branch 2 taken 931 times.
3871 }
499 6062 }
500
501
502 //------------------------------------------------------------------------------
503
504
505 namespace {
506
507 enum ResourceRecord {
508 kRrA = 0,
509 kRrAaaa,
510 };
511
512 /**
513 * Used to transport a name resolving request across the asynchronous c-ares
514 * interface. The QueryInfo objects are used for both IPv4 and IPv6 requests.
515 * The addresses are entered directly via pointers, ttls and fqdns are later
516 * merged into a single response (for IPv4/IPv6).
517 */
518 struct QueryInfo {
519 2989 QueryInfo(vector<string> *a, const string &n, const ResourceRecord r)
520 2989 : addresses(a)
521 2989 , complete(false)
522 2989 , fqdn(n)
523
1/2
✓ Branch 1 taken 2989 times.
✗ Branch 2 not taken.
2989 , name(n)
524 2989 , record(r)
525 2989 , status(kFailOther)
526 2989 , ttl(0) { }
527
528 vector<string> *addresses;
529 bool complete;
530 string fqdn;
531 string name;
532 ResourceRecord record;
533 Failures status;
534 unsigned ttl;
535 };
536
537 } // namespace
538
539
540 static Failures CaresExtractIpv4(const unsigned char *abuf, int alen,
541 vector<string> *addresses, unsigned *ttl,
542 string *fqdn);
543 static Failures CaresExtractIpv6(const unsigned char *abuf, int alen,
544 vector<string> *addresses, unsigned *ttl,
545 string *fqdn);
546
547 /**
548 * Called when a DNS query returns or times out. Sets the return status and the
549 * IP addresses (if successful) in the QueryInfo object.
550 */
551 2989 static void CallbackCares(void *arg, int status, int timeouts_ms,
552 unsigned char *abuf, int alen) {
553 2989 QueryInfo *info = reinterpret_cast<QueryInfo *>(arg);
554
555 2989 info->complete = true;
556
5/7
✓ Branch 0 taken 2107 times.
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 588 times.
✓ Branch 4 taken 98 times.
✓ Branch 5 taken 98 times.
✗ Branch 6 not taken.
2989 switch (status) {
557 2107 case ARES_SUCCESS:
558 Failures retval;
559
2/3
✓ Branch 0 taken 1078 times.
✓ Branch 1 taken 1029 times.
✗ Branch 2 not taken.
2107 switch (info->record) {
560 1078 case kRrA:
561 1078 retval = CaresExtractIpv4(abuf, alen, info->addresses, &info->ttl,
562 &info->fqdn);
563 1078 break;
564 1029 case kRrAaaa:
565 1029 retval = CaresExtractIpv6(abuf, alen, info->addresses, &info->ttl,
566 &info->fqdn);
567 1029 break;
568 ✗ default:
569 // Never here.
570 ✗ PANIC(NULL);
571 }
572 2107 info->status = retval;
573 2107 break;
574 98 case ARES_ENODATA:
575 98 info->status = kFailUnknownHost;
576 98 break;
577 ✗ case ARES_EFORMERR:
578 ✗ info->status = kFailMalformed;
579 ✗ break;
580 588 case ARES_ENOTFOUND:
581 588 info->status = kFailUnknownHost;
582 588 break;
583 98 case ARES_ETIMEOUT:
584 98 info->status = kFailTimeout;
585 98 break;
586 98 case ARES_ECONNREFUSED:
587 98 info->status = kFailInvalidResolvers;
588 98 break;
589 ✗ default:
590 ✗ info->status = kFailOther;
591 }
592 2989 }
593
594
595 /**
596 * Extracts IPv4 addresses from an A record return in c-ares. TTLs are
597 * merged to a single one, representing the minimum.
598 */
599 1078 static Failures CaresExtractIpv4(const unsigned char *abuf,
600 int alen,
601 vector<string> *addresses,
602 unsigned *ttl,
603 string *fqdn) {
604 1078 struct hostent *host_entry = NULL;
605 struct ares_addrttl records[CaresResolver::kMaxAddresses];
606 1078 int naddrttls = CaresResolver::kMaxAddresses;
607
1/2
✓ Branch 1 taken 1078 times.
✗ Branch 2 not taken.
1078 const int retval = ares_parse_a_reply(abuf, alen, &host_entry, records,
608 &naddrttls);
609
610
1/3
✓ Branch 0 taken 1078 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
1078 switch (retval) {
611 1078 case ARES_SUCCESS:
612
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1078 times.
1078 if (host_entry == NULL)
613 ✗ return kFailMalformed;
614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1078 times.
1078 if (host_entry->h_name == NULL) {
615 ✗ ares_free_hostent(host_entry);
616 ✗ return kFailMalformed;
617 }
618
1/2
✓ Branch 2 taken 1078 times.
✗ Branch 3 not taken.
1078 *fqdn = string(host_entry->h_name);
619
1/2
✓ Branch 1 taken 1078 times.
✗ Branch 2 not taken.
1078 ares_free_hostent(host_entry);
620
621
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1078 times.
1078 if (naddrttls <= 0)
622 ✗ return kFailMalformed;
623 1078 *ttl = unsigned(-1);
624
2/2
✓ Branch 0 taken 1078 times.
✓ Branch 1 taken 1078 times.
2156 for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) {
625
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1078 times.
1078 if (records[i].ttl < 0)
626 ✗ continue;
627 1078 *ttl = std::min(unsigned(records[i].ttl), *ttl);
628
629 char addrstr[INET_ADDRSTRLEN];
630 1078 const void *retval_p = inet_ntop(AF_INET, &(records[i].ipaddr), addrstr,
631 INET_ADDRSTRLEN);
632
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1078 times.
1078 if (!retval_p)
633 ✗ continue;
634
2/4
✓ Branch 2 taken 1078 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1078 times.
✗ Branch 6 not taken.
1078 addresses->push_back(addrstr);
635 }
636
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1078 times.
1078 if (addresses->empty())
637 ✗ return kFailMalformed;
638 1078 return kFailOk;
639 ✗ case ARES_EBADRESP:
640 // Fall through
641 case ARES_ENODATA:
642 ✗ return kFailMalformed;
643 ✗ default:
644 ✗ return kFailOther;
645 }
646 }
647
648
649 1029 static Failures CaresExtractIpv6(const unsigned char *abuf,
650 int alen,
651 vector<string> *addresses,
652 unsigned *ttl,
653 string *fqdn) {
654 1029 struct hostent *host_entry = NULL;
655 struct ares_addr6ttl records[CaresResolver::kMaxAddresses];
656 1029 int naddrttls = CaresResolver::kMaxAddresses;
657
1/2
✓ Branch 1 taken 1029 times.
✗ Branch 2 not taken.
1029 const int retval = ares_parse_aaaa_reply(abuf, alen, &host_entry, records,
658 &naddrttls);
659
660
1/3
✓ Branch 0 taken 1029 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
1029 switch (retval) {
661 1029 case ARES_SUCCESS:
662
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1029 times.
1029 if (host_entry == NULL)
663 ✗ return kFailMalformed;
664
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1029 times.
1029 if (host_entry->h_name == NULL) {
665 ✗ ares_free_hostent(host_entry);
666 ✗ return kFailMalformed;
667 }
668
1/2
✓ Branch 2 taken 1029 times.
✗ Branch 3 not taken.
1029 *fqdn = string(host_entry->h_name);
669
1/2
✓ Branch 1 taken 1029 times.
✗ Branch 2 not taken.
1029 ares_free_hostent(host_entry);
670
671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1029 times.
1029 if (naddrttls <= 0)
672 ✗ return kFailMalformed;
673 1029 *ttl = unsigned(-1);
674
2/2
✓ Branch 0 taken 1029 times.
✓ Branch 1 taken 1029 times.
2058 for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) {
675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1029 times.
1029 if (records[i].ttl < 0)
676 ✗ continue;
677 1029 *ttl = std::min(unsigned(records[i].ttl), *ttl);
678
679 char addrstr[INET6_ADDRSTRLEN];
680 1029 const void *retval_p = inet_ntop(AF_INET6, &(records[i].ip6addr),
681 addrstr, INET6_ADDRSTRLEN);
682
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1029 times.
1029 if (!retval_p)
683 ✗ continue;
684
2/4
✓ Branch 2 taken 1029 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1029 times.
✗ Branch 6 not taken.
1029 addresses->push_back(addrstr);
685 }
686
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1029 times.
1029 if (addresses->empty())
687 ✗ return kFailMalformed;
688 1029 return kFailOk;
689 ✗ case ARES_EBADRESP:
690 // Fall through
691 case ARES_ENODATA:
692 ✗ return kFailMalformed;
693 ✗ default:
694 ✗ return kFailOther;
695 }
696 }
697
698
699 11101 CaresResolver::CaresResolver(const bool ipv4_only,
700 const unsigned retries,
701 11101 const unsigned timeout_ms)
702 : Resolver(ipv4_only, retries, timeout_ms)
703 11101 , channel_(NULL)
704 11101 , lookup_options_(strdup("b")) { }
705
706
707 42060 CaresResolver::~CaresResolver() {
708
1/2
✓ Branch 0 taken 10515 times.
✗ Branch 1 not taken.
21030 if (channel_) {
709 21030 ares_destroy(*channel_);
710 21030 free(channel_);
711 }
712 21030 free(lookup_options_);
713 42060 }
714
715
716 /**
717 * Returns a CaresResolver readily initialized, or NULL if an error occurs.
718 */
719 11101 CaresResolver *CaresResolver::Create(const bool ipv4_only,
720 const unsigned retries,
721 const unsigned timeout_ms) {
722 int retval;
723
2/2
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 11052 times.
11101 if (getenv("HOSTALIASES") == NULL) {
724 49 retval = setenv("HOSTALIASES", "/etc/hosts", 1);
725
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
49 assert(retval == 0);
726 }
727
728
1/2
✓ Branch 1 taken 11101 times.
✗ Branch 2 not taken.
11101 CaresResolver *resolver = new CaresResolver(ipv4_only, retries, timeout_ms);
729 11101 resolver->channel_ = reinterpret_cast<ares_channel *>(
730 11101 smalloc(sizeof(ares_channel)));
731 11101 memset(resolver->channel_, 0, sizeof(ares_channel));
732
733 struct ares_addr_node *addresses;
734 struct ares_addr_node *iter;
735 struct ares_options options;
736 int optmask;
737 11101 memset(&options, 0, sizeof(options));
738 11101 options.timeout = timeout_ms;
739 11101 options.tries = 1 + retries;
740 11101 options.lookups = resolver->lookup_options_;
741 11101 optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_LOOKUPS;
742
1/2
✓ Branch 1 taken 11101 times.
✗ Branch 2 not taken.
11101 retval = ares_init_options(resolver->channel_, &options, optmask);
743
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11101 times.
11101 if (retval != ARES_SUCCESS)
744 ✗ goto create_fail;
745
746 // Save search domains
747
1/2
✓ Branch 1 taken 11101 times.
✗ Branch 2 not taken.
11101 retval = ares_save_options(*resolver->channel_, &options, &optmask);
748
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11101 times.
11101 if (retval != ARES_SUCCESS)
749 ✗ goto create_fail;
750
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11101 times.
11101 for (int i = 0; i < options.ndomains; ++i) {
751 ✗ resolver->domains_.push_back(options.domains[i]);
752 }
753
1/2
✓ Branch 1 taken 11101 times.
✗ Branch 2 not taken.
11101 ares_destroy_options(&options);
754
1/2
✓ Branch 1 taken 11101 times.
✗ Branch 2 not taken.
11101 resolver->system_domains_ = resolver->domains_;
755
756 // Save the system default resolvers
757 11101 addresses = NULL;
758
1/2
✓ Branch 1 taken 11101 times.
✗ Branch 2 not taken.
11101 retval = ares_get_servers(*resolver->channel_, &addresses);
759
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11101 times.
11101 if (retval != ARES_SUCCESS)
760 ✗ goto create_fail;
761 11101 iter = addresses;
762
2/2
✓ Branch 0 taken 55505 times.
✓ Branch 1 taken 11101 times.
66606 while (iter) {
763
2/3
✓ Branch 0 taken 33303 times.
✓ Branch 1 taken 22202 times.
✗ Branch 2 not taken.
55505 switch (iter->family) {
764 33303 case AF_INET: {
765 char addrstr[INET_ADDRSTRLEN];
766 33303 const void *retval_p = inet_ntop(AF_INET, &(iter->addr), addrstr,
767 INET_ADDRSTRLEN);
768
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33303 times.
33303 if (!retval_p) {
769 ✗ LogCvmfs(kLogDns, kLogDebug | kLogSyslogErr,
770 "invalid system name resolver");
771 } else {
772
3/6
✓ Branch 2 taken 33303 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 33303 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 33303 times.
✗ Branch 9 not taken.
33303 resolver->resolvers_.push_back(string(addrstr) + ":53");
773 }
774 33303 break;
775 }
776 22202 case AF_INET6: {
777 char addrstr[INET6_ADDRSTRLEN];
778 22202 const void *retval_p = inet_ntop(AF_INET6, &(iter->addr), addrstr,
779 INET6_ADDRSTRLEN);
780
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22202 times.
22202 if (!retval_p) {
781 ✗ LogCvmfs(kLogDns, kLogDebug | kLogSyslogErr,
782 "invalid system name resolver");
783 } else {
784
4/8
✓ Branch 2 taken 22202 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 22202 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 22202 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 22202 times.
✗ Branch 12 not taken.
22202 resolver->resolvers_.push_back("[" + string(addrstr) + "]:53");
785 }
786 22202 break;
787 }
788 ✗ default:
789 // Never here.
790 ✗ PANIC(NULL);
791 }
792 55505 iter = iter->next;
793 }
794
1/2
✓ Branch 1 taken 11101 times.
✗ Branch 2 not taken.
11101 ares_free_data(addresses);
795
1/2
✓ Branch 1 taken 11101 times.
✗ Branch 2 not taken.
11101 resolver->system_resolvers_ = resolver->resolvers_;
796
797 11101 return resolver;
798
799 ✗ create_fail:
800 ✗ LogCvmfs(kLogDns, kLogDebug | kLogSyslogErr,
801 "failed to initialize c-ares resolver (%d - %s)", retval,
802 ares_strerror(retval));
803 ✗ free(resolver->channel_);
804 ✗ resolver->channel_ = NULL;
805 ✗ delete resolver;
806 ✗ return NULL;
807 }
808
809
810 /**
811 * Pushes all the DNS queries into the c-ares channel and waits for the results
812 * on the file descriptors.
813 */
814 3759 void CaresResolver::DoResolve(const vector<string> &names,
815 const vector<bool> &skip,
816 vector<vector<string> > *ipv4_addresses,
817 vector<vector<string> > *ipv6_addresses,
818 vector<Failures> *failures,
819 vector<unsigned> *ttls,
820 vector<string> *fqdns) {
821 3759 const unsigned num = names.size();
822
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3759 times.
3759 if (num == 0)
823 ✗ return;
824
825
1/2
✓ Branch 2 taken 3759 times.
✗ Branch 3 not taken.
3759 vector<QueryInfo *> infos_ipv4(num, NULL);
826
1/2
✓ Branch 2 taken 3759 times.
✗ Branch 3 not taken.
3759 vector<QueryInfo *> infos_ipv6(num, NULL);
827
828
2/2
✓ Branch 0 taken 4715 times.
✓ Branch 1 taken 3759 times.
8474 for (unsigned i = 0; i < num; ++i) {
829
3/4
✓ Branch 1 taken 4715 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3196 times.
✓ Branch 4 taken 1519 times.
4715 if (skip[i])
830 3196 continue;
831
832
2/2
✓ Branch 1 taken 1470 times.
✓ Branch 2 taken 49 times.
1519 if (!ipv4_only()) {
833
2/4
✓ Branch 3 taken 1470 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1470 times.
✗ Branch 7 not taken.
1470 infos_ipv6[i] = new QueryInfo(&(*ipv6_addresses)[i], names[i], kRrAaaa);
834
1/2
✓ Branch 3 taken 1470 times.
✗ Branch 4 not taken.
1470 ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_aaaa,
835 1470 CallbackCares, infos_ipv6[i]);
836 }
837
2/4
✓ Branch 3 taken 1519 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1519 times.
✗ Branch 7 not taken.
1519 infos_ipv4[i] = new QueryInfo(&(*ipv4_addresses)[i], names[i], kRrA);
838
1/2
✓ Branch 3 taken 1519 times.
✗ Branch 4 not taken.
1519 ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_a, CallbackCares,
839 1519 infos_ipv4[i]);
840 }
841
842 bool all_complete;
843 do {
844 141129247 all_complete = true;
845
1/2
✓ Branch 1 taken 141129247 times.
✗ Branch 2 not taken.
141129247 WaitOnCares();
846
2/2
✓ Branch 0 taken 141130840 times.
✓ Branch 1 taken 3759 times.
141134599 for (unsigned i = 0; i < num; ++i) {
847
2/2
✓ Branch 2 taken 8720873 times.
✓ Branch 3 taken 132406771 times.
282258484 if ((infos_ipv4[i] && !infos_ipv4[i]->complete)
848
8/8
✓ Branch 0 taken 141127644 times.
✓ Branch 1 taken 3196 times.
✓ Branch 3 taken 8720824 times.
✓ Branch 4 taken 3245 times.
✓ Branch 6 taken 8718717 times.
✓ Branch 7 taken 2107 times.
✓ Branch 8 taken 141125488 times.
✓ Branch 9 taken 5352 times.
282258484 || (infos_ipv6[i] && !infos_ipv6[i]->complete)) {
849 141125488 all_complete = false;
850 141125488 break;
851 }
852 }
853
2/2
✓ Branch 0 taken 141125488 times.
✓ Branch 1 taken 3759 times.
141129247 } while (!all_complete);
854
855 // Silently ignore errors with IPv4/6 if there are at least some usable IP
856 // addresses.
857
2/2
✓ Branch 0 taken 4715 times.
✓ Branch 1 taken 3759 times.
8474 for (unsigned i = 0; i < num; ++i) {
858
3/4
✓ Branch 1 taken 4715 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3196 times.
✓ Branch 4 taken 1519 times.
4715 if (skip[i])
859 3196 continue;
860
861 1519 Failures status = kFailOther;
862 1519 (*ttls)[i] = unsigned(-1);
863
1/2
✓ Branch 2 taken 1519 times.
✗ Branch 3 not taken.
1519 (*fqdns)[i] = "";
864
2/2
✓ Branch 1 taken 1470 times.
✓ Branch 2 taken 49 times.
1519 if (infos_ipv6[i]) {
865 1470 status = infos_ipv6[i]->status;
866
2/2
✓ Branch 0 taken 1029 times.
✓ Branch 1 taken 441 times.
1470 if (status == kFailOk) {
867 1029 (*ttls)[i] = std::min(infos_ipv6[i]->ttl, (*ttls)[i]);
868
1/2
✓ Branch 3 taken 1029 times.
✗ Branch 4 not taken.
1029 (*fqdns)[i] = infos_ipv6[i]->fqdn;
869 }
870 }
871
1/2
✓ Branch 1 taken 1519 times.
✗ Branch 2 not taken.
1519 if (infos_ipv4[i]) {
872 1519 (*ttls)[i] = std::min(infos_ipv4[i]->ttl, (*ttls)[i]);
873
2/2
✓ Branch 2 taken 490 times.
✓ Branch 3 taken 1029 times.
1519 if ((*fqdns)[i] == "")
874
1/2
✓ Branch 3 taken 490 times.
✗ Branch 4 not taken.
490 (*fqdns)[i] = infos_ipv4[i]->fqdn;
875
2/2
✓ Branch 0 taken 490 times.
✓ Branch 1 taken 1029 times.
1519 if (status != kFailOk)
876 490 status = infos_ipv4[i]->status;
877 }
878 1519 (*failures)[i] = status;
879 }
880
881
2/2
✓ Branch 0 taken 4715 times.
✓ Branch 1 taken 3759 times.
8474 for (unsigned i = 0; i < num; ++i) {
882
2/2
✓ Branch 1 taken 1519 times.
✓ Branch 2 taken 3196 times.
4715 delete infos_ipv4[i];
883
2/2
✓ Branch 1 taken 1470 times.
✓ Branch 2 taken 3245 times.
4715 delete infos_ipv6[i];
884 }
885 3759 }
886
887
888 147 bool CaresResolver::SetResolvers(const vector<string> &resolvers) {
889
2/4
✓ Branch 2 taken 147 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 147 times.
✗ Branch 6 not taken.
294 const string address_list = JoinStrings(resolvers, ",");
890
1/2
✓ Branch 2 taken 147 times.
✗ Branch 3 not taken.
147 const int retval = ares_set_servers_csv(*channel_, address_list.c_str());
891
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 147 times.
147 if (retval != ARES_SUCCESS)
892 ✗ return false;
893
894
1/2
✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
147 resolvers_ = resolvers;
895 147 return true;
896 147 }
897
898
899 /**
900 * Changes the options of the active channel. This is hacky and deals with
901 * c-ares internal data structures because there is no way to do it via public
902 * APIs.
903 */
904 98 bool CaresResolver::SetSearchDomains(const vector<string> &domains) {
905 // From ares_private.h
906 struct {
907 int flags;
908 int timeout;
909 int tries;
910 int ndots;
911 int rotate;
912 int udp_port;
913 int tcp_port;
914 int socket_send_buffer_size;
915 int socket_receive_buffer_size;
916 char **domains;
917 int ndomains;
918 // More fields come in the original data structure
919 } ares_channelhead;
920
921 98 memcpy(&ares_channelhead, *channel_, sizeof(ares_channelhead));
922
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 49 times.
98 if (ares_channelhead.domains) {
923
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 49 times.
147 for (int i = 0; i < ares_channelhead.ndomains; ++i) {
924 98 free(ares_channelhead.domains[i]);
925 }
926 49 free(ares_channelhead.domains);
927 49 ares_channelhead.domains = NULL;
928 }
929
930 98 ares_channelhead.ndomains = static_cast<int>(domains.size());
931
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 49 times.
98 if (ares_channelhead.ndomains > 0) {
932 49 ares_channelhead.domains = reinterpret_cast<char **>(
933 49 smalloc(ares_channelhead.ndomains * sizeof(char *)));
934
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 49 times.
147 for (int i = 0; i < ares_channelhead.ndomains; ++i) {
935 98 ares_channelhead.domains[i] = strdup(domains[i].c_str());
936 }
937 }
938
939 98 memcpy(*channel_, &ares_channelhead, sizeof(ares_channelhead));
940
941
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
98 domains_ = domains;
942 98 return true;
943 }
944
945
946 ✗ void CaresResolver::SetSystemResolvers() {
947 ✗ const int retval = SetResolvers(system_resolvers_);
948 ✗ assert(retval == true);
949 }
950
951
952 ✗ void CaresResolver::SetSystemSearchDomains() {
953 ✗ const int retval = SetSearchDomains(system_domains_);
954 ✗ assert(retval == true);
955 }
956
957
958 /**
959 * Polls on c-ares sockets and triggers call-backs execution. Might be
960 * necessary to call this repeatadly.
961 */
962 141129247 void CaresResolver::WaitOnCares() {
963 // Adapted from libcurl
964 ares_socket_t socks[ARES_GETSOCK_MAXNUM];
965 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
966
1/2
✓ Branch 1 taken 141129247 times.
✗ Branch 2 not taken.
141129247 const int bitmask = ares_getsock(*channel_, socks, ARES_GETSOCK_MAXNUM);
967 141129247 unsigned num = 0;
968
1/2
✓ Branch 0 taken 414661408 times.
✗ Branch 1 not taken.
414661408 for (unsigned i = 0; i < ARES_GETSOCK_MAXNUM; ++i) {
969 414661408 pfd[i].events = 0;
970 414661408 pfd[i].revents = 0;
971
2/2
✓ Branch 0 taken 273532161 times.
✓ Branch 1 taken 141129247 times.
414661408 if (ARES_GETSOCK_READABLE(bitmask, i)) {
972 273532161 pfd[i].fd = socks[i];
973 273532161 pfd[i].events |= POLLRDNORM | POLLIN;
974 }
975
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 414661408 times.
414661408 if (ARES_GETSOCK_WRITABLE(bitmask, i)) {
976 ✗ pfd[i].fd = socks[i];
977 ✗ pfd[i].events |= POLLWRNORM | POLLOUT;
978 }
979
2/2
✓ Branch 0 taken 273532161 times.
✓ Branch 1 taken 141129247 times.
414661408 if (pfd[i].events != 0)
980 273532161 num++;
981 else
982 141129247 break;
983 }
984
985 141129247 int nfds = 0;
986
2/2
✓ Branch 0 taken 141125978 times.
✓ Branch 1 taken 3269 times.
141129247 if (num > 0) {
987 do {
988
1/2
✓ Branch 2 taken 141125978 times.
✗ Branch 3 not taken.
141125978 nfds = poll(pfd, num, timeout_ms());
989
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 141125978 times.
141125978 if (nfds == -1) {
990 // poll must not fail for other reasons
991 ✗ if ((errno != EAGAIN) && (errno != EINTR))
992 ✗ PANIC(NULL);
993 }
994
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 141125978 times.
141125978 } while (nfds == -1);
995 }
996
997
2/2
✓ Branch 0 taken 3269 times.
✓ Branch 1 taken 141125978 times.
141129247 if (nfds == 0) {
998 // Call ares_process() unconditionally here, even if we simply timed out
999 // above, as otherwise the ares name resolve won't timeout.
1000
1/2
✓ Branch 1 taken 3269 times.
✗ Branch 2 not taken.
3269 ares_process_fd(*channel_, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
1001 } else {
1002 // Go through the descriptors and ask for executing the callbacks.
1003
2/2
✓ Branch 0 taken 273532161 times.
✓ Branch 1 taken 141125978 times.
414658139 for (unsigned i = 0; i < num; ++i) {
1004 547064322 ares_process_fd(
1005
1/2
✓ Branch 1 taken 273532161 times.
✗ Branch 2 not taken.
273532161 *channel_,
1006
2/2
✓ Branch 0 taken 1715 times.
✓ Branch 1 taken 273530446 times.
273532161 pfd[i].revents & (POLLRDNORM | POLLIN) ? pfd[i].fd : ARES_SOCKET_BAD,
1007
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 273532161 times.
273532161 pfd[i].revents & (POLLWRNORM | POLLOUT) ? pfd[i].fd
1008 : ARES_SOCKET_BAD);
1009 }
1010 }
1011 141129247 }
1012
1013
1014 //------------------------------------------------------------------------------
1015
1016
1017 /**
1018 * Opens a file descriptor to the host file that stays open until destruction.
1019 * If no path is given, the HOST_ALIASES environment variable is evaluated
1020 * followed by /etc/hosts.
1021 */
1022 8458 HostfileResolver *HostfileResolver::Create(const string &path, bool ipv4_only) {
1023
1/2
✓ Branch 1 taken 8458 times.
✗ Branch 2 not taken.
8458 HostfileResolver *resolver = new HostfileResolver(ipv4_only);
1024
1025
1/2
✓ Branch 1 taken 8458 times.
✗ Branch 2 not taken.
8458 string hosts_file = path;
1026
2/2
✓ Branch 1 taken 6204 times.
✓ Branch 2 taken 2254 times.
8458 if (hosts_file == "") {
1027 6204 char *hosts_env = getenv("HOST_ALIASES");
1028
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 6106 times.
6204 if (hosts_env != NULL) {
1029
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 hosts_file = string(hosts_env);
1030 } else {
1031
1/2
✓ Branch 1 taken 6106 times.
✗ Branch 2 not taken.
6106 hosts_file = "/etc/hosts";
1032 }
1033 }
1034
1/2
✓ Branch 2 taken 8458 times.
✗ Branch 3 not taken.
8458 resolver->fhosts_ = fopen(hosts_file.c_str(), "r");
1035
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 8360 times.
8458 if (!resolver->fhosts_) {
1036
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn, "failed to read host file %s",
1037 hosts_file.c_str());
1038
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 delete resolver;
1039 98 return NULL;
1040 }
1041 8360 return resolver;
1042 8458 }
1043
1044
1045 /**
1046 * Used to process longer domain names before shorter ones, in order to get
1047 * the correct fully qualified domain name. Reversed return value in order to
1048 * sort in descending order.
1049 */
1050 147 static bool SortNameLength(const string &a, const string &b) {
1051 147 const unsigned len_a = a.length();
1052 147 const unsigned len_b = b.length();
1053
1/2
✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
147 if (len_a != len_b)
1054 147 return len_a > len_b;
1055 ✗ return a > b;
1056 }
1057
1058
1059 /**
1060 * Creates a fresh reverse lookup map
1061 */
1062 4048 void HostfileResolver::DoResolve(const vector<string> &names,
1063 const vector<bool> &skip,
1064 vector<vector<std::string> > *ipv4_addresses,
1065 vector<vector<std::string> > *ipv6_addresses,
1066 vector<Failures> *failures,
1067 vector<unsigned> *ttls,
1068 vector<string> *fqdns) {
1069 4048 const unsigned num = names.size();
1070
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4048 times.
4048 if (num == 0)
1071 ✗ return;
1072
1073 4048 ParseHostFile();
1074
2/2
✓ Branch 0 taken 4367 times.
✓ Branch 1 taken 4048 times.
8415 for (unsigned i = 0; i < num; ++i) {
1075
3/4
✓ Branch 1 taken 4367 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2652 times.
✓ Branch 4 taken 1715 times.
4367 if (skip[i])
1076 2652 continue;
1077
1078 1715 vector<string> effective_names;
1079
5/6
✓ Branch 2 taken 1715 times.
✗ Branch 3 not taken.
✓ Branch 8 taken 98 times.
✓ Branch 9 taken 1617 times.
✓ Branch 10 taken 98 times.
✓ Branch 11 taken 1617 times.
1715 if (!names[i].empty() && (names[i][names[i].length() - 1] == '.')) {
1080
2/4
✓ Branch 4 taken 98 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 98 times.
✗ Branch 8 not taken.
98 effective_names.push_back(names[i].substr(0, names[i].length() - 1));
1081 } else {
1082
1/2
✓ Branch 2 taken 1617 times.
✗ Branch 3 not taken.
1617 effective_names.push_back(names[i]);
1083
2/2
✓ Branch 2 taken 147 times.
✓ Branch 3 taken 1617 times.
1764 for (unsigned j = 0; j < domains().size(); ++j) {
1084
3/6
✓ Branch 4 taken 147 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 147 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 147 times.
✗ Branch 11 not taken.
147 effective_names.push_back(names[i] + "." + domains()[j]);
1085 }
1086 }
1087
1088 // Use the longest matching name as fqdn
1089
1/2
✓ Branch 3 taken 1715 times.
✗ Branch 4 not taken.
1715 std::sort(effective_names.begin(), effective_names.end(), SortNameLength);
1090
1091 1715 (*failures)[i] = kFailUnknownHost;
1092
1/2
✓ Branch 3 taken 1715 times.
✗ Branch 4 not taken.
1715 (*fqdns)[i] = names[i];
1093
2/2
✓ Branch 1 taken 1715 times.
✓ Branch 2 taken 490 times.
2205 for (unsigned j = 0; j < effective_names.size(); ++j) {
1094
1/2
✓ Branch 1 taken 1715 times.
✗ Branch 2 not taken.
1715 const map<string, HostEntry>::iterator iter = host_map_.find(
1095 1715 effective_names[j]);
1096
2/2
✓ Branch 2 taken 1225 times.
✓ Branch 3 taken 490 times.
1715 if (iter != host_map_.end()) {
1097
1/2
✓ Branch 7 taken 1225 times.
✗ Branch 8 not taken.
3675 (*ipv4_addresses)[i].insert((*ipv4_addresses)[i].end(),
1098 1225 iter->second.ipv4_addresses.begin(),
1099 1225 iter->second.ipv4_addresses.end());
1100
1/2
✓ Branch 7 taken 1225 times.
✗ Branch 8 not taken.
3675 (*ipv6_addresses)[i].insert((*ipv6_addresses)[i].end(),
1101 1225 iter->second.ipv6_addresses.begin(),
1102 1225 iter->second.ipv6_addresses.end());
1103 1225 (*ttls)[i] = min_ttl_;
1104
1/2
✓ Branch 3 taken 1225 times.
✗ Branch 4 not taken.
1225 (*fqdns)[i] = effective_names[j];
1105 1225 (*failures)[i] = kFailOk;
1106 1225 break;
1107 } // Host name found
1108 } // All possible names (search domains added)
1109 1715 }
1110 }
1111
1112
1113 8458 HostfileResolver::HostfileResolver(const bool ipv4_only)
1114 8458 : Resolver(ipv4_only, 0, 0), fhosts_(NULL) { }
1115
1116
1117 33828 HostfileResolver::~HostfileResolver() {
1118
2/2
✓ Branch 0 taken 8359 times.
✓ Branch 1 taken 98 times.
16914 if (fhosts_)
1119 16718 fclose(fhosts_);
1120 33828 }
1121
1122
1123 /**
1124 * TODO: this should be only necessary when the modification timestamp changed.
1125 */
1126 4048 void HostfileResolver::ParseHostFile() {
1127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4048 times.
4048 assert(fhosts_);
1128
1/2
✓ Branch 1 taken 4048 times.
✗ Branch 2 not taken.
4048 rewind(fhosts_);
1129 4048 host_map_.clear();
1130
1131 4048 string line;
1132
3/4
✓ Branch 1 taken 15996 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11948 times.
✓ Branch 4 taken 4048 times.
15996 while (GetLineFile(fhosts_, &line)) {
1133 char address[kIpMaxLength + 1];
1134 char hostname[kHostnameMaxLength + 1];
1135 int bytes_read;
1136 11948 size_t str_offset = 0;
1137
1138 // strip comments
1139 11948 const size_t hash_pos = line.find_first_of('#');
1140
2/2
✓ Branch 0 taken 490 times.
✓ Branch 1 taken 11458 times.
11948 if (hash_pos != string::npos)
1141
1/2
✓ Branch 1 taken 490 times.
✗ Branch 2 not taken.
490 line = line.substr(0, hash_pos);
1142
1143 // First token is an IP address
1144 11948 int ip_start_pos = -1, ip_end_pos = -1, scan_result;
1145 11948 scan_result = sscanf(line.c_str(), " %n%*s%n", &ip_start_pos, &ip_end_pos);
1146
2/2
✓ Branch 0 taken 686 times.
✓ Branch 1 taken 11262 times.
11948 if (scan_result == EOF)
1147 784 continue;
1148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11262 times.
11262 assert(ip_start_pos != -1);
1149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11262 times.
11262 assert(ip_end_pos != -1);
1150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11262 times.
11262 if (ip_start_pos == ip_end_pos)
1151 ✗ continue;
1152
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 11164 times.
11262 if (ip_end_pos - ip_start_pos > kIpMaxLength) {
1153
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 LogCvmfs(
1154 kLogDns, kLogSyslogWarn,
1155 "Skipping line in hosts file due to invalid IP address format: %s",
1156 line.c_str());
1157 98 continue;
1158 }
1159
1160 11164 bytes_read = -1;
1161 11164 scan_result = sscanf(line.c_str(), " %s%n", address, &bytes_read);
1162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11164 times.
11164 assert(scan_result == 1);
1163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11164 times.
11164 assert(bytes_read != -1);
1164 11164 str_offset += bytes_read;
1165
1166 // Next tokens are hostnames and aliases (we treat them as equal)
1167
2/2
✓ Branch 1 taken 30533 times.
✓ Branch 2 taken 10821 times.
41354 while (str_offset < line.length()) {
1168 // check hostname length
1169 30533 int hostname_start_pos = -1, hostname_end_pos = -1;
1170 30533 scan_result = sscanf(line.c_str() + str_offset, " %n%*s%n",
1171 &hostname_start_pos, &hostname_end_pos);
1172
2/2
✓ Branch 0 taken 343 times.
✓ Branch 1 taken 30190 times.
30533 if (scan_result == EOF)
1173 343 break;
1174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30190 times.
30190 assert(hostname_start_pos != -1);
1175
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30190 times.
30190 assert(hostname_end_pos != -1);
1176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30190 times.
30190 if (hostname_start_pos == hostname_end_pos)
1177 ✗ break;
1178
1179
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 30092 times.
30190 if (hostname_end_pos - hostname_start_pos > kHostnameMaxLength) {
1180
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 LogCvmfs(
1181 kLogDns, kLogSyslogWarn,
1182 "Skipping invalid (too long) hostname in hosts file on line: %s",
1183 line.c_str());
1184 98 str_offset += hostname_end_pos;
1185 98 continue;
1186 }
1187
1188 30092 bytes_read = -1;
1189 30092 scan_result = sscanf(line.c_str() + str_offset, " %s%n", hostname,
1190 &bytes_read);
1191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30092 times.
30092 assert(scan_result == 1);
1192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30092 times.
30092 assert(bytes_read != -1);
1193 30092 str_offset += bytes_read;
1194
1195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30092 times.
30092 if (hostname[strlen(hostname) - 1] == '.')
1196 ✗ hostname[strlen(hostname) - 1] = 0; // strip the last character
1197
1198
2/4
✓ Branch 2 taken 30092 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 30092 times.
✗ Branch 6 not taken.
30092 const map<string, HostEntry>::iterator iter = host_map_.find(hostname);
1199
2/2
✓ Branch 2 taken 24299 times.
✓ Branch 3 taken 5793 times.
30092 if (iter == host_map_.end()) {
1200 24299 HostEntry entry;
1201
4/6
✓ Branch 2 taken 24299 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 24299 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 18653 times.
✓ Branch 10 taken 5646 times.
24299 if (IsIpv4Address(address))
1202
2/4
✓ Branch 2 taken 18653 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 18653 times.
✗ Branch 6 not taken.
18653 entry.ipv4_addresses.push_back(address);
1203
2/2
✓ Branch 1 taken 5548 times.
✓ Branch 2 taken 98 times.
5646 else if (!ipv4_only())
1204
2/4
✓ Branch 2 taken 5548 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5548 times.
✗ Branch 6 not taken.
5548 entry.ipv6_addresses.push_back(address);
1205
3/6
✓ Branch 2 taken 24299 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 24299 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 24299 times.
✗ Branch 9 not taken.
24299 host_map_[hostname] = entry;
1206 24299 } else {
1207
4/6
✓ Branch 2 taken 5793 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5793 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 147 times.
✓ Branch 10 taken 5646 times.
5793 if (IsIpv4Address(address))
1208
2/4
✓ Branch 3 taken 147 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 147 times.
✗ Branch 7 not taken.
147 iter->second.ipv4_addresses.push_back(address);
1209
2/2
✓ Branch 1 taken 5548 times.
✓ Branch 2 taken 98 times.
5646 else if (!ipv4_only())
1210
2/4
✓ Branch 3 taken 5548 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5548 times.
✗ Branch 7 not taken.
5548 iter->second.ipv6_addresses.push_back(address);
1211 }
1212 } // Current line
1213 } // Hosts file
1214 4048 }
1215
1216
1217 6057 bool HostfileResolver::SetSearchDomains(const vector<string> &domains) {
1218 6057 domains_ = domains;
1219 6057 return true;
1220 }
1221
1222
1223 ✗ void HostfileResolver::SetSystemSearchDomains() {
1224 // TODO(jblomer)
1225 ✗ PANIC(NULL);
1226 }
1227
1228
1229 //------------------------------------------------------------------------------
1230
1231
1232 /**
1233 * Creates hostfile and c-ares resolvers and uses c-ares resolvers search
1234 * domains for the hostfile resolver.
1235 */
1236 6057 NormalResolver *NormalResolver::Create(const bool ipv4_only,
1237 const unsigned retries,
1238 const unsigned timeout_ms) {
1239 6057 CaresResolver *cares_resolver = CaresResolver::Create(ipv4_only, retries,
1240 timeout_ms);
1241
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6057 times.
6057 if (!cares_resolver)
1242 ✗ return NULL;
1243
2/4
✓ Branch 2 taken 6057 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6057 times.
✗ Branch 6 not taken.
6057 HostfileResolver *hostfile_resolver = HostfileResolver::Create("", ipv4_only);
1244
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 6008 times.
6057 if (!hostfile_resolver) {
1245
1/2
✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
49 delete cares_resolver;
1246 49 return NULL;
1247 }
1248 6008 const bool retval = hostfile_resolver->SetSearchDomains(
1249 cares_resolver->domains());
1250
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6008 times.
6008 assert(retval);
1251
1252 6008 NormalResolver *normal_resolver = new NormalResolver();
1253 6008 normal_resolver->cares_resolver_ = cares_resolver;
1254 6008 normal_resolver->hostfile_resolver_ = hostfile_resolver;
1255 6008 normal_resolver->domains_ = cares_resolver->domains();
1256 6008 normal_resolver->resolvers_ = cares_resolver->resolvers();
1257 6008 normal_resolver->retries_ = cares_resolver->retries();
1258 6008 normal_resolver->timeout_ms_ = cares_resolver->timeout_ms();
1259 6008 return normal_resolver;
1260 }
1261
1262
1263 /**
1264 * Makes only sense for the c-ares resolver.
1265 */
1266 49 bool NormalResolver::SetResolvers(const vector<string> &resolvers) {
1267 49 return cares_resolver_->SetResolvers(resolvers);
1268 }
1269
1270
1271 /**
1272 * Set new search domains for both resolvers or for none.
1273 */
1274 ✗ bool NormalResolver::SetSearchDomains(const vector<string> &domains) {
1275 ✗ const vector<string> old_domains = hostfile_resolver_->domains();
1276 ✗ bool retval = hostfile_resolver_->SetSearchDomains(domains);
1277 ✗ if (!retval)
1278 ✗ return false;
1279 ✗ retval = cares_resolver_->SetSearchDomains(domains);
1280 ✗ if (!retval) {
1281 ✗ retval = hostfile_resolver_->SetSearchDomains(old_domains);
1282 ✗ assert(retval);
1283 ✗ return false;
1284 }
1285 ✗ return true;
1286 }
1287
1288
1289 ✗ void NormalResolver::SetSystemResolvers() {
1290 ✗ cares_resolver_->SetSystemResolvers();
1291 }
1292
1293
1294 ✗ void NormalResolver::SetSystemSearchDomains() {
1295 ✗ cares_resolver_->SetSystemSearchDomains();
1296 ✗ const bool retval = hostfile_resolver_->SetSearchDomains(
1297 ✗ cares_resolver_->domains());
1298 ✗ assert(retval);
1299 }
1300
1301
1302 /**
1303 * First pass done by the hostfile resolver, all successfully resolved names
1304 * are skipped by the c-ares resolver.
1305 */
1306 2578 void NormalResolver::DoResolve(const vector<string> &names,
1307 const vector<bool> &skip,
1308 vector<vector<string> > *ipv4_addresses,
1309 vector<vector<string> > *ipv6_addresses,
1310 vector<Failures> *failures,
1311 vector<unsigned> *ttls,
1312 vector<string> *fqdns) {
1313 2578 const unsigned num = names.size();
1314
1/2
✓ Branch 1 taken 2578 times.
✗ Branch 2 not taken.
2578 hostfile_resolver_->DoResolve(names, skip, ipv4_addresses, ipv6_addresses,
1315 failures, ttls, fqdns);
1316
1/2
✓ Branch 1 taken 2578 times.
✗ Branch 2 not taken.
2578 vector<bool> skip_cares = skip;
1317
2/2
✓ Branch 0 taken 2603 times.
✓ Branch 1 taken 2578 times.
5181 for (unsigned i = 0; i < num; ++i) {
1318
2/2
✓ Branch 1 taken 2554 times.
✓ Branch 2 taken 49 times.
2603 if ((*failures)[i] == kFailOk)
1319
1/2
✓ Branch 1 taken 2554 times.
✗ Branch 2 not taken.
2554 skip_cares[i] = true;
1320 }
1321
1/2
✓ Branch 1 taken 2578 times.
✗ Branch 2 not taken.
2578 cares_resolver_->DoResolve(names, skip_cares, ipv4_addresses, ipv6_addresses,
1322 failures, ttls, fqdns);
1323 2578 }
1324
1325
1326 6008 NormalResolver::NormalResolver()
1327 6008 : Resolver(false, 0, 0), cares_resolver_(NULL), hostfile_resolver_(NULL) { }
1328
1329
1330 24028 NormalResolver::~NormalResolver() {
1331
1/2
✓ Branch 0 taken 6007 times.
✗ Branch 1 not taken.
12014 delete cares_resolver_;
1332
1/2
✓ Branch 0 taken 6007 times.
✗ Branch 1 not taken.
12014 delete hostfile_resolver_;
1333 24028 }
1334
1335 } // namespace dns
1336
1337