nameserver_entry.cc 15.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15 16
#include <config.h>

17 18 19 20
#include <algorithm>
#include <functional>
#include <cassert>
#include <iostream>
21 22
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
23 24 25 26

#include <ctype.h>
#include <strings.h>

Michal Vaner's avatar
Michal Vaner committed
27
#include <config.h>
28

Michal Vaner's avatar
Michal Vaner committed
29 30 31 32
#include <exceptions/exceptions.h>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <dns/rrttl.h>
33 34
#include <dns/rcode.h>
#include <dns/opcode.h>
35
#include <dns/question.h>
36
#include <resolve/resolver_interface.h>
37 38

#include "address_entry.h"
39
#include "nameserver_address.h"
40 41
#include "nameserver_entry.h"

42
using namespace asiolink;
43
using namespace isc::nsas;
44 45
using namespace isc::dns;
using namespace std;
46 47 48 49

namespace isc {
namespace nsas {

50 51
namespace {

52
// Just shorter type alias
53
typedef isc::locks::scoped_lock<isc::locks::recursive_mutex> Lock;
54

55 56
}

57 58 59 60
// Returns the list of addresses matching the given family
Fetchable::State
NameserverEntry::getAddresses(AddressVector& addresses,
    AddressFamily family, bool expired_ok)
61
{
62 63
    Lock lock(mutex_);

64 65 66 67 68
    // Check TTL
    time_t now(time(NULL));
    // We take = as well, so we catch TTL 0 correctly
    // expiration_ == 0 means not set, the reason is we are UNREACHABLE or
    // NOT_ASKED or IN_PROGRESS
69
    if (getState() != NOT_ASKED && expiration_ && expiration_ <= now) {
70 71 72 73 74 75 76
        setState(EXPIRED);
    }

    if (getState() == EXPIRED && !expired_ok) {
        return EXPIRED;
    }

77 78
    switch (getState()) {
        case IN_PROGRESS:
79 80 81 82 83 84 85 86 87
            /*
             * Did we receive the address already?
             *
             * We might have already received the addresses for this family
             * and still wait for the other (in which case has_address_[family]
             * will be true). We might already received a negative answer,
             * in which case expect_address_[family] is false and
             * has_address_[family] is false as well.
             */
88 89 90 91
            if (!has_address_[family] && expect_address_[family]) {
                return IN_PROGRESS;
            }
            // If we do not expect the address, then fall trough to READY
92
        case EXPIRED: // If expired_ok, we pretend to be ready
93 94 95 96
        case READY:
            if (!has_address_[family]) {
                return UNREACHABLE;
            }
97
            break; // OK, we give some answers
98 99 100 101 102 103
        case NOT_ASKED:
        case UNREACHABLE:
            // Reject giving any data
            return (getState());
    }

104
    boost::shared_ptr<NameserverEntry> self(shared_from_this());
105 106
    // If any address is OK, just pass everything we have
    if (family == ANY_OK) {
107 108 109 110 111 112
        BOOST_FOREACH(const AddressEntry& entry, addresses_[V6_ONLY]) {
            addresses.push_back(NameserverAddress(self, entry, V6_ONLY));
        }
        BOOST_FOREACH(const AddressEntry& entry, addresses_[V4_ONLY]) {
            addresses.push_back(NameserverAddress(self, entry, V4_ONLY));
        }
113
    } else {
114 115 116
        BOOST_FOREACH(const AddressEntry& entry, addresses_[family]) {
            addresses.push_back(NameserverAddress(self, entry, family));
        }
117 118 119 120 121 122 123
    }
    if (getState() == EXPIRED && expired_ok) {
        return READY;
    }
    return getState();
}

124
// Return the address corresponding to the family
125 126
asiolink::IOAddress
NameserverEntry::getAddressAtIndex(size_t index, AddressFamily family) const {
127 128
    Lock lock(mutex_);

129
    assert(index < addresses_[family].size());
130

131
    return (addresses_[family][index].getAddress());
132 133
}

134
// Set the address RTT to a specific value
135 136 137
void
NameserverEntry::setAddressRTT(const IOAddress& address, uint32_t rtt) {
    Lock lock(mutex_);
138 139

    // Search through the list of addresses for a match
140 141 142 143 144 145 146
    AddressFamily family(V4_ONLY);
    for (;;) {
        BOOST_FOREACH(AddressEntry& entry, addresses_[family]) {
            if (entry.getAddress().equal(address)) {
                entry.setRTT(rtt);
                return;
            }
147 148
        }

149 150 151 152
        // Hack. C++ does not allow ++ on enums, enumerating trough them is pain
        switch (family) {
            case V4_ONLY: family = V6_ONLY; break;
            default: return;
153 154 155 156
        }
    }
}

157
// Update the address's rtt
158
#define UPDATE_RTT_ALPHA 0.7
159 160 161 162
void
NameserverEntry::updateAddressRTTAtIndex(uint32_t rtt, size_t index,
    AddressFamily family)
{
163 164
    Lock lock(mutex_);

165
    //make sure it is a valid index
166
    if(index >= addresses_[family].size()) return;
167 168 169 170 171

    // Smoothly update the rtt
    // The algorithm is as the same as bind8/bind9:
    //    new_rtt = old_rtt * alpha + new_rtt * (1 - alpha), where alpha is a float number in [0, 1.0]
    // The default value for alpha is 0.7
172 173 174 175
    uint32_t old_rtt = addresses_[family][index].getRTT();
    uint32_t new_rtt = (uint32_t)(old_rtt * UPDATE_RTT_ALPHA + rtt *
        (1 - UPDATE_RTT_ALPHA));
    addresses_[family][index].setRTT(new_rtt);
176 177
}

178 179 180 181 182 183 184 185 186 187 188 189 190
void
NameserverEntry::updateAddressRTT(uint32_t rtt,
    const asiolink::IOAddress& address, AddressFamily family)
{
    Lock lock(mutex_);
    for (size_t i(0); i < addresses_[family].size(); ++ i) {
        if (addresses_[family][i].getAddress().equal(address)) {
            updateAddressRTTAtIndex(rtt, i, family);
            return;
        }
    }
}

191
// Sets the address to be unreachable
192 193
void
NameserverEntry::setAddressUnreachable(const IOAddress& address) {
194 195 196
    setAddressRTT(address, AddressEntry::UNREACHABLE);
}

197 198 199 200 201 202
/**
 * \short A callback into the resolver.
 *
 * Whenever we ask the resolver something, this is created and the answer is
 * fed back trough this. It holds a shared pointer to the entry so it is not
 * destroyed too soon.
203
 */
204 205
class NameserverEntry::ResolverCallback :
        public isc::resolve::ResolverInterface::Callback {
206
    public:
207
        ResolverCallback(boost::shared_ptr<NameserverEntry> entry,
208
            AddressFamily family, const RRType& type) :
209
            entry_(entry),
210 211
            family_(family),
            type_(type)
212
        { }
213 214 215 216 217 218
        /**
         * \short We received the address successfully.
         *
         * This extracts the addresses out from the response and puts them
         * inside the entry. It tries to reuse the address entries from before (if there were any), to keep their RTTs.
         */
219
        virtual void success(MessagePtr response_message) {
220 221
            time_t now = time(NULL);

222 223
            Lock lock(entry_->mutex_);

224 225 226 227 228 229 230 231 232 233 234
            // TODO: find the correct RRset, not simply the first
            if (!response_message ||
                response_message->getRcode() != isc::dns::Rcode::NOERROR() ||
                response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
                failureInternal(lock);
            }
                
            isc::dns::RRsetIterator rrsi =
                response_message->beginSection(isc::dns::Message::SECTION_ANSWER);
            const isc::dns::RRsetPtr response = *rrsi;
            
235 236
            vector<AddressEntry> entries;

237 238
            if (response->getType() != type_ ||
                response->getClass() != RRClass(entry_->getClass()))
239
            {
240 241 242 243 244
                // TODO Log we got answer of different type
                failureInternal(lock);
                return;
            }

Michal Vaner's avatar
Michal Vaner committed
245 246 247
            for (RdataIteratorPtr i(response->getRdataIterator());
                !i->isLast(); i->next())
            {
248
                // Try to find the original value and reuse it
249
                string address(i->getCurrent().toText());
250 251 252
                AddressEntry *found(NULL);
                BOOST_FOREACH(AddressEntry& entry,
                    entry_->previous_addresses_[family_])
253 254
                {
                    if (entry.getAddress().toText() == address) {
255
                        // Good, found it.
256
                        found = &entry;
257 258 259
                        break;
                    }
                }
260
                // If we found it, use it. If not, create a new one.
261
                entries.push_back(found ? *found : AddressEntry(IOAddress(
262
                    i->getCurrent().toText()), 1));
263 264
            }

265 266
            // We no longer need the previous set of addresses, we have
            // the current ones now.
267 268
            entry_->previous_addresses_[family_].clear();

269 270 271
            if (entries.empty()) {
                // No data there, count it as a failure
                failureInternal(lock);
272
            } else {
273
                // We received the data, so mark it
274 275 276 277
                entry_->expect_address_[family_] = false;
                entry_->expect_address_[ANY_OK] =
                    entry_->expect_address_[V4_ONLY] ||
                    entry_->expect_address_[V6_ONLY];
278
                // Everything is here (all address families)
279 280 281 282 283 284
                if (!entry_->expect_address_[ANY_OK]) {
                    entry_->setState(READY);
                }
                // We have some address
                entry_->has_address_[ANY_OK] =
                    entry_->has_address_[family_] = true;
285
                // Insert the entries inside
286
                entry_->addresses_[family_].swap(entries);
287 288 289 290
                // Update the expiration time. If it is 0, it means we
                // did not set it yet, so reset
                time_t expiration(now + response->getTTL().getValue());
                if (entry_->expiration_) {
291
                    // We expire at the time first address expires
292 293
                    entry_->expiration_ = min(entry_->expiration_, expiration);
                } else {
294
                    // We have no expiration time set, use this one
295 296
                    entry_->expiration_ = expiration;
                }
297 298
                // Run the right callbacks
                dispatchCallbacks(lock);
299 300
            }
        }
301 302 303 304 305
        /**
         * \short The resolver failed to retrieve the data.
         *
         * So mark the current address family as unreachable.
         */
306
        virtual void failure() {
307 308
            Lock lock(entry_->mutex_);
            failureInternal(lock);
309 310
        }
    private:
311
        boost::shared_ptr<NameserverEntry> entry_;
312 313 314 315
        AddressFamily family_;
        RRType type_;

        // Dispatches all relevant callbacks. Keeps lock unlocked afterwards.
316
        // TODO: We might want to use recursive lock and get rid of this
317
        void dispatchCallbacks(Lock& lock)
318
        {
319 320 321 322 323 324
            // We dispatch ANY addresses if there is at last one address or
            // there's no chance we'll get some in future
            bool dispatch_any = entry_->has_address_[ANY_OK] ||
                !entry_->expect_address_[ANY_OK];
            // Sort out the callbacks we want
            vector<CallbackPair> keep;
325
            vector<boost::shared_ptr<NameserverEntry::Callback> > dispatch;
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
            BOOST_FOREACH(const CallbackPair &callback, entry_->callbacks_)
            {
                if (callback.first == family_ || (dispatch_any &&
                    callback.first == ANY_OK))
                {
                    dispatch.push_back(callback.second);
                } else {
                    keep.push_back(callback);
                }
            }
            // Put there only the ones that we do not want, drop the rest
            keep.swap(entry_->callbacks_);
            keep.clear();

            // We can't keep the lock while we execute callbacks
            lock.unlock();
            // Run all the callbacks
343
            /*
344 345
             * FIXME: This is not completely exception safe. If there's an
             * exception in a callback, we lose the rest of them.
346
             */
347
            BOOST_FOREACH(const boost::shared_ptr<NameserverEntry::Callback>&
348
                callback, dispatch)
349
            {
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
                (*callback)(entry_);
            }
        }

        // Handle a failure to optain data. Dispatches callbacks and leaves
        // lock unlocked
        void failureInternal(Lock &lock) {
            // Set state of the addresses
            entry_->expect_address_[family_] = false;
            entry_->expect_address_[ANY_OK] =
                entry_->expect_address_[V4_ONLY] ||
                entry_->expect_address_[V6_ONLY];
            // When we do not expect any more addresses, decide the state
            if (!entry_->expect_address_[ANY_OK]) {
                if (entry_->has_address_[ANY_OK]) {
                    // We have at last one kind of address, so OK
                    entry_->setState(READY);
                } else {
                    // No addresses :-(
                    entry_->setState(UNREACHABLE);
                }
371
            }
372 373
            // Drop the previous addresses, no use of them now
            entry_->previous_addresses_[family_].clear();
374 375
            // Dispatch any relevant callbacks
            dispatchCallbacks(lock);
376 377 378
        }
};

379
void
380 381
NameserverEntry::askIP(
    boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
382
    const RRType& type, AddressFamily family)
383 384
{
    QuestionPtr question(new Question(Name(getName()), RRClass(getClass()),
385
        type));
386
    boost::shared_ptr<ResolverCallback> callback(new ResolverCallback(
387
        shared_from_this(), family, type));
Michal Vaner's avatar
Michal Vaner committed
388
    resolver->resolve(question, callback);
389 390 391
}

void
392 393
NameserverEntry::askIP(
    boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
394
    boost::shared_ptr<Callback> callback, AddressFamily family)
395
{
396 397 398 399 400 401
    Lock lock(mutex_);

    if (getState() == EXPIRED || getState() == NOT_ASKED) {
        // We will request the addresses

        // Set internal state first
402 403
        // We store the old addresses so we can pick their RTT when
        // we get the same addresses again (most probably)
404 405 406 407
        previous_addresses_[V4_ONLY].clear();
        previous_addresses_[V6_ONLY].clear();
        addresses_[V4_ONLY].swap(previous_addresses_[V4_ONLY]);
        addresses_[V6_ONLY].swap(previous_addresses_[V6_ONLY]);
408 409 410 411 412
        setState(IN_PROGRESS);
        has_address_[V4_ONLY] = has_address_[V6_ONLY] = has_address_[ANY_OK] =
            false;
        expect_address_[V4_ONLY] = expect_address_[V6_ONLY] =
            expect_address_[ANY_OK] = true;
413
        expiration_ = 0;
414 415 416 417 418

        // Store the callback
        callbacks_.push_back(CallbackPair(family, callback));

        // Ask for both types of addresses
Michal Vaner's avatar
Michal Vaner committed
419 420
        // We are unlocked here, as the callback from that might want to lock
        lock.unlock();
421 422
        askIP(resolver, RRType::A(), V4_ONLY);
        askIP(resolver, RRType::AAAA(), V6_ONLY);
Michal Vaner's avatar
Michal Vaner committed
423 424
        // Make sure we end the routine when we are not locked
        return;
425 426 427 428 429
    } else {
        // We already asked. Do we expect this address type still to come?
        if (!expect_address_[family]) {
            // We do not expect it to come, dispatch right away
            lock.unlock();
430
            (*callback)(shared_from_this());
431 432 433 434 435
            return;
        } else {
            // It will come in future, store the callback until then
            callbacks_.push_back(CallbackPair(family, callback));
        }
436 437 438
    }
}

439 440
} // namespace dns
} // namespace isc