Directory: | cvmfs/ |
---|---|
File: | cvmfs/network/dns.cc |
Date: | 2025-06-29 02:35:41 |
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 | 3282 | static void PinpointHostSubstr(const std::string &url, | |
54 | unsigned *pos_begin, | ||
55 | unsigned *pos_end) { | ||
56 | 3282 | *pos_begin = *pos_end = 0; | |
57 | 3282 | const unsigned len = url.size(); | |
58 | 3282 | unsigned i = 0; | |
59 | |||
60 | // Search '//' in the url string and jump behind | ||
61 |
2/2✓ Branch 0 taken 19728 times.
✓ Branch 1 taken 1513 times.
|
21241 | for (; i < len; ++i) { |
62 |
8/8✓ Branch 1 taken 1782 times.
✓ Branch 2 taken 17946 times.
✓ Branch 3 taken 1772 times.
✓ Branch 4 taken 10 times.
✓ Branch 6 taken 1769 times.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 1769 times.
✓ Branch 9 taken 17959 times.
|
19728 | if ((url[i] == '/') && (i < len - 2) && (url[i + 1] == '/')) { |
63 | 1769 | i += 2; | |
64 | 1769 | *pos_begin = i; | |
65 | 1769 | break; | |
66 | } | ||
67 | } | ||
68 | |||
69 | // Search '@' within the hostname part and jump behind if present | ||
70 |
2/2✓ Branch 0 taken 1769 times.
✓ Branch 1 taken 1513 times.
|
3282 | if (*pos_begin > 0) { |
71 |
2/2✓ Branch 0 taken 24339 times.
✓ Branch 1 taken 1741 times.
|
26080 | for (i = *pos_begin; i < len; ++i) { |
72 |
2/2✓ Branch 1 taken 23 times.
✓ Branch 2 taken 24316 times.
|
24339 | if (url[i] == '/') { |
73 | 23 | break; | |
74 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 2 taken 24311 times.
|
24316 | } else if (url[i] == '@') { |
75 | 5 | *pos_begin = ++i; | |
76 | 5 | break; | |
77 | } | ||
78 | } | ||
79 | } | ||
80 | |||
81 | // Find the end of the hostname part | ||
82 |
2/2✓ Branch 0 taken 1769 times.
✓ Branch 1 taken 1513 times.
|
3282 | if (*pos_begin > 0) { |
83 | 1769 | bool in_ipv6 = (url[*pos_begin] == '['); | |
84 |
2/2✓ Branch 0 taken 17450 times.
✓ Branch 1 taken 19 times.
|
17469 | for (i = *pos_begin; i < len; ++i) { |
85 |
2/2✓ Branch 0 taken 110 times.
✓ Branch 1 taken 17340 times.
|
17450 | if (in_ipv6) { |
86 |
2/2✓ Branch 1 taken 89 times.
✓ Branch 2 taken 21 times.
|
110 | if (url[i] != ']') |
87 | 89 | continue; | |
88 | 21 | in_ipv6 = false; | |
89 | } | ||
90 | |||
91 |
6/6✓ Branch 1 taken 15625 times.
✓ Branch 2 taken 1736 times.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 15611 times.
✓ Branch 6 taken 1750 times.
✓ Branch 7 taken 15611 times.
|
17361 | if ((url[i] == ':') || (url[i] == '/')) |
92 | 1750 | break; | |
93 | } | ||
94 |
2/2✓ Branch 0 taken 1763 times.
✓ Branch 1 taken 6 times.
|
1769 | if (!in_ipv6) |
95 | 1763 | *pos_end = i - 1; | |
96 | |||
97 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 1751 times.
|
1769 | if (*pos_end < *pos_begin) |
98 | 18 | *pos_end = *pos_begin = 0; | |
99 | } | ||
100 | 3282 | } | |
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 | 2366 | std::string ExtractHost(const std::string &url) { | |
109 | unsigned pos_begin; | ||
110 | unsigned pos_end; | ||
111 | 2366 | PinpointHostSubstr(url, &pos_begin, &pos_end); | |
112 |
2/2✓ Branch 0 taken 1499 times.
✓ Branch 1 taken 867 times.
|
2366 | if (pos_begin == 0) |
113 |
1/2✓ Branch 2 taken 1499 times.
✗ Branch 3 not taken.
|
1499 | return ""; |
114 |
1/2✓ Branch 1 taken 867 times.
✗ Branch 2 not taken.
|
867 | 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 | 832 | std::string ExtractPort(const std::string &url) { | |
124 | unsigned pos_begin; | ||
125 | unsigned pos_end; | ||
126 | 832 | PinpointHostSubstr(url, &pos_begin, &pos_end); | |
127 |
9/10✓ Branch 0 taken 811 times.
✓ Branch 1 taken 21 times.
✓ Branch 3 taken 804 times.
✓ Branch 4 taken 7 times.
✓ Branch 6 taken 804 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 801 times.
✓ Branch 10 taken 31 times.
✓ Branch 11 taken 801 times.
|
832 | if (pos_begin == 0 || pos_end + 2 >= url.size() || url.at(pos_end + 1) != ':') |
128 |
1/2✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
|
31 | return ""; |
129 | |||
130 | // Do not include path | ||
131 | 801 | const std::size_t pos_port = url.find("/", pos_end); | |
132 | 801 | std::string retme; | |
133 |
2/2✓ Branch 0 taken 795 times.
✓ Branch 1 taken 6 times.
|
801 | if (pos_port == std::string::npos) |
134 |
1/2✓ Branch 1 taken 795 times.
✗ Branch 2 not taken.
|
795 | retme = url.substr(pos_end + 2); |
135 | else | ||
136 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | retme = url.substr(pos_end + 2, pos_port - pos_end - 2); |
137 | |||
138 | // Port is an integer | ||
139 |
2/2✓ Branch 4 taken 3163 times.
✓ Branch 5 taken 797 times.
|
3960 | for (std::string::iterator it = retme.begin(); it != retme.end(); ++it) |
140 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3159 times.
|
3163 | if (isdigit(*it) == 0) |
141 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | return ""; |
142 | |||
143 | 797 | return retme; | |
144 | 801 | } | |
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 | 84 | string RewriteUrl(const string &url, const string &ip) { | |
153 | unsigned pos_begin; | ||
154 | unsigned pos_end; | ||
155 | 84 | PinpointHostSubstr(url, &pos_begin, &pos_end); | |
156 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 73 times.
|
84 | if (pos_begin == 0) |
157 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | return url; |
158 | |||
159 |
1/2✓ Branch 1 taken 73 times.
✗ Branch 2 not taken.
|
73 | string result = url; |
160 |
1/2✓ Branch 1 taken 73 times.
✗ Branch 2 not taken.
|
73 | result.replace(pos_begin, (pos_end - pos_begin) + 1, ip); |
161 | 73 | return result; | |
162 | 73 | } | |
163 | |||
164 | |||
165 | /** | ||
166 | * Removes the brackets from IPv6 addresses. Leaves IPv4 addresses unchanged. | ||
167 | */ | ||
168 | 8 | string StripIp(const string &decorated_ip) { | |
169 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
|
8 | if (!decorated_ip.empty()) { |
170 | 7 | if ((decorated_ip[0] == '[') | |
171 |
6/6✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 5 times.
|
7 | && (decorated_ip[decorated_ip.length() - 1] == ']')) { |
172 | 2 | return decorated_ip.substr(1, decorated_ip.length() - 2); | |
173 | } | ||
174 | } | ||
175 | 6 | return decorated_ip; | |
176 | } | ||
177 | |||
178 | |||
179 | /** | ||
180 | * Adds http:// if it is missing from proxy | ||
181 | */ | ||
182 | 3119 | std::string AddDefaultScheme(const std::string &proxy) { | |
183 | 3119 | const bool ignore_case = true; | |
184 |
4/12✓ Branch 1 taken 3119 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3119 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3116 times.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
6238 | if (HasPrefix(proxy, "http://", ignore_case) |
185 |
9/19✓ Branch 2 taken 2990 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2990 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2989 times.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 2985 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 2990 times.
✓ Branch 14 taken 129 times.
✓ Branch 16 taken 3119 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
|
6109 | || HasPrefix(proxy, "https://", ignore_case) || (proxy == "DIRECT") |
186 |
7/9✓ Branch 2 taken 3119 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2990 times.
✓ Branch 5 taken 129 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 2990 times.
✓ Branch 10 taken 129 times.
|
9228 | || proxy.empty()) { |
187 | 3116 | return proxy; | |
188 | } | ||
189 | 3 | return "http://" + proxy; | |
190 | } | ||
191 | |||
192 | |||
193 | //------------------------------------------------------------------------------ | ||
194 | |||
195 | |||
196 | atomic_int64 Host::global_id_ = 0; | ||
197 | |||
198 | 73 | const set<string> &Host::ViewBestAddresses(IpPreference preference) const { | |
199 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
|
6 | if (((preference == kIpPreferSystem) || (preference == kIpPreferV4)) |
200 |
6/6✓ Branch 0 taken 6 times.
✓ Branch 1 taken 67 times.
✓ Branch 3 taken 68 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 68 times.
✓ Branch 6 taken 5 times.
|
79 | && HasIpv4()) { |
201 | 68 | return ipv4_addresses_; | |
202 | } | ||
203 |
6/6✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 4 times.
|
5 | if ((preference == kIpPreferV6) && !HasIpv6()) |
204 | 1 | return ipv4_addresses_; | |
205 | 4 | return ipv6_addresses_; | |
206 | } | ||
207 | |||
208 | |||
209 | 6202 | void Host::CopyFrom(const Host &other) { | |
210 | 6202 | deadline_ = other.deadline_; | |
211 | 6202 | id_ = other.id_; | |
212 | 6202 | ipv4_addresses_ = other.ipv4_addresses_; | |
213 | 6202 | ipv6_addresses_ = other.ipv6_addresses_; | |
214 | 6202 | name_ = other.name_; | |
215 | 6202 | status_ = other.status_; | |
216 | 6202 | } | |
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 | 1 | Host Host::ExtendDeadline(const Host &original, unsigned seconds_from_now) { | |
224 | 1 | Host new_host(original); | |
225 | 1 | new_host.id_ = atomic_xadd64(&global_id_, 1); | |
226 | 1 | new_host.deadline_ = time(NULL) + seconds_from_now; | |
227 | 1 | 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 | 3384 | Host::Host() | |
236 | 3384 | : deadline_(0) | |
237 | 3384 | , id_(atomic_xadd64(&global_id_, 1)) | |
238 | 3384 | , status_(kFailNotYetResolved) { } | |
239 | |||
240 | |||
241 |
1/2✓ Branch 4 taken 6075 times.
✗ Branch 5 not taken.
|
6075 | Host::Host(const Host &other) { CopyFrom(other); } |
242 | |||
243 | |||
244 | 143 | Host &Host::operator=(const Host &other) { | |
245 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 127 times.
|
143 | if (&other == this) |
246 | 16 | return *this; | |
247 | 127 | CopyFrom(other); | |
248 | 127 | 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 | 22 | bool Host::IsEquivalent(const Host &other) const { | |
258 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 1 times.
|
19 | return (status_ == kFailOk) && (other.status_ == kFailOk) |
259 |
3/4✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✓ Branch 5 taken 2 times.
|
18 | && (name_ == other.name_) && (ipv4_addresses_ == other.ipv4_addresses_) |
260 |
4/4✓ Branch 0 taken 19 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 4 times.
|
41 | && (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 | 78 | bool Host::IsExpired() const { | |
268 | 78 | const time_t now = time(NULL); | |
269 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
|
78 | assert(now != static_cast<time_t>(-1)); |
270 | 78 | 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 | 16 | bool Host::IsValid() const { | |
280 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
|
16 | if (status_ != kFailOk) |
281 | 6 | return false; | |
282 | |||
283 |
3/4✓ Branch 1 taken 2 times.
✓ Branch 2 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
|
10 | assert(!ipv4_addresses_.empty() || !ipv6_addresses_.empty()); |
284 | 10 | 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 | 15901 | bool Resolver::IsIpv4Address(const string &address) { | |
296 | // Are there any unexpected characters? | ||
297 |
2/4✓ Branch 2 taken 15901 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 15901 times.
✗ Branch 6 not taken.
|
31802 | const sanitizer::InputSanitizer sanitizer("09 ."); |
298 |
3/4✓ Branch 1 taken 15901 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6277 times.
✓ Branch 4 taken 9624 times.
|
15901 | if (!sanitizer.IsValid(address)) |
299 | 6277 | return false; | |
300 | |||
301 | // 4 octets in the range 0-255? | ||
302 |
1/2✓ Branch 1 taken 9624 times.
✗ Branch 2 not taken.
|
9624 | vector<string> octets = SplitString(address, '.'); |
303 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9623 times.
|
9624 | if (octets.size() != 4) |
304 | 1 | return false; | |
305 |
2/2✓ Branch 0 taken 38492 times.
✓ Branch 1 taken 9622 times.
|
48114 | for (unsigned i = 0; i < 4; ++i) { |
306 |
1/2✓ Branch 2 taken 38492 times.
✗ Branch 3 not taken.
|
38492 | const uint64_t this_octet = String2Uint64(octets[i]); |
307 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 38491 times.
|
38492 | if (this_octet > 255) |
308 | 1 | return false; | |
309 | } | ||
310 | |||
311 | 9622 | return true; | |
312 | 15901 | } | |
313 | |||
314 | |||
315 | /** | ||
316 | * Basic input validation to ensure that this could syntactically represent a | ||
317 | * valid IPv6 address. | ||
318 | */ | ||
319 | 44 | bool Resolver::IsIpv6Address(const string &address) { | |
320 | // Are there any unexpected characters? | ||
321 |
2/4✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
|
88 | const sanitizer::InputSanitizer sanitizer("09 af AF :"); |
322 |
1/2✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
|
88 | return sanitizer.IsValid(address); |
323 | 44 | } | |
324 | |||
325 | |||
326 | 9858 | Resolver::Resolver(const bool ipv4_only, | |
327 | const unsigned retries, | ||
328 | 9858 | const unsigned timeout_ms) | |
329 | 9858 | : ipv4_only_(ipv4_only) | |
330 | 9858 | , retries_(retries) | |
331 | 9858 | , timeout_ms_(timeout_ms) | |
332 | 9858 | , throttle_(0) | |
333 | 9858 | , min_ttl_(kDefaultMinTtl) | |
334 | 9858 | , max_ttl_(kDefaultMaxTtl) { | |
335 | 9858 | prng_.InitLocaltime(); | |
336 | 9858 | } | |
337 | |||
338 | |||
339 | /** | ||
340 | * Wrapper around the vector interface. | ||
341 | */ | ||
342 | 221 | Host Resolver::Resolve(const string &name) { | |
343 | 221 | vector<string> names; | |
344 |
1/2✓ Branch 1 taken 221 times.
✗ Branch 2 not taken.
|
221 | names.push_back(name); |
345 | 221 | vector<Host> hosts; | |
346 |
1/2✓ Branch 1 taken 221 times.
✗ Branch 2 not taken.
|
221 | ResolveMany(names, &hosts); |
347 |
1/2✓ Branch 2 taken 221 times.
✗ Branch 3 not taken.
|
442 | return hosts[0]; |
348 | 221 | } | |
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 | 1764 | void Resolver::ResolveMany(const vector<string> &names, vector<Host> *hosts) { | |
356 | 1764 | const unsigned num = names.size(); | |
357 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1764 times.
|
1764 | if (num == 0) |
358 | ✗ | return; | |
359 | |||
360 |
1/2✓ Branch 2 taken 1764 times.
✗ Branch 3 not taken.
|
1764 | vector<vector<string> > ipv4_addresses(num); |
361 |
1/2✓ Branch 2 taken 1764 times.
✗ Branch 3 not taken.
|
1764 | vector<vector<string> > ipv6_addresses(num); |
362 |
1/2✓ Branch 2 taken 1764 times.
✗ Branch 3 not taken.
|
1764 | vector<Failures> failures(num); |
363 |
1/2✓ Branch 2 taken 1764 times.
✗ Branch 3 not taken.
|
1764 | vector<unsigned> ttls(num); |
364 |
1/2✓ Branch 2 taken 1764 times.
✗ Branch 3 not taken.
|
1764 | vector<string> fqdns(num); |
365 |
1/2✓ Branch 2 taken 1764 times.
✗ Branch 3 not taken.
|
1764 | vector<bool> skip(num); |
366 | |||
367 | // Deal with special names: empty, IPv4, IPv6 | ||
368 |
2/2✓ Branch 0 taken 1805 times.
✓ Branch 1 taken 1764 times.
|
3569 | for (unsigned i = 0; i < num; ++i) { |
369 |
2/2✓ Branch 2 taken 1493 times.
✓ Branch 3 taken 312 times.
|
1805 | if (names[i].empty()) { |
370 |
1/2✓ Branch 1 taken 1493 times.
✗ Branch 2 not taken.
|
1493 | LogCvmfs(kLogDns, kLogDebug, "empty hostname"); |
371 | 1493 | Host invalid_host; | |
372 |
1/2✓ Branch 1 taken 1493 times.
✗ Branch 2 not taken.
|
1493 | invalid_host.name_ = ""; |
373 | 1493 | invalid_host.status_ = kFailInvalidHost; | |
374 |
1/2✓ Branch 1 taken 1493 times.
✗ Branch 2 not taken.
|
1493 | hosts->push_back(invalid_host); |
375 |
1/2✓ Branch 1 taken 1493 times.
✗ Branch 2 not taken.
|
1493 | skip[i] = true; |
376 |
3/4✓ Branch 3 taken 312 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 229 times.
✓ Branch 6 taken 83 times.
|
1805 | } else if (IsIpv4Address(names[i])) { |
377 |
1/2✓ Branch 3 taken 229 times.
✗ Branch 4 not taken.
|
229 | LogCvmfs(kLogDns, kLogDebug, "IPv4 address %s", names[i].c_str()); |
378 | 229 | Host ipv4_host; | |
379 |
1/2✓ Branch 2 taken 229 times.
✗ Branch 3 not taken.
|
229 | ipv4_host.name_ = names[i]; |
380 | 229 | ipv4_host.status_ = kFailOk; | |
381 |
1/2✓ Branch 2 taken 229 times.
✗ Branch 3 not taken.
|
229 | ipv4_host.ipv4_addresses_.insert(names[i]); |
382 | 229 | ipv4_host.deadline_ = time(NULL) + max_ttl_; | |
383 |
1/2✓ Branch 1 taken 229 times.
✗ Branch 2 not taken.
|
229 | hosts->push_back(ipv4_host); |
384 |
1/2✓ Branch 1 taken 229 times.
✗ Branch 2 not taken.
|
229 | skip[i] = true; |
385 |
2/2✓ Branch 5 taken 4 times.
✓ Branch 6 taken 74 times.
|
390 | } else if ((names[i].length() >= 3) && (names[i][0] == '[') |
386 |
5/6✓ Branch 0 taken 78 times.
✓ Branch 1 taken 5 times.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 79 times.
|
161 | && (names[i][names[i].length() - 1] == ']')) { |
387 |
1/2✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
4 | LogCvmfs(kLogDns, kLogDebug, "IPv6 address %s", names[i].c_str()); |
388 | 4 | Host ipv6_host; | |
389 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | ipv6_host.name_ = names[i]; |
390 | 4 | ipv6_host.status_ = kFailOk; | |
391 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | ipv6_host.ipv6_addresses_.insert(names[i]); |
392 | 4 | ipv6_host.deadline_ = time(NULL) + max_ttl_; | |
393 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | hosts->push_back(ipv6_host); |
394 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | skip[i] = true; |
395 | 4 | } else { | |
396 |
1/2✓ Branch 2 taken 79 times.
✗ Branch 3 not taken.
|
79 | hosts->push_back(Host()); |
397 |
1/2✓ Branch 1 taken 79 times.
✗ Branch 2 not taken.
|
79 | skip[i] = false; |
398 | } | ||
399 | } | ||
400 | |||
401 |
1/2✓ Branch 1 taken 1764 times.
✗ Branch 2 not taken.
|
1764 | DoResolve(names, skip, &ipv4_addresses, &ipv6_addresses, &failures, &ttls, |
402 | &fqdns); | ||
403 | |||
404 | // Construct host objects | ||
405 |
2/2✓ Branch 0 taken 1805 times.
✓ Branch 1 taken 1764 times.
|
3569 | for (unsigned i = 0; i < num; ++i) { |
406 |
3/5✓ Branch 1 taken 1805 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1726 times.
✓ Branch 5 taken 79 times.
|
1805 | if (skip[i]) |
407 | 1742 | continue; | |
408 | |||
409 | 79 | Host host; | |
410 |
1/2✓ Branch 2 taken 79 times.
✗ Branch 3 not taken.
|
79 | host.name_ = fqdns[i]; |
411 | 79 | host.status_ = failures[i]; | |
412 | |||
413 | 79 | unsigned effective_ttl = ttls[i]; | |
414 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 62 times.
|
79 | if (effective_ttl < min_ttl_) { |
415 | 17 | effective_ttl = min_ttl_; | |
416 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 60 times.
|
62 | } else if (effective_ttl > max_ttl_) { |
417 | 2 | effective_ttl = max_ttl_; | |
418 | } | ||
419 | 79 | host.deadline_ = time(NULL) + effective_ttl; | |
420 | |||
421 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 63 times.
|
79 | if (host.status_ != kFailOk) { |
422 |
1/2✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
|
32 | LogCvmfs(kLogDns, kLogDebug, "failed to resolve %s - %d (%s), ttl %u", |
423 | 16 | names[i].c_str(), host.status_, Code2Ascii(host.status_), | |
424 | effective_ttl); | ||
425 |
1/2✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
|
16 | (*hosts)[i] = host; |
426 | 16 | continue; | |
427 | } | ||
428 | |||
429 | // Verify addresses and make them readily available for curl | ||
430 |
2/2✓ Branch 2 taken 71 times.
✓ Branch 3 taken 63 times.
|
134 | for (unsigned j = 0; j < ipv4_addresses[i].size(); ++j) { |
431 |
3/4✓ Branch 3 taken 71 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 67 times.
|
71 | if (!IsIpv4Address(ipv4_addresses[i][j])) { |
432 |
1/4✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
8 | LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn, |
433 | "host name %s resolves to invalid IPv4 address %s", | ||
434 | 8 | names[i].c_str(), ipv4_addresses[i][j].c_str()); | |
435 | 4 | continue; | |
436 | } | ||
437 |
2/2✓ Branch 2 taken 61 times.
✓ Branch 3 taken 6 times.
|
67 | if (names[i] == host.name_) { |
438 |
1/2✓ Branch 4 taken 61 times.
✗ Branch 5 not taken.
|
61 | LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s", names[i].c_str(), |
439 | 61 | ipv4_addresses[i][j].c_str()); | |
440 | } else { | ||
441 |
1/2✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
|
12 | LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s -> %s", |
442 | 6 | names[i].c_str(), host.name_.c_str(), | |
443 | 6 | ipv4_addresses[i][j].c_str()); | |
444 | } | ||
445 |
1/2✓ Branch 3 taken 67 times.
✗ Branch 4 not taken.
|
67 | host.ipv4_addresses_.insert(ipv4_addresses[i][j]); |
446 | } | ||
447 | |||
448 |
2/2✓ Branch 2 taken 44 times.
✓ Branch 3 taken 63 times.
|
107 | for (unsigned j = 0; j < ipv6_addresses[i].size(); ++j) { |
449 |
3/4✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 42 times.
|
44 | if (!IsIpv6Address(ipv6_addresses[i][j])) { |
450 |
1/4✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
4 | LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn, |
451 | "host name %s resolves to invalid IPv6 address %s", | ||
452 | 4 | names[i].c_str(), ipv6_addresses[i][j].c_str()); | |
453 | 2 | continue; | |
454 | } | ||
455 | // For URLs we need brackets around IPv6 addresses | ||
456 |
2/2✓ Branch 2 taken 39 times.
✓ Branch 3 taken 3 times.
|
42 | if (names[i] == host.name_) { |
457 |
1/2✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
|
39 | LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s", names[i].c_str(), |
458 | 39 | ipv6_addresses[i][j].c_str()); | |
459 | } else { | ||
460 |
1/2✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
6 | LogCvmfs(kLogDns, kLogDebug, "add address %s -> %s -> %s", |
461 | 3 | names[i].c_str(), host.name_.c_str(), | |
462 | 3 | ipv6_addresses[i][j].c_str()); | |
463 | } | ||
464 |
3/6✓ Branch 3 taken 42 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 42 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 42 times.
✗ Branch 10 not taken.
|
42 | host.ipv6_addresses_.insert("[" + ipv6_addresses[i][j] + "]"); |
465 | } | ||
466 | |||
467 |
6/6✓ Branch 1 taken 5 times.
✓ Branch 2 taken 58 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 60 times.
|
63 | if (host.ipv4_addresses_.empty() && host.ipv6_addresses_.empty()) { |
468 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | LogCvmfs(kLogDns, kLogDebug, "no addresses returned for %s", |
469 | 3 | names[i].c_str()); | |
470 | 3 | host.status_ = kFailNoAddress; | |
471 | } | ||
472 | |||
473 | // Remove surplus IP addresses | ||
474 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 62 times.
|
63 | if (throttle_ > 0) { |
475 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
|
3 | while (host.ipv4_addresses_.size() > throttle_) { |
476 | 2 | const unsigned random = prng_.Next(host.ipv4_addresses_.size()); | |
477 | 2 | set<string>::iterator rnd_itr = host.ipv4_addresses_.begin(); | |
478 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | std::advance(rnd_itr, random); |
479 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | host.ipv4_addresses_.erase(rnd_itr); |
480 | } | ||
481 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
|
3 | while (host.ipv6_addresses_.size() > throttle_) { |
482 | 2 | const unsigned random = prng_.Next(host.ipv6_addresses_.size()); | |
483 | 2 | set<string>::iterator rnd_itr = host.ipv6_addresses_.begin(); | |
484 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | std::advance(rnd_itr, random); |
485 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | host.ipv6_addresses_.erase(rnd_itr); |
486 | } | ||
487 | } | ||
488 | |||
489 |
1/2✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
|
63 | (*hosts)[i] = host; |
490 |
2/2✓ Branch 1 taken 63 times.
✓ Branch 2 taken 16 times.
|
79 | } |
491 | 1764 | } | |
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 | 61 | QueryInfo(vector<string> *a, const string &n, const ResourceRecord r) | |
512 | 61 | : addresses(a) | |
513 | 61 | , complete(false) | |
514 | 61 | , fqdn(n) | |
515 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
61 | , name(n) |
516 | 61 | , record(r) | |
517 | 61 | , status(kFailOther) | |
518 | 61 | , 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 | 61 | static void CallbackCares(void *arg, int status, int timeouts_ms, | |
544 | unsigned char *abuf, int alen) { | ||
545 | 61 | QueryInfo *info = reinterpret_cast<QueryInfo *>(arg); | |
546 | |||
547 | 61 | info->complete = true; | |
548 |
5/7✓ Branch 0 taken 48 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
61 | switch (status) { |
549 | 48 | case ARES_SUCCESS: | |
550 | Failures retval; | ||
551 |
2/3✓ Branch 0 taken 25 times.
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
48 | switch (info->record) { |
552 | 25 | case kRrA: | |
553 | 25 | retval = CaresExtractIpv4(abuf, alen, info->addresses, &info->ttl, | |
554 | &info->fqdn); | ||
555 | 25 | break; | |
556 | 23 | case kRrAaaa: | |
557 | 23 | retval = CaresExtractIpv6(abuf, alen, info->addresses, &info->ttl, | |
558 | &info->fqdn); | ||
559 | 23 | break; | |
560 | ✗ | default: | |
561 | // Never here. | ||
562 | ✗ | PANIC(NULL); | |
563 | } | ||
564 | 48 | info->status = retval; | |
565 | 48 | break; | |
566 | 1 | case ARES_ENODATA: | |
567 | 1 | info->status = kFailUnknownHost; | |
568 | 1 | break; | |
569 | ✗ | case ARES_EFORMERR: | |
570 | ✗ | info->status = kFailMalformed; | |
571 | ✗ | break; | |
572 | 8 | case ARES_ENOTFOUND: | |
573 | 8 | info->status = kFailUnknownHost; | |
574 | 8 | break; | |
575 | 3 | case ARES_ETIMEOUT: | |
576 | 3 | info->status = kFailTimeout; | |
577 | 3 | break; | |
578 | 1 | case ARES_ECONNREFUSED: | |
579 | 1 | info->status = kFailInvalidResolvers; | |
580 | 1 | break; | |
581 | ✗ | default: | |
582 | ✗ | info->status = kFailOther; | |
583 | } | ||
584 | 61 | } | |
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 | 25 | static Failures CaresExtractIpv4(const unsigned char *abuf, | |
592 | int alen, | ||
593 | vector<string> *addresses, | ||
594 | unsigned *ttl, | ||
595 | string *fqdn) { | ||
596 | 25 | struct hostent *host_entry = NULL; | |
597 | struct ares_addrttl records[CaresResolver::kMaxAddresses]; | ||
598 | 25 | int naddrttls = CaresResolver::kMaxAddresses; | |
599 |
1/2✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
|
25 | const int retval = ares_parse_a_reply(abuf, alen, &host_entry, records, |
600 | &naddrttls); | ||
601 | |||
602 |
1/3✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
25 | switch (retval) { |
603 | 25 | case ARES_SUCCESS: | |
604 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (host_entry == NULL) |
605 | ✗ | return kFailMalformed; | |
606 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (host_entry->h_name == NULL) { |
607 | ✗ | ares_free_hostent(host_entry); | |
608 | ✗ | return kFailMalformed; | |
609 | } | ||
610 |
1/2✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
|
25 | *fqdn = string(host_entry->h_name); |
611 |
1/2✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
|
25 | ares_free_hostent(host_entry); |
612 | |||
613 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (naddrttls <= 0) |
614 | ✗ | return kFailMalformed; | |
615 | 25 | *ttl = unsigned(-1); | |
616 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 25 times.
|
50 | for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) { |
617 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (records[i].ttl < 0) |
618 | ✗ | continue; | |
619 | 25 | *ttl = std::min(unsigned(records[i].ttl), *ttl); | |
620 | |||
621 | char addrstr[INET_ADDRSTRLEN]; | ||
622 | 25 | 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 25 times.
|
25 | if (!retval_p) |
625 | ✗ | continue; | |
626 |
2/4✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 25 times.
✗ Branch 6 not taken.
|
25 | addresses->push_back(addrstr); |
627 | } | ||
628 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
|
25 | if (addresses->empty()) |
629 | ✗ | return kFailMalformed; | |
630 | 25 | 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 | 23 | static Failures CaresExtractIpv6(const unsigned char *abuf, | |
642 | int alen, | ||
643 | vector<string> *addresses, | ||
644 | unsigned *ttl, | ||
645 | string *fqdn) { | ||
646 | 23 | struct hostent *host_entry = NULL; | |
647 | struct ares_addr6ttl records[CaresResolver::kMaxAddresses]; | ||
648 | 23 | int naddrttls = CaresResolver::kMaxAddresses; | |
649 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
23 | const int retval = ares_parse_aaaa_reply(abuf, alen, &host_entry, records, |
650 | &naddrttls); | ||
651 | |||
652 |
1/3✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
23 | switch (retval) { |
653 | 23 | case ARES_SUCCESS: | |
654 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
|
23 | if (host_entry == NULL) |
655 | ✗ | return kFailMalformed; | |
656 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
|
23 | if (host_entry->h_name == NULL) { |
657 | ✗ | ares_free_hostent(host_entry); | |
658 | ✗ | return kFailMalformed; | |
659 | } | ||
660 |
1/2✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
|
23 | *fqdn = string(host_entry->h_name); |
661 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
23 | ares_free_hostent(host_entry); |
662 | |||
663 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
|
23 | if (naddrttls <= 0) |
664 | ✗ | return kFailMalformed; | |
665 | 23 | *ttl = unsigned(-1); | |
666 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 23 times.
|
46 | for (unsigned i = 0; i < static_cast<unsigned>(naddrttls); ++i) { |
667 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
|
23 | if (records[i].ttl < 0) |
668 | ✗ | continue; | |
669 | 23 | *ttl = std::min(unsigned(records[i].ttl), *ttl); | |
670 | |||
671 | char addrstr[INET6_ADDRSTRLEN]; | ||
672 | 23 | 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 23 times.
|
23 | if (!retval_p) |
675 | ✗ | continue; | |
676 |
2/4✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 23 times.
✗ Branch 6 not taken.
|
23 | addresses->push_back(addrstr); |
677 | } | ||
678 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
|
23 | if (addresses->empty()) |
679 | ✗ | return kFailMalformed; | |
680 | 23 | 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 | 3457 | CaresResolver::CaresResolver(const bool ipv4_only, | |
692 | const unsigned retries, | ||
693 | 3457 | const unsigned timeout_ms) | |
694 | : Resolver(ipv4_only, retries, timeout_ms) | ||
695 | 3457 | , channel_(NULL) | |
696 | 3457 | , lookup_options_(strdup("b")) { } | |
697 | |||
698 | |||
699 | 13056 | CaresResolver::~CaresResolver() { | |
700 |
1/2✓ Branch 0 taken 3264 times.
✗ Branch 1 not taken.
|
6528 | if (channel_) { |
701 | 6528 | ares_destroy(*channel_); | |
702 | 6528 | free(channel_); | |
703 | } | ||
704 | 6528 | free(lookup_options_); | |
705 | 13056 | } | |
706 | |||
707 | |||
708 | /** | ||
709 | * Returns a CaresResolver readily initialized, or NULL if an error occurs. | ||
710 | */ | ||
711 | 3457 | 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 46 times.
✓ Branch 2 taken 3411 times.
|
3457 | if (getenv("HOSTALIASES") == NULL) { |
716 | 46 | retval = setenv("HOSTALIASES", "/etc/hosts", 1); | |
717 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
|
46 | assert(retval == 0); |
718 | } | ||
719 | |||
720 |
1/2✓ Branch 1 taken 3457 times.
✗ Branch 2 not taken.
|
3457 | CaresResolver *resolver = new CaresResolver(ipv4_only, retries, timeout_ms); |
721 | 3457 | resolver->channel_ = reinterpret_cast<ares_channel *>( | |
722 | 3457 | smalloc(sizeof(ares_channel))); | |
723 | 3457 | 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 | 3457 | memset(&options, 0, sizeof(options)); | |
730 | 3457 | options.timeout = timeout_ms; | |
731 | 3457 | options.tries = 1 + retries; | |
732 | 3457 | options.lookups = resolver->lookup_options_; | |
733 | 3457 | optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_LOOKUPS; | |
734 |
1/2✓ Branch 1 taken 3457 times.
✗ Branch 2 not taken.
|
3457 | retval = ares_init_options(resolver->channel_, &options, optmask); |
735 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3457 times.
|
3457 | if (retval != ARES_SUCCESS) |
736 | ✗ | goto create_fail; | |
737 | |||
738 | // Save search domains | ||
739 |
1/2✓ Branch 1 taken 3457 times.
✗ Branch 2 not taken.
|
3457 | retval = ares_save_options(*resolver->channel_, &options, &optmask); |
740 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3457 times.
|
3457 | if (retval != ARES_SUCCESS) |
741 | ✗ | goto create_fail; | |
742 |
2/2✓ Branch 0 taken 3457 times.
✓ Branch 1 taken 3457 times.
|
6914 | for (int i = 0; i < options.ndomains; ++i) { |
743 |
2/4✓ Branch 2 taken 3457 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3457 times.
✗ Branch 6 not taken.
|
3457 | resolver->domains_.push_back(options.domains[i]); |
744 | } | ||
745 |
1/2✓ Branch 1 taken 3457 times.
✗ Branch 2 not taken.
|
3457 | ares_destroy_options(&options); |
746 |
1/2✓ Branch 1 taken 3457 times.
✗ Branch 2 not taken.
|
3457 | resolver->system_domains_ = resolver->domains_; |
747 | |||
748 | // Save the system default resolvers | ||
749 | 3457 | addresses = NULL; | |
750 |
1/2✓ Branch 1 taken 3457 times.
✗ Branch 2 not taken.
|
3457 | retval = ares_get_servers(*resolver->channel_, &addresses); |
751 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3457 times.
|
3457 | if (retval != ARES_SUCCESS) |
752 | ✗ | goto create_fail; | |
753 | 3457 | iter = addresses; | |
754 |
2/2✓ Branch 0 taken 17285 times.
✓ Branch 1 taken 3457 times.
|
20742 | while (iter) { |
755 |
2/3✓ Branch 0 taken 10371 times.
✓ Branch 1 taken 6914 times.
✗ Branch 2 not taken.
|
17285 | switch (iter->family) { |
756 | 10371 | case AF_INET: { | |
757 | char addrstr[INET_ADDRSTRLEN]; | ||
758 | 10371 | const void *retval_p = inet_ntop(AF_INET, &(iter->addr), addrstr, | |
759 | INET_ADDRSTRLEN); | ||
760 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10371 times.
|
10371 | if (!retval_p) { |
761 | ✗ | LogCvmfs(kLogDns, kLogDebug | kLogSyslogErr, | |
762 | "invalid system name resolver"); | ||
763 | } else { | ||
764 |
3/6✓ Branch 2 taken 10371 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10371 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10371 times.
✗ Branch 9 not taken.
|
10371 | resolver->resolvers_.push_back(string(addrstr) + ":53"); |
765 | } | ||
766 | 10371 | break; | |
767 | } | ||
768 | 6914 | case AF_INET6: { | |
769 | char addrstr[INET6_ADDRSTRLEN]; | ||
770 | 6914 | const void *retval_p = inet_ntop(AF_INET6, &(iter->addr), addrstr, | |
771 | INET6_ADDRSTRLEN); | ||
772 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6914 times.
|
6914 | if (!retval_p) { |
773 | ✗ | LogCvmfs(kLogDns, kLogDebug | kLogSyslogErr, | |
774 | "invalid system name resolver"); | ||
775 | } else { | ||
776 |
4/8✓ Branch 2 taken 6914 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6914 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 6914 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 6914 times.
✗ Branch 12 not taken.
|
6914 | resolver->resolvers_.push_back("[" + string(addrstr) + "]:53"); |
777 | } | ||
778 | 6914 | break; | |
779 | } | ||
780 | ✗ | default: | |
781 | // Never here. | ||
782 | ✗ | PANIC(NULL); | |
783 | } | ||
784 | 17285 | iter = iter->next; | |
785 | } | ||
786 |
1/2✓ Branch 1 taken 3457 times.
✗ Branch 2 not taken.
|
3457 | ares_free_data(addresses); |
787 |
1/2✓ Branch 1 taken 3457 times.
✗ Branch 2 not taken.
|
3457 | resolver->system_resolvers_ = resolver->resolvers_; |
788 | |||
789 | 3457 | 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 | 1717 | 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 | 1717 | const unsigned num = names.size(); | |
814 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1717 times.
|
1717 | if (num == 0) |
815 | ✗ | return; | |
816 | |||
817 |
1/2✓ Branch 2 taken 1717 times.
✗ Branch 3 not taken.
|
1717 | vector<QueryInfo *> infos_ipv4(num, NULL); |
818 |
1/2✓ Branch 2 taken 1717 times.
✗ Branch 3 not taken.
|
1717 | vector<QueryInfo *> infos_ipv6(num, NULL); |
819 | |||
820 |
2/2✓ Branch 0 taken 1752 times.
✓ Branch 1 taken 1717 times.
|
3469 | for (unsigned i = 0; i < num; ++i) { |
821 |
3/4✓ Branch 1 taken 1752 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1721 times.
✓ Branch 4 taken 31 times.
|
1752 | if (skip[i]) |
822 | 1721 | continue; | |
823 | |||
824 |
2/2✓ Branch 1 taken 30 times.
✓ Branch 2 taken 1 times.
|
31 | if (!ipv4_only()) { |
825 |
2/4✓ Branch 3 taken 30 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 30 times.
✗ Branch 7 not taken.
|
30 | infos_ipv6[i] = new QueryInfo(&(*ipv6_addresses)[i], names[i], kRrAaaa); |
826 |
1/2✓ Branch 3 taken 30 times.
✗ Branch 4 not taken.
|
30 | ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_aaaa, |
827 | 30 | CallbackCares, infos_ipv6[i]); | |
828 | } | ||
829 |
2/4✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
|
31 | infos_ipv4[i] = new QueryInfo(&(*ipv4_addresses)[i], names[i], kRrA); |
830 |
1/2✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
|
31 | ares_search(*channel_, names[i].c_str(), ns_c_in, ns_t_a, CallbackCares, |
831 | 31 | infos_ipv4[i]); | |
832 | } | ||
833 | |||
834 | bool all_complete; | ||
835 | do { | ||
836 | 1734925 | all_complete = true; | |
837 |
1/2✓ Branch 1 taken 1734925 times.
✗ Branch 2 not taken.
|
1734925 | WaitOnCares(); |
838 |
2/2✓ Branch 0 taken 1735057 times.
✓ Branch 1 taken 1717 times.
|
1736774 | for (unsigned i = 0; i < num; ++i) { |
839 |
2/2✓ Branch 2 taken 122 times.
✓ Branch 3 taken 1733204 times.
|
3468383 | if ((infos_ipv4[i] && !infos_ipv4[i]->complete) |
840 |
8/8✓ Branch 0 taken 1733326 times.
✓ Branch 1 taken 1731 times.
✓ Branch 3 taken 121 times.
✓ Branch 4 taken 1732 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 117 times.
✓ Branch 8 taken 1733208 times.
✓ Branch 9 taken 1849 times.
|
3468383 | || (infos_ipv6[i] && !infos_ipv6[i]->complete)) { |
841 | 1733208 | all_complete = false; | |
842 | 1733208 | break; | |
843 | } | ||
844 | } | ||
845 |
2/2✓ Branch 0 taken 1733208 times.
✓ Branch 1 taken 1717 times.
|
1734925 | } 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 1752 times.
✓ Branch 1 taken 1717 times.
|
3469 | for (unsigned i = 0; i < num; ++i) { |
850 |
3/4✓ Branch 1 taken 1752 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1721 times.
✓ Branch 4 taken 31 times.
|
1752 | if (skip[i]) |
851 | 1721 | continue; | |
852 | |||
853 | 31 | Failures status = kFailOther; | |
854 | 31 | (*ttls)[i] = unsigned(-1); | |
855 |
1/2✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
|
31 | (*fqdns)[i] = ""; |
856 |
2/2✓ Branch 1 taken 30 times.
✓ Branch 2 taken 1 times.
|
31 | if (infos_ipv6[i]) { |
857 | 30 | status = infos_ipv6[i]->status; | |
858 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 7 times.
|
30 | if (status == kFailOk) { |
859 | 23 | (*ttls)[i] = std::min(infos_ipv6[i]->ttl, (*ttls)[i]); | |
860 |
1/2✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
|
23 | (*fqdns)[i] = infos_ipv6[i]->fqdn; |
861 | } | ||
862 | } | ||
863 |
1/2✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
|
31 | if (infos_ipv4[i]) { |
864 | 31 | (*ttls)[i] = std::min(infos_ipv4[i]->ttl, (*ttls)[i]); | |
865 |
2/2✓ Branch 2 taken 8 times.
✓ Branch 3 taken 23 times.
|
31 | if ((*fqdns)[i] == "") |
866 |
1/2✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
|
8 | (*fqdns)[i] = infos_ipv4[i]->fqdn; |
867 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 23 times.
|
31 | if (status != kFailOk) |
868 | 8 | status = infos_ipv4[i]->status; | |
869 | } | ||
870 | 31 | (*failures)[i] = status; | |
871 | } | ||
872 | |||
873 |
2/2✓ Branch 0 taken 1752 times.
✓ Branch 1 taken 1717 times.
|
3469 | for (unsigned i = 0; i < num; ++i) { |
874 |
2/2✓ Branch 1 taken 31 times.
✓ Branch 2 taken 1721 times.
|
1752 | delete infos_ipv4[i]; |
875 |
2/2✓ Branch 1 taken 30 times.
✓ Branch 2 taken 1722 times.
|
1752 | delete infos_ipv6[i]; |
876 | } | ||
877 | 1717 | } | |
878 | |||
879 | |||
880 | 3 | bool CaresResolver::SetResolvers(const vector<string> &resolvers) { | |
881 |
2/4✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
|
6 | const string address_list = JoinStrings(resolvers, ","); |
882 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | const int retval = ares_set_servers_csv(*channel_, address_list.c_str()); |
883 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (retval != ARES_SUCCESS) |
884 | ✗ | return false; | |
885 | |||
886 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | resolvers_ = resolvers; |
887 | 3 | return true; | |
888 | 3 | } | |
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 | 2 | 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 | 2 | memcpy(&ares_channelhead, *channel_, sizeof(ares_channelhead)); | |
914 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (ares_channelhead.domains) { |
915 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
|
5 | for (int i = 0; i < ares_channelhead.ndomains; ++i) { |
916 | 3 | free(ares_channelhead.domains[i]); | |
917 | } | ||
918 | 2 | free(ares_channelhead.domains); | |
919 | 2 | ares_channelhead.domains = NULL; | |
920 | } | ||
921 | |||
922 | 2 | ares_channelhead.ndomains = static_cast<int>(domains.size()); | |
923 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (ares_channelhead.ndomains > 0) { |
924 | 1 | ares_channelhead.domains = reinterpret_cast<char **>( | |
925 | 1 | smalloc(ares_channelhead.ndomains * sizeof(char *))); | |
926 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | for (int i = 0; i < ares_channelhead.ndomains; ++i) { |
927 | 2 | ares_channelhead.domains[i] = strdup(domains[i].c_str()); | |
928 | } | ||
929 | } | ||
930 | |||
931 | 2 | memcpy(*channel_, &ares_channelhead, sizeof(ares_channelhead)); | |
932 | |||
933 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | domains_ = domains; |
934 | 2 | 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 | 1734925 | 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 1734925 times.
✗ Branch 2 not taken.
|
1734925 | const int bitmask = ares_getsock(*channel_, socks, ARES_GETSOCK_MAXNUM); |
959 | 1734925 | unsigned num = 0; | |
960 |
1/2✓ Branch 0 taken 3468148 times.
✗ Branch 1 not taken.
|
3468148 | for (unsigned i = 0; i < ARES_GETSOCK_MAXNUM; ++i) { |
961 | 3468148 | pfd[i].events = 0; | |
962 | 3468148 | pfd[i].revents = 0; | |
963 |
2/2✓ Branch 0 taken 1733223 times.
✓ Branch 1 taken 1734925 times.
|
3468148 | if (ARES_GETSOCK_READABLE(bitmask, i)) { |
964 | 1733223 | pfd[i].fd = socks[i]; | |
965 | 1733223 | pfd[i].events |= POLLRDNORM | POLLIN; | |
966 | } | ||
967 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3468148 times.
|
3468148 | 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 1733223 times.
✓ Branch 1 taken 1734925 times.
|
3468148 | if (pfd[i].events != 0) |
972 | 1733223 | num++; | |
973 | else | ||
974 | 1734925 | break; | |
975 | } | ||
976 | |||
977 | 1734925 | int nfds = 0; | |
978 |
2/2✓ Branch 0 taken 1733223 times.
✓ Branch 1 taken 1702 times.
|
1734925 | if (num > 0) { |
979 | do { | ||
980 |
1/2✓ Branch 2 taken 1733223 times.
✗ Branch 3 not taken.
|
1733223 | nfds = poll(pfd, num, timeout_ms()); |
981 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1733223 times.
|
1733223 | 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 1733223 times.
|
1733223 | } while (nfds == -1); |
987 | } | ||
988 | |||
989 |
2/2✓ Branch 0 taken 1708 times.
✓ Branch 1 taken 1733217 times.
|
1734925 | 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 1708 times.
✗ Branch 2 not taken.
|
1708 | 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 1733217 times.
✓ Branch 1 taken 1733217 times.
|
3466434 | for (unsigned i = 0; i < num; ++i) { |
996 | 3466434 | ares_process_fd( | |
997 |
1/2✓ Branch 1 taken 1733217 times.
✗ Branch 2 not taken.
|
1733217 | *channel_, |
998 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 1733177 times.
|
1733217 | pfd[i].revents & (POLLRDNORM | POLLIN) ? pfd[i].fd : ARES_SOCKET_BAD, |
999 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1733217 times.
|
1733217 | pfd[i].revents & (POLLWRNORM | POLLOUT) ? pfd[i].fd |
1000 | : ARES_SOCKET_BAD); | ||
1001 | } | ||
1002 | } | ||
1003 | 1734925 | } | |
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 | 3223 | HostfileResolver *HostfileResolver::Create(const string &path, bool ipv4_only) { | |
1015 |
1/2✓ Branch 1 taken 3223 times.
✗ Branch 2 not taken.
|
3223 | HostfileResolver *resolver = new HostfileResolver(ipv4_only); |
1016 | |||
1017 |
1/2✓ Branch 1 taken 3223 times.
✗ Branch 2 not taken.
|
3223 | string hosts_file = path; |
1018 |
2/2✓ Branch 1 taken 3177 times.
✓ Branch 2 taken 46 times.
|
3223 | if (hosts_file == "") { |
1019 | 3177 | char *hosts_env = getenv("HOST_ALIASES"); | |
1020 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3175 times.
|
3177 | if (hosts_env != NULL) { |
1021 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | hosts_file = string(hosts_env); |
1022 | } else { | ||
1023 |
1/2✓ Branch 1 taken 3175 times.
✗ Branch 2 not taken.
|
3175 | hosts_file = "/etc/hosts"; |
1024 | } | ||
1025 | } | ||
1026 |
1/2✓ Branch 2 taken 3223 times.
✗ Branch 3 not taken.
|
3223 | resolver->fhosts_ = fopen(hosts_file.c_str(), "r"); |
1027 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3221 times.
|
3223 | if (!resolver->fhosts_) { |
1028 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | LogCvmfs(kLogDns, kLogDebug | kLogSyslogWarn, "failed to read host file %s", |
1029 | hosts_file.c_str()); | ||
1030 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | delete resolver; |
1031 | 2 | return NULL; | |
1032 | } | ||
1033 | 3221 | return resolver; | |
1034 | 3223 | } | |
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 | 6 | static bool SortNameLength(const string &a, const string &b) { | |
1043 | 6 | const unsigned len_a = a.length(); | |
1044 | 6 | const unsigned len_b = b.length(); | |
1045 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (len_a != len_b) |
1046 | 6 | return len_a > len_b; | |
1047 | ✗ | return a > b; | |
1048 | } | ||
1049 | |||
1050 | |||
1051 | /** | ||
1052 | * Creates a fresh reverse lookup map | ||
1053 | */ | ||
1054 | 1573 | 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 | 1573 | const unsigned num = names.size(); | |
1062 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1573 times.
|
1573 | if (num == 0) |
1063 | ✗ | return; | |
1064 | |||
1065 | 1573 | ParseHostFile(); | |
1066 |
2/2✓ Branch 0 taken 1595 times.
✓ Branch 1 taken 1573 times.
|
3168 | for (unsigned i = 0; i < num; ++i) { |
1067 |
3/4✓ Branch 1 taken 1595 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1560 times.
✓ Branch 4 taken 35 times.
|
1595 | if (skip[i]) |
1068 | 1560 | continue; | |
1069 | |||
1070 | 35 | vector<string> effective_names; | |
1071 |
5/6✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 33 times.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 33 times.
|
35 | if (!names[i].empty() && (names[i][names[i].length() - 1] == '.')) { |
1072 |
2/4✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
|
2 | effective_names.push_back(names[i].substr(0, names[i].length() - 1)); |
1073 | } else { | ||
1074 |
1/2✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
|
33 | effective_names.push_back(names[i]); |
1075 |
2/2✓ Branch 2 taken 6 times.
✓ Branch 3 taken 33 times.
|
39 | for (unsigned j = 0; j < domains().size(); ++j) { |
1076 |
3/6✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 11 not taken.
|
6 | 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 35 times.
✗ Branch 4 not taken.
|
35 | std::sort(effective_names.begin(), effective_names.end(), SortNameLength); |
1082 | |||
1083 | 35 | (*failures)[i] = kFailUnknownHost; | |
1084 |
1/2✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
|
35 | (*fqdns)[i] = names[i]; |
1085 |
2/2✓ Branch 1 taken 38 times.
✓ Branch 2 taken 10 times.
|
48 | for (unsigned j = 0; j < effective_names.size(); ++j) { |
1086 |
1/2✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
|
38 | const map<string, HostEntry>::iterator iter = host_map_.find( |
1087 | 38 | effective_names[j]); | |
1088 |
2/2✓ Branch 2 taken 25 times.
✓ Branch 3 taken 13 times.
|
38 | if (iter != host_map_.end()) { |
1089 |
1/2✓ Branch 7 taken 25 times.
✗ Branch 8 not taken.
|
75 | (*ipv4_addresses)[i].insert((*ipv4_addresses)[i].end(), |
1090 | 25 | iter->second.ipv4_addresses.begin(), | |
1091 | 25 | iter->second.ipv4_addresses.end()); | |
1092 |
1/2✓ Branch 7 taken 25 times.
✗ Branch 8 not taken.
|
75 | (*ipv6_addresses)[i].insert((*ipv6_addresses)[i].end(), |
1093 | 25 | iter->second.ipv6_addresses.begin(), | |
1094 | 25 | iter->second.ipv6_addresses.end()); | |
1095 | 25 | (*ttls)[i] = min_ttl_; | |
1096 |
1/2✓ Branch 3 taken 25 times.
✗ Branch 4 not taken.
|
25 | (*fqdns)[i] = effective_names[j]; |
1097 | 25 | (*failures)[i] = kFailOk; | |
1098 | 25 | break; | |
1099 | } // Host name found | ||
1100 | } // All possible names (search domains added) | ||
1101 | 35 | } | |
1102 | } | ||
1103 | |||
1104 | |||
1105 | 3223 | HostfileResolver::HostfileResolver(const bool ipv4_only) | |
1106 | 3223 | : Resolver(ipv4_only, 0, 0), fhosts_(NULL) { } | |
1107 | |||
1108 | |||
1109 | 12888 | HostfileResolver::~HostfileResolver() { | |
1110 |
2/2✓ Branch 0 taken 3220 times.
✓ Branch 1 taken 2 times.
|
6444 | if (fhosts_) |
1111 | 6440 | fclose(fhosts_); | |
1112 | 12888 | } | |
1113 | |||
1114 | |||
1115 | /** | ||
1116 | * TODO: this should be only necessary when the modification timestamp changed. | ||
1117 | */ | ||
1118 | 1573 | void HostfileResolver::ParseHostFile() { | |
1119 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1573 times.
|
1573 | assert(fhosts_); |
1120 |
1/2✓ Branch 1 taken 1573 times.
✗ Branch 2 not taken.
|
1573 | rewind(fhosts_); |
1121 | 1573 | host_map_.clear(); | |
1122 | |||
1123 | 1573 | string line; | |
1124 |
3/4✓ Branch 1 taken 6288 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4715 times.
✓ Branch 4 taken 1573 times.
|
6288 | while (GetLineFile(fhosts_, &line)) { |
1125 | char address[kIpMaxLength + 1]; | ||
1126 | char hostname[kHostnameMaxLength + 1]; | ||
1127 | int bytes_read; | ||
1128 | 4715 | size_t str_offset = 0; | |
1129 | |||
1130 | // strip comments | ||
1131 | 4715 | const size_t hash_pos = line.find_first_of('#'); | |
1132 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 4705 times.
|
4715 | if (hash_pos != string::npos) |
1133 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | line = line.substr(0, hash_pos); |
1134 | |||
1135 | // First token is an IP address | ||
1136 | 4715 | int ip_start_pos = -1, ip_end_pos = -1, scan_result; | |
1137 | 4715 | scan_result = sscanf(line.c_str(), " %n%*s%n", &ip_start_pos, &ip_end_pos); | |
1138 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 4701 times.
|
4715 | if (scan_result == EOF) |
1139 | 16 | continue; | |
1140 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4701 times.
|
4701 | assert(ip_start_pos != -1); |
1141 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4701 times.
|
4701 | assert(ip_end_pos != -1); |
1142 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4701 times.
|
4701 | if (ip_start_pos == ip_end_pos) |
1143 | ✗ | continue; | |
1144 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4699 times.
|
4701 | if (ip_end_pos - ip_start_pos > kIpMaxLength) { |
1145 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | LogCvmfs( |
1146 | kLogDns, kLogSyslogWarn, | ||
1147 | "Skipping line in hosts file due to invalid IP address format: %s", | ||
1148 | line.c_str()); | ||
1149 | 2 | continue; | |
1150 | } | ||
1151 | |||
1152 | 4699 | bytes_read = -1; | |
1153 | 4699 | scan_result = sscanf(line.c_str(), " %s%n", address, &bytes_read); | |
1154 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4699 times.
|
4699 | assert(scan_result == 1); |
1155 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4699 times.
|
4699 | assert(bytes_read != -1); |
1156 | 4699 | str_offset += bytes_read; | |
1157 | |||
1158 | // Next tokens are hostnames and aliases (we treat them as equal) | ||
1159 |
2/2✓ Branch 1 taken 15527 times.
✓ Branch 2 taken 4692 times.
|
20219 | while (str_offset < line.length()) { |
1160 | // check hostname length | ||
1161 | 15527 | int hostname_start_pos = -1, hostname_end_pos = -1; | |
1162 | 15527 | scan_result = sscanf(line.c_str() + str_offset, " %n%*s%n", | |
1163 | &hostname_start_pos, &hostname_end_pos); | ||
1164 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 15520 times.
|
15527 | if (scan_result == EOF) |
1165 | 7 | break; | |
1166 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15520 times.
|
15520 | assert(hostname_start_pos != -1); |
1167 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15520 times.
|
15520 | assert(hostname_end_pos != -1); |
1168 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15520 times.
|
15520 | if (hostname_start_pos == hostname_end_pos) |
1169 | ✗ | break; | |
1170 | |||
1171 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15518 times.
|
15520 | if (hostname_end_pos - hostname_start_pos > kHostnameMaxLength) { |
1172 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | LogCvmfs( |
1173 | kLogDns, kLogSyslogWarn, | ||
1174 | "Skipping invalid (too long) hostname in hosts file on line: %s", | ||
1175 | line.c_str()); | ||
1176 | 2 | str_offset += hostname_end_pos; | |
1177 | 2 | continue; | |
1178 | } | ||
1179 | |||
1180 | 15518 | bytes_read = -1; | |
1181 | 15518 | scan_result = sscanf(line.c_str() + str_offset, " %s%n", hostname, | |
1182 | &bytes_read); | ||
1183 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15518 times.
|
15518 | assert(scan_result == 1); |
1184 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15518 times.
|
15518 | assert(bytes_read != -1); |
1185 | 15518 | str_offset += bytes_read; | |
1186 | |||
1187 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15518 times.
|
15518 | if (hostname[strlen(hostname) - 1] == '.') |
1188 | ✗ | hostname[strlen(hostname) - 1] = 0; // strip the last character | |
1189 | |||
1190 |
2/4✓ Branch 2 taken 15518 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 15518 times.
✗ Branch 6 not taken.
|
15518 | const map<string, HostEntry>::iterator iter = host_map_.find(hostname); |
1191 |
2/2✓ Branch 2 taken 12419 times.
✓ Branch 3 taken 3099 times.
|
15518 | if (iter == host_map_.end()) { |
1192 | 12419 | HostEntry entry; | |
1193 |
4/6✓ Branch 2 taken 12419 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12419 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 9323 times.
✓ Branch 10 taken 3096 times.
|
12419 | if (IsIpv4Address(address)) |
1194 |
2/4✓ Branch 2 taken 9323 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9323 times.
✗ Branch 6 not taken.
|
9323 | entry.ipv4_addresses.push_back(address); |
1195 |
2/2✓ Branch 1 taken 3094 times.
✓ Branch 2 taken 2 times.
|
3096 | else if (!ipv4_only()) |
1196 |
2/4✓ Branch 2 taken 3094 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3094 times.
✗ Branch 6 not taken.
|
3094 | entry.ipv6_addresses.push_back(address); |
1197 |
3/6✓ Branch 2 taken 12419 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12419 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 12419 times.
✗ Branch 9 not taken.
|
12419 | host_map_[hostname] = entry; |
1198 | 12419 | } else { | |
1199 |
4/6✓ Branch 2 taken 3099 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3099 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 3 times.
✓ Branch 10 taken 3096 times.
|
3099 | if (IsIpv4Address(address)) |
1200 |
2/4✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
|
3 | iter->second.ipv4_addresses.push_back(address); |
1201 |
2/2✓ Branch 1 taken 3094 times.
✓ Branch 2 taken 2 times.
|
3096 | else if (!ipv4_only()) |
1202 |
2/4✓ Branch 3 taken 3094 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3094 times.
✗ Branch 7 not taken.
|
3094 | iter->second.ipv6_addresses.push_back(address); |
1203 | } | ||
1204 | } // Current line | ||
1205 | } // Hosts file | ||
1206 | 1573 | } | |
1207 | |||
1208 | |||
1209 | 3174 | bool HostfileResolver::SetSearchDomains(const vector<string> &domains) { | |
1210 | 3174 | domains_ = domains; | |
1211 | 3174 | 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 | 3174 | NormalResolver *NormalResolver::Create(const bool ipv4_only, | |
1229 | const unsigned retries, | ||
1230 | const unsigned timeout_ms) { | ||
1231 | 3174 | CaresResolver *cares_resolver = CaresResolver::Create(ipv4_only, retries, | |
1232 | timeout_ms); | ||
1233 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3174 times.
|
3174 | if (!cares_resolver) |
1234 | ✗ | return NULL; | |
1235 |
2/4✓ Branch 2 taken 3174 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3174 times.
✗ Branch 6 not taken.
|
3174 | HostfileResolver *hostfile_resolver = HostfileResolver::Create("", ipv4_only); |
1236 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3173 times.
|
3174 | if (!hostfile_resolver) { |
1237 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | delete cares_resolver; |
1238 | 1 | return NULL; | |
1239 | } | ||
1240 | 3173 | const bool retval = hostfile_resolver->SetSearchDomains( | |
1241 | cares_resolver->domains()); | ||
1242 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3173 times.
|
3173 | assert(retval); |
1243 | |||
1244 | 3173 | NormalResolver *normal_resolver = new NormalResolver(); | |
1245 | 3173 | normal_resolver->cares_resolver_ = cares_resolver; | |
1246 | 3173 | normal_resolver->hostfile_resolver_ = hostfile_resolver; | |
1247 | 3173 | normal_resolver->domains_ = cares_resolver->domains(); | |
1248 | 3173 | normal_resolver->resolvers_ = cares_resolver->resolvers(); | |
1249 | 3173 | normal_resolver->retries_ = cares_resolver->retries(); | |
1250 | 3173 | normal_resolver->timeout_ms_ = cares_resolver->timeout_ms(); | |
1251 | 3173 | return normal_resolver; | |
1252 | } | ||
1253 | |||
1254 | |||
1255 | /** | ||
1256 | * Makes only sense for the c-ares resolver. | ||
1257 | */ | ||
1258 | 1 | bool NormalResolver::SetResolvers(const vector<string> &resolvers) { | |
1259 | 1 | 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 | 1543 | 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 | 1543 | const unsigned num = names.size(); | |
1306 |
1/2✓ Branch 1 taken 1543 times.
✗ Branch 2 not taken.
|
1543 | hostfile_resolver_->DoResolve(names, skip, ipv4_addresses, ipv6_addresses, |
1307 | failures, ttls, fqdns); | ||
1308 |
1/2✓ Branch 1 taken 1543 times.
✗ Branch 2 not taken.
|
1543 | vector<bool> skip_cares = skip; |
1309 |
2/2✓ Branch 0 taken 1559 times.
✓ Branch 1 taken 1543 times.
|
3102 | for (unsigned i = 0; i < num; ++i) { |
1310 |
2/2✓ Branch 1 taken 1558 times.
✓ Branch 2 taken 1 times.
|
1559 | if ((*failures)[i] == kFailOk) |
1311 |
1/2✓ Branch 1 taken 1558 times.
✗ Branch 2 not taken.
|
1558 | skip_cares[i] = true; |
1312 | } | ||
1313 |
1/2✓ Branch 1 taken 1543 times.
✗ Branch 2 not taken.
|
1543 | cares_resolver_->DoResolve(names, skip_cares, ipv4_addresses, ipv6_addresses, |
1314 | failures, ttls, fqdns); | ||
1315 | 1543 | } | |
1316 | |||
1317 | |||
1318 | 3173 | NormalResolver::NormalResolver() | |
1319 | 3173 | : Resolver(false, 0, 0), cares_resolver_(NULL), hostfile_resolver_(NULL) { } | |
1320 | |||
1321 | |||
1322 | 12688 | NormalResolver::~NormalResolver() { | |
1323 |
1/2✓ Branch 0 taken 3172 times.
✗ Branch 1 not taken.
|
6344 | delete cares_resolver_; |
1324 |
1/2✓ Branch 0 taken 3172 times.
✗ Branch 1 not taken.
|
6344 | delete hostfile_resolver_; |
1325 | 12688 | } | |
1326 | |||
1327 | } // namespace dns | ||
1328 |