Branch data Line data Source code
1 : : // Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <config.h>
16 : :
17 : : #include <algorithm>
18 : : #include <functional>
19 : : #include <cassert>
20 : : #include <iostream>
21 : : #include <boost/bind.hpp>
22 : : #include <boost/foreach.hpp>
23 : :
24 : : #include <ctype.h>
25 : : #include <strings.h>
26 : :
27 : : #include <config.h>
28 : :
29 : : #include <exceptions/exceptions.h>
30 : : #include <dns/name.h>
31 : : #include <dns/rrclass.h>
32 : : #include <dns/rrttl.h>
33 : : #include <dns/rcode.h>
34 : : #include <dns/opcode.h>
35 : : #include <dns/question.h>
36 : : #include <resolve/resolver_interface.h>
37 : :
38 : : #include <asiolink/io_address.h>
39 : :
40 : : #include "address_entry.h"
41 : : #include "nameserver_address.h"
42 : : #include "nameserver_entry.h"
43 : : #include "nsas_log.h"
44 : :
45 : : using namespace isc::asiolink;
46 : : using namespace isc::nsas;
47 : : using namespace isc::dns;
48 : : using namespace std;
49 : :
50 : : namespace isc {
51 : : namespace nsas {
52 : :
53 : : namespace {
54 : :
55 : : // Just shorter type alias
56 : : typedef isc::util::locks::scoped_lock<isc::util::locks::recursive_mutex> Lock;
57 : :
58 : : }
59 : :
60 : : // Returns the list of addresses matching the given family
61 : : Fetchable::State
62 : 802132 : NameserverEntry::getAddresses(AddressVector& addresses,
63 : : AddressFamily family, bool expired_ok)
64 : : {
65 : : Lock lock(mutex_);
66 : :
67 : : // Check TTL
68 : 802132 : time_t now(time(NULL));
69 : : // We take = as well, so we catch TTL 0 correctly
70 : : // expiration_ == 0 means not set, the reason is we are UNREACHABLE or
71 : : // NOT_ASKED or IN_PROGRESS
72 [ + + ][ + + ]: 802132 : if (getState() != NOT_ASKED && expiration_ && expiration_ <= now) {
[ + + ][ + + ]
73 : 11 : setState(EXPIRED);
74 : : }
75 : :
76 [ + + ][ + + ]: 802132 : if (getState() == EXPIRED && !expired_ok) {
[ + + ]
77 : : return EXPIRED;
78 : : }
79 : :
80 [ + + + - ]: 802128 : switch (getState()) {
81 : : case IN_PROGRESS:
82 : : /*
83 : : * Did we receive the address already?
84 : : *
85 : : * We might have already received the addresses for this family
86 : : * and still wait for the other (in which case has_address_[family]
87 : : * will be true). We might already received a negative answer,
88 : : * in which case expect_address_[family] is false and
89 : : * has_address_[family] is false as well.
90 : : */
91 [ + + ][ + + ]: 2068 : if (!has_address_[family] && expect_address_[family]) {
92 : : return IN_PROGRESS;
93 : : }
94 : : // If we do not expect the address, then fall trough to READY
95 : : case EXPIRED: // If expired_ok, we pretend to be ready
96 : : case READY:
97 [ + + ]: 802059 : if (!has_address_[family]) {
98 : : return UNREACHABLE;
99 : : }
100 : : break; // OK, we give some answers
101 : : case NOT_ASKED:
102 : : case UNREACHABLE:
103 : : // Reject giving any data
104 : 29 : return (getState());
105 : : }
106 : :
107 : 802052 : boost::shared_ptr<NameserverEntry> self(shared_from_this());
108 : : // If any address is OK, just pass everything we have
109 [ + + ]: 802052 : if (family == ANY_OK) {
110 [ + + ][ + - ]: 1602083 : BOOST_FOREACH(const AddressEntry& entry, addresses_[V6_ONLY]) {
[ + - ][ + + ]
[ + + ]
111 [ + - ][ + - ]: 400023 : addresses.push_back(NameserverAddress(self, entry, V6_ONLY));
112 : : }
113 [ + + ][ + - ]: 2410177 : BOOST_FOREACH(const AddressEntry& entry, addresses_[V4_ONLY]) {
[ + - ][ + + ]
[ + + ]
114 [ + - ][ + - ]: 804070 : addresses.push_back(NameserverAddress(self, entry, V4_ONLY));
115 : : }
116 : : } else {
117 [ + + ][ + - ]: 45 : BOOST_FOREACH(const AddressEntry& entry, addresses_[family]) {
[ + - ][ + + ]
[ + + ]
118 [ + - ][ + - ]: 15 : addresses.push_back(NameserverAddress(self, entry, family));
119 : : }
120 : : }
121 [ + + ][ - + ]: 802052 : if (getState() == EXPIRED && expired_ok) {
[ + + ]
122 : : return READY;
123 : : }
124 : 802045 : return getState();
125 : : }
126 : :
127 : : // Return the address corresponding to the family
128 : : asiolink::IOAddress
129 : 3 : NameserverEntry::getAddressAtIndex(size_t index, AddressFamily family) const {
130 : : Lock lock(mutex_);
131 : :
132 [ - + ]: 3 : assert(index < addresses_[family].size());
133 : :
134 : 3 : return (addresses_[family][index].getAddress());
135 : : }
136 : :
137 : : // Set the address RTT to a specific value
138 : : void
139 : 14 : NameserverEntry::setAddressRTT(const IOAddress& address, uint32_t rtt) {
140 : : Lock lock(mutex_);
141 : :
142 : : // Search through the list of addresses for a match
143 : 14 : AddressFamily family(V4_ONLY);
144 : : for (;;) {
145 [ + + ][ + - ]: 74 : BOOST_FOREACH(AddressEntry& entry, addresses_[family]) {
[ + - ][ + + ]
[ + + ]
146 [ + + ]: 27 : if (entry.getAddress().equals(address)) {
147 : : entry.setRTT(rtt);
148 : : return;
149 : : }
150 : : }
151 : :
152 : : // Hack. C++ does not allow ++ on enums, enumerating trough them is pain
153 [ + - ]: 21 : switch (family) {
154 : : case V4_ONLY: family = V6_ONLY; break;
155 : : default: return;
156 : : }
157 : : }
158 : : }
159 : :
160 : : // Update the address's rtt
161 : : #define UPDATE_RTT_ALPHA 0.7
162 : : void
163 : 22003 : NameserverEntry::updateAddressRTTAtIndex(uint32_t rtt, size_t index,
164 : : AddressFamily family)
165 : : {
166 : : Lock lock(mutex_);
167 : :
168 : : //make sure it is a valid index
169 [ + - ]: 44006 : if(index >= addresses_[family].size()) return;
170 : :
171 : : // Smoothly update the rtt
172 : : // The algorithm is as the same as bind8/bind9:
173 : : // new_rtt = old_rtt * alpha + new_rtt * (1 - alpha), where alpha is a float number in [0, 1.0]
174 : : // The default value for alpha is 0.7
175 : 44006 : uint32_t old_rtt = addresses_[family][index].getRTT();
176 : : uint32_t new_rtt = (uint32_t)(old_rtt * UPDATE_RTT_ALPHA + rtt *
177 : 22003 : (1 - UPDATE_RTT_ALPHA));
178 [ + + ]: 22003 : if (new_rtt == 0) {
179 : 2000 : new_rtt = 1;
180 : : }
181 : 22003 : addresses_[family][index].setRTT(new_rtt);
182 [ + - ][ + - ]: 44006 : LOG_DEBUG(nsas_logger, NSAS_DBG_RTT, NSAS_UPDATE_RTT)
183 [ + - ][ + - ]: 44006 : .arg(addresses_[family][index].getAddress().toText())
184 [ + - ][ + - ]: 22003 : .arg(old_rtt).arg(new_rtt);
185 : : }
186 : :
187 : : void
188 : 22008 : NameserverEntry::updateAddressRTT(uint32_t rtt,
189 : : const asiolink::IOAddress& address, AddressFamily family)
190 : : {
191 : : Lock lock(mutex_);
192 [ + + ]: 32009 : for (size_t i(0); i < addresses_[family].size(); ++ i) {
193 [ + + ]: 32004 : if (addresses_[family][i].getAddress().equals(address)) {
194 : 22003 : updateAddressRTTAtIndex(rtt, i, family);
195 : 22008 : return;
196 : : }
197 : : }
198 : : }
199 : :
200 : : // Sets the address to be unreachable
201 : : void
202 : 5 : NameserverEntry::setAddressUnreachable(const IOAddress& address) {
203 : 5 : setAddressRTT(address, AddressEntry::UNREACHABLE);
204 : 5 : }
205 : :
206 : : /**
207 : : * \short A callback into the resolver.
208 : : *
209 : : * Whenever we ask the resolver something, this is created and the answer is
210 : : * fed back through this. It holds a shared pointer to the entry so it is not
211 : : * destroyed too soon.
212 : : */
213 : 176 : class NameserverEntry::ResolverCallback :
214 : : public isc::resolve::ResolverInterface::Callback {
215 : : public:
216 : : ResolverCallback(boost::shared_ptr<NameserverEntry> entry,
217 : : AddressFamily family, const RRType& type) :
218 : : entry_(entry),
219 : : family_(family),
220 : 264 : type_(type)
221 : : { }
222 : : /**
223 : : * \short We received the address successfully.
224 : : *
225 : : * This extracts the addresses out from the response and puts them
226 : : * inside the entry. It tries to reuse the address entries from before
227 : : * (if there were any), to keep their RTTs.
228 : : */
229 : 58 : virtual void success(MessagePtr response_message) {
230 : 58 : time_t now = time(NULL);
231 : :
232 : 58 : Lock lock(entry_->mutex_);
233 : :
234 : : // TODO: find the correct RRset, not simply the first
235 [ - + ]: 58 : if (!response_message) {
236 [ # # ][ # # ]: 0 : LOG_ERROR(nsas_logger, NSAS_NULL_RESPONSE).arg(entry_->getName());
[ # # ][ # # ]
237 : 0 : failureInternal(lock);
238 : 0 : return;
239 : :
240 [ - + ]: 58 : } else if (response_message->getRcode() != isc::dns::Rcode::NOERROR()) {
241 [ # # ][ # # ]: 0 : LOG_DEBUG(nsas_logger, NSAS_DBG_RESULTS, NSAS_ERROR_RESPONSE).
242 [ # # ][ # # ]: 0 : arg(response_message->getRcode()).arg(entry_->getName());
[ # # ][ # # ]
243 : 0 : failureInternal(lock);
244 : 0 : return;
245 : :
246 [ - + ]: 58 : } else if (
247 : 58 : response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
248 [ # # ][ # # ]: 0 : LOG_DEBUG(nsas_logger, NSAS_DBG_RESULTS, NSAS_EMPTY_RESPONSE).
249 [ # # ][ # # ]: 0 : arg(entry_->getName());
250 : 0 : failureInternal(lock);
251 : 0 : return;
252 : : }
253 : :
254 : : isc::dns::RRsetIterator rrsi =
255 : 116 : response_message->beginSection(isc::dns::Message::SECTION_ANSWER);
256 [ + - ]: 58 : const isc::dns::RRsetPtr response = *rrsi;
257 : :
258 : 58 : vector<AddressEntry> entries;
259 : :
260 [ + - ][ + - ]: 116 : if (response->getType() != type_ ||
[ - + ][ - + ]
261 [ + - ]: 58 : response->getClass() != RRClass(entry_->getClass()))
262 : : {
263 : : // Invalid response type or class
264 [ # # ][ # # ]: 0 : LOG_ERROR(nsas_logger, NSAS_WRONG_ANSWER)
[ # # ]
265 [ # # ][ # # ]: 0 : .arg(entry_->getName()).arg(type_)
[ # # ]
266 [ # # ][ # # ]: 0 : .arg(entry_->getClass()).arg(response->getType())
[ # # ]
267 [ # # ][ # # ]: 0 : .arg(response->getClass());
268 : :
269 [ # # ]: 0 : failureInternal(lock);
270 : : return;
271 : : }
272 : :
273 [ + - ][ + + ]: 205 : for (RdataIteratorPtr i(response->getRdataIterator());
274 [ + - ][ + - ]: 147 : !i->isLast(); i->next())
275 : : {
276 : : // Try to find the original value and reuse it
277 [ + - ][ + - ]: 178 : string address(i->getCurrent().toText());
278 : 89 : AddressEntry *found(NULL);
279 [ + + ][ + + ]: 110 : BOOST_FOREACH(AddressEntry& entry,
[ + + ][ + + ]
[ + + ]
280 : : entry_->previous_addresses_[family_])
281 : : {
282 [ + - ][ + + ]: 21 : if (entry.getAddress().toText() == address) {
283 : : // Good, found it.
284 : : found = &entry;
285 : : break;
286 : : }
287 : : }
288 : : // If we found it, use it. If not, create a new one.
289 : : entries.push_back(found ? *found : AddressEntry(
290 [ + + ][ + - ]: 89 : IOAddress(address), 1));
291 [ + - ][ + - ]: 178 : LOG_DEBUG(nsas_logger, NSAS_DBG_RESULTS, NSAS_FOUND_ADDRESS)
[ + - ]
292 [ + - ][ + - ]: 267 : .arg(address).arg(entry_->getName());
[ + - ]
293 : : }
294 : :
295 : : // We no longer need the previous set of addresses, we have
296 : : // the current ones now.
297 : 58 : entry_->previous_addresses_[family_].clear();
298 : :
299 [ - + ]: 58 : if (entries.empty()) {
300 : : // No data there, count it as a failure
301 [ # # ]: 0 : failureInternal(lock);
302 : : } else {
303 : : // We received the data, so mark it
304 : 58 : entry_->expect_address_[family_] = false;
305 : 58 : entry_->expect_address_[ANY_OK] =
306 : 58 : entry_->expect_address_[V4_ONLY] ||
307 [ + - ][ + + ]: 58 : entry_->expect_address_[V6_ONLY];
308 : : // Everything is here (all address families)
309 [ + + ]: 58 : if (!entry_->expect_address_[ANY_OK]) {
310 : 25 : entry_->setState(READY);
311 : : }
312 : : // We have some address
313 : 58 : entry_->has_address_[ANY_OK] =
314 : 58 : entry_->has_address_[family_] = true;
315 : : // Insert the entries inside
316 : 58 : entry_->addresses_[family_].swap(entries);
317 : : // Update the expiration time. If it is 0, it means we
318 : : // did not set it yet, so reset
319 [ + - ]: 58 : time_t expiration(now + response->getTTL().getValue());
320 [ + + ]: 58 : if (entry_->expiration_) {
321 : : // We expire at the time first address expires
322 : 44 : entry_->expiration_ = min(entry_->expiration_, expiration);
323 : : } else {
324 : : // We have no expiration time set, use this one
325 : 36 : entry_->expiration_ = expiration;
326 : : }
327 : : // Run the right callbacks
328 [ + - ]: 58 : dispatchCallbacks(lock);
329 : : }
330 : : }
331 : : /**
332 : : * \short The resolver failed to retrieve the data.
333 : : *
334 : : * So mark the current address family as unreachable.
335 : : */
336 : 95 : virtual void failure() {
337 [ + - ][ + - ]: 190 : LOG_DEBUG(nsas_logger, NSAS_DBG_RESULTS, NSAS_NS_LOOKUP_FAIL)
338 [ + - ][ + - ]: 285 : .arg(type_).arg(entry_->getName());
[ + - ]
339 : 95 : Lock lock(entry_->mutex_);
340 : 95 : failureInternal(lock);
341 : 95 : }
342 : : private:
343 : : boost::shared_ptr<NameserverEntry> entry_;
344 : : AddressFamily family_;
345 : : RRType type_;
346 : :
347 : : // Dispatches all relevant callbacks. Keeps lock unlocked afterwards.
348 : : // TODO: We might want to use recursive lock and get rid of this
349 : 153 : void dispatchCallbacks(Lock& lock)
350 : : {
351 : : // We dispatch ANY addresses if there is at last one address or
352 : : // there's no chance we'll get some in future
353 : 153 : bool dispatch_any = entry_->has_address_[ANY_OK] ||
354 [ + + ][ + + ]: 153 : !entry_->expect_address_[ANY_OK];
355 : : // Sort out the callbacks we want
356 : 153 : vector<CallbackPair> keep;
357 [ + - ]: 153 : vector<boost::shared_ptr<NameserverEntry::Callback> > dispatch;
358 [ + + ][ + - ]: 449 : BOOST_FOREACH(const CallbackPair &callback, entry_->callbacks_)
[ + - ][ + + ]
[ + + ]
359 : : {
360 [ + + ][ + + ]: 148 : if (callback.first == family_ || (dispatch_any &&
[ + + ]
361 : : callback.first == ANY_OK))
362 : : {
363 : 99 : dispatch.push_back(callback.second);
364 : : } else {
365 [ + - ]: 148 : keep.push_back(callback);
366 : : }
367 : : }
368 : : // Put there only the ones that we do not want, drop the rest
369 : 153 : keep.swap(entry_->callbacks_);
370 : : keep.clear();
371 : :
372 : : // We can't keep the lock while we execute callbacks
373 : : lock.unlock();
374 : : // Run all the callbacks
375 : : /*
376 : : * FIXME: This is not completely exception safe. If there's an
377 : : * exception in a callback, we lose the rest of them.
378 : : */
379 [ + + ][ + - ]: 351 : BOOST_FOREACH(const boost::shared_ptr<NameserverEntry::Callback>&
[ + - ][ + + ]
[ + + ]
380 : : callback, dispatch)
381 : : {
382 [ + - ]: 99 : (*callback)(entry_);
383 : : }
384 : 153 : }
385 : :
386 : : // Handle a failure to obtain data. Dispatches callbacks and leaves
387 : : // lock unlocked
388 : 95 : void failureInternal(Lock &lock) {
389 : : // Set state of the addresses
390 : 95 : entry_->expect_address_[family_] = false;
391 : 95 : entry_->expect_address_[ANY_OK] =
392 : 95 : entry_->expect_address_[V4_ONLY] ||
393 [ + + ][ + + ]: 95 : entry_->expect_address_[V6_ONLY];
394 : : // When we do not expect any more addresses, decide the state
395 [ + + ]: 95 : if (!entry_->expect_address_[ANY_OK]) {
396 [ + + ]: 75 : if (entry_->has_address_[ANY_OK]) {
397 : : // We have at last one kind of address, so OK
398 : 61 : entry_->setState(READY);
399 : : } else {
400 : : // No addresses :-(
401 : 14 : entry_->setState(UNREACHABLE);
402 : : }
403 : : }
404 : : // Drop the previous addresses, no use of them now
405 : 95 : entry_->previous_addresses_[family_].clear();
406 : : // Dispatch any relevant callbacks
407 : 95 : dispatchCallbacks(lock);
408 : 95 : }
409 : : };
410 : :
411 : : void
412 : 88 : NameserverEntry::askIP(isc::resolve::ResolverInterface* resolver,
413 : : const RRType& type, AddressFamily family)
414 : : {
415 : 88 : QuestionPtr question(new Question(Name(getName()), RRClass(getClass()),
416 [ + - ][ + - ]: 88 : type));
417 : : boost::shared_ptr<ResolverCallback> callback(new ResolverCallback(
418 [ + - ][ + - ]: 88 : shared_from_this(), family, type));
419 [ + - ]: 88 : resolver->resolve(question, callback);
420 : 88 : }
421 : :
422 : : void
423 : 103 : NameserverEntry::askIP(isc::resolve::ResolverInterface* resolver,
424 : : boost::shared_ptr<Callback> callback, AddressFamily family)
425 : : {
426 : : Lock lock(mutex_);
427 : :
428 [ + + ][ + + ]: 103 : if (getState() == EXPIRED || getState() == NOT_ASKED) {
[ + + ]
429 : : // We will request the addresses
430 : :
431 : : // Set internal state first
432 : : // We store the old addresses so we can pick their RTT when
433 : : // we get the same addresses again (most probably)
434 : 44 : previous_addresses_[V4_ONLY].clear();
435 : 44 : previous_addresses_[V6_ONLY].clear();
436 : 44 : addresses_[V4_ONLY].swap(previous_addresses_[V4_ONLY]);
437 : 44 : addresses_[V6_ONLY].swap(previous_addresses_[V6_ONLY]);
438 : 44 : setState(IN_PROGRESS);
439 : 88 : has_address_[V4_ONLY] = has_address_[V6_ONLY] = has_address_[ANY_OK] =
440 : 132 : false;
441 : 44 : expect_address_[V4_ONLY] = expect_address_[V6_ONLY] =
442 : 88 : expect_address_[ANY_OK] = true;
443 : 44 : expiration_ = 0;
444 : :
445 : : // Store the callback
446 [ + - ]: 44 : callbacks_.push_back(CallbackPair(family, callback));
447 : :
448 : : // Ask for both types of addresses
449 : : // We are unlocked here, as the callback from that might want to lock
450 : : lock.unlock();
451 : :
452 [ + - + - ]: 88 : LOG_DEBUG(nsas_logger, NSAS_DBG_TRACE, NSAS_FIND_NS_ADDRESS).arg(getName());
[ + - ][ + - ]
453 : 44 : askIP(resolver, RRType::A(), V4_ONLY);
454 : 44 : askIP(resolver, RRType::AAAA(), V6_ONLY);
455 : : // Make sure we end the routine when we are not locked
456 : 44 : return;
457 : : } else {
458 : : // We already asked. Do we expect this address type still to come?
459 [ + + ]: 59 : if (!expect_address_[family]) {
460 : : // We do not expect it to come, dispatch right away
461 : : lock.unlock();
462 [ + - ]: 4 : (*callback)(shared_from_this());
463 : : return;
464 : : } else {
465 : : // It will come in future, store the callback until then
466 [ + - ]: 55 : callbacks_.push_back(CallbackPair(family, callback));
467 : : }
468 : : }
469 : : }
470 : :
471 : : } // namespace dns
472 : 4 : } // namespace isc
|