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