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