nsas_test.h 13.3 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
#ifndef __NSAS_TEST_H
#define __NSAS_TEST_H
17

Michal Vaner's avatar
Michal Vaner committed
18
/// \file nsas_test.h
19
///
20 21
/// Contains miscellaneous classes and other stuff to help with the nameserver
/// address store tests.
22 23

#include <string>
24
#include <vector>
25
#include <map>
26

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

29
#include <dns/message.h>
Michal Vaner's avatar
Michal Vaner committed
30 31 32
#include <dns/buffer.h>
#include <dns/rdata.h>
#include <dns/rrtype.h>
33
#include <dns/rrttl.h>
34 35
#include <dns/opcode.h>
#include <dns/rcode.h>
Michal Vaner's avatar
Michal Vaner committed
36
#include <dns/messagerenderer.h>
37
#include <dns/rdataclass.h>
38
#include <resolve/resolver_interface.h>
Michal Vaner's avatar
Michal Vaner committed
39
#include "../nsas_entry.h"
40 41 42

using namespace isc::dns::rdata;
using namespace isc::dns;
43

44 45 46 47 48 49 50 51 52 53 54 55
namespace {
    MessagePtr
    createResponseMessage(RRsetPtr answer_rrset)
    {
        MessagePtr response(new Message(Message::RENDER));
        response->setOpcode(Opcode::QUERY());
        response->setRcode(Rcode::NOERROR());
        response->addRRset(Message::SECTION_ANSWER, answer_rrset);
        return response;
    }
}

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
namespace isc {
namespace dns {

/// \brief Class Types
///
/// Very simple classes to provide a type for the RdataTest class below.
/// All they do is return the type code associated with the record type.

class A {
public:
    uint16_t getType() const
    {return RRType::A().getCode();}
};

class AAAA {
public:
    uint16_t getType() const
    {return RRType::AAAA().getCode();}
};

class MX {
public:
    uint16_t getType() const
    {return RRType::MX().getCode();}
};

/// \brief Hold Rdata
///
/// A concrete implementation of the Rdata class, this holds data for the
/// tests.  All RRs in the tests are either A, AAAA, NS or MX records, and
/// as a result the text form of the Rdata is a single uninterpreted string.
/// For this reason, a single class definition

template <typename T>
class RdataTest: public Rdata {
public:

    /// \brief Constructor
    ///
    /// Set the data in the object.
    ///
    /// \param v4address IPV4 address to store.  (The format of this address is
    /// not checked.)
99
    RdataTest(const std::string& data) : data_(data)
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
    {}

    /// \brief Convert Rdata to string
    ///
    /// This is the convenient interface to extract the information stored
    /// in this object into a form that can be used by the tests.
    virtual std::string toText() const {
        return (data_);
    }

    /// \brief Return type of Rdata
    ///
    /// Returns the type of the data.  May be useful in the tests, although this
    /// will not appear in the main code as this interface is not defined.
    virtual uint16_t getType() const {
        return (type_.getType());
    }

    /// \name Unused Methods
    ///
    /// These methods are not used in the tests.
    ///
    //@{
    /// \brief Render the \c Rdata in the wire format to a buffer
    virtual void toWire(OutputBuffer& buffer) const;

    /// \brief render the \Rdata in the wire format to a \c MessageRenderer
    virtual void toWire(MessageRenderer& renderer) const;
    
    /// \brief Comparison Method
    virtual int compare(const Rdata& other) const;
    //@}

private:
134
    std::string data_;          ///< Rdata itself
135 136 137 138
    T           type_;          ///< Identifies type of the Rdata
};

template <typename T>
Michal Vaner's avatar
Michal Vaner committed
139
void RdataTest<T>::toWire(OutputBuffer&) const {
140 141 142
}

template <typename T>
Michal Vaner's avatar
Michal Vaner committed
143
void RdataTest<T>::toWire(MessageRenderer&) const {
144 145 146
}

template <typename T>
Michal Vaner's avatar
Michal Vaner committed
147
int RdataTest<T>::compare(const Rdata&) const {
148 149 150
    return 0;
}

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
} // namespace dns
} // namespace isc

namespace isc {
namespace nsas {

/// \brief Test Entry Class
///
/// This is an element that can be stored in both the hash table and the
/// LRU list.

class TestEntry : public NsasEntry<TestEntry> {
public:

    /// \brief Constructor
    ///
    /// \param name Name that will be used for the object.  This will form
    /// part of the key.
    /// \param class_code Class associated with the object.
Michal Vaner's avatar
Michal Vaner committed
170
    TestEntry(std::string name, const isc::dns::RRClass& class_code) :
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
        name_(name), class_code_(class_code)
    {}

    /// \brief Virtual Destructor
    virtual ~TestEntry()
        {}

    /// \brief Return Hash Key
    ///
    /// This must be overridden in all classes derived from NsasEntry, and
    /// returns the hash key corresponding to the name and class.
    virtual HashKey hashKey() const {
        return HashKey(name_, class_code_);
    }

    /// \brief Get the Name
    ///
    /// \return Name given to this object
    virtual std::string getName() const {
        return name_;
    }

    /// \brief Set the Name
    ///
    /// \param name New name of the object
    virtual void setName(const std::string& name) {
        name_ = name;
    }

    /// \brief Get the Class
    ///
    /// \return Class code assigned to this object
Michal Vaner's avatar
Michal Vaner committed
203
    virtual const isc::dns::RRClass& getClass() const {
204 205 206 207 208 209
        return class_code_;
    }

    /// \brief Set the Class
    ///
    /// \param class_code New class code of the object
Michal Vaner's avatar
Michal Vaner committed
210
    virtual void setClass(const isc::dns::RRClass& class_code) {
211 212 213 214 215
        class_code_ = class_code;
    }

private:
    std::string name_;          ///< Name of the object
Michal Vaner's avatar
Michal Vaner committed
216
    isc::dns::RRClass    class_code_;    ///< Class of the object
217 218 219 220 221 222 223
};

/// \brief isc::nsas Constants
///
/// Some constants used in the various tests.

static const uint32_t HASHTABLE_DEFAULT_SIZE = 1009; ///< First prime above 1000
224 225 226 227

} // namespace nsas
} // namespace isc

228 229 230 231
namespace {

using namespace std;

232 233 234 235
/*
 * This pretends to be a resolver. It stores the queries and
 * they can be answered.
 */
236
class TestResolver : public isc::resolve::ResolverInterface {
Michal Vaner's avatar
Michal Vaner committed
237
    private:
Michal Vaner's avatar
Michal Vaner committed
238 239
        bool checkIndex(size_t index) {
            return (requests.size() > index);
Michal Vaner's avatar
Michal Vaner committed
240
        }
241

242
        typedef std::map<isc::dns::Question, RRsetPtr >
243 244
            PresetAnswers;
        PresetAnswers answers_;
245 246 247
    public:
        typedef pair<QuestionPtr, CallbackPtr> Request;
        vector<Request> requests;
Michal Vaner's avatar
Michal Vaner committed
248
        virtual void resolve(const QuestionPtr& q, const CallbackPtr& c) {
249 250 251 252 253
            PresetAnswers::iterator it(answers_.find(*q));
            if (it == answers_.end()) {
                requests.push_back(Request(q, c));
            } else {
                if (it->second) {
254
                    c->success(createResponseMessage(it->second));
255 256 257 258 259 260 261 262 263 264 265 266
                } else {
                    c->failure();
                }
            }
        }

        /*
         * Add a preset answer. If shared_ptr() is passed (eg. NULL),
         * it will generate failure. If the question is not preset,
         * it goes to requests and you can answer later.
         */
        void addPresetAnswer(const isc::dns::Question& question,
267
            RRsetPtr answer)
268 269
        {
            answers_[question] = answer;
270
        }
Michal Vaner's avatar
Michal Vaner committed
271 272 273 274

        // Thrown if the query at the given index does not exist.
        class NoSuchRequest : public std::exception { };

Michal Vaner's avatar
Michal Vaner committed
275 276 277
        // Thrown if the answer does not match the query
        class DifferentRequest : public std::exception { };

278
        QuestionPtr operator[](size_t index) {
Michal Vaner's avatar
Michal Vaner committed
279 280 281
            if (index >= requests.size()) {
                throw NoSuchRequest();
            }
282 283
            return (requests[index].first);
        }
284
        /*
285 286
         * Looks if the two provided requests in resolver are A and AAAA.
         * Sorts them so index1 is A.
Michal Vaner's avatar
Michal Vaner committed
287 288
         *
         * Returns false if there aren't enough elements
289
         */
Michal Vaner's avatar
Michal Vaner committed
290
        bool asksIPs(const Name& name, size_t index1, size_t index2) {
291
            size_t max = (index1 < index2) ? index2 : index1;
Michal Vaner's avatar
Michal Vaner committed
292 293 294
            if (!checkIndex(max)) {
                return false;
            }
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
            EXPECT_EQ(name, (*this)[index1]->getName());
            EXPECT_EQ(name, (*this)[index2]->getName());
            EXPECT_EQ(RRClass::IN(), (*this)[index1]->getClass());
            EXPECT_EQ(RRClass::IN(), (*this)[index2]->getClass());
            // If they are the other way around, swap
            if ((*this)[index1]->getType() == RRType::AAAA() &&
                (*this)[index2]->getType() == RRType::A())
            {
                TestResolver::Request tmp((*this).requests[index1]);
                (*this).requests[index1] =
                    (*this).requests[index2];
                (*this).requests[index2] = tmp;
            }
            // Check the correct addresses
            EXPECT_EQ(RRType::A(), (*this)[index1]->getType());
            EXPECT_EQ(RRType::AAAA(), (*this)[index2]->getType());
Michal Vaner's avatar
Michal Vaner committed
311
            return (true);
312
        }
313 314 315 316 317

        /*
         * Sends a simple answer to a query.
         * Provide index of a query and the address to pass.
         */
318 319 320
        void answer(size_t index, const Name& name, const RRType& type,
            const rdata::Rdata& rdata, size_t TTL = 100)
        {
Michal Vaner's avatar
Michal Vaner committed
321 322 323
            if (index >= requests.size()) {
                throw NoSuchRequest();
            }
324
            RRsetPtr set(new RRset(name, RRClass::IN(),
325
                type, RRTTL(TTL)));
326
            set->addRdata(rdata);
327
            requests[index].second->success(createResponseMessage(set));
328
        }
Michal Vaner's avatar
Michal Vaner committed
329

330
        void provideNS(size_t index,
331
            RRsetPtr nameservers)
332
        {
Michal Vaner's avatar
Michal Vaner committed
333 334 335 336 337 338 339 340
            if (index >= requests.size()) {
                throw NoSuchRequest();
            }
            if (requests[index].first->getName() != nameservers->getName() ||
                requests[index].first->getType() != RRType::NS())
            {
                throw DifferentRequest();
            }
341
            requests[index].second->success(createResponseMessage(nameservers));
Michal Vaner's avatar
Michal Vaner committed
342
        }
343 344
};

Michal Vaner's avatar
Michal Vaner committed
345 346 347 348 349 350 351
// String constants.  These should end in a dot.
static const std::string EXAMPLE_CO_UK("example.co.uk.");
static const std::string EXAMPLE_NET("example.net.");
static const std::string MIXED_EXAMPLE_CO_UK("EXAmple.co.uk.");

class TestWithRdata : public ::testing::Test {
protected:
Michal Vaner's avatar
Michal Vaner committed
352
    typedef boost::shared_ptr<RRset> RRsetPtr;
Michal Vaner's avatar
Michal Vaner committed
353 354 355 356 357 358
    /// \brief Constructor
    ///
    /// Initializes the RRsets used in the tests.  The RRsets themselves have to
    /// be initialized with the basic data on their construction. The Rdata for
    /// them is added in SetUp().
    TestWithRdata() :
Michal Vaner's avatar
Michal Vaner committed
359
        rrv4_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::A(),
Michal Vaner's avatar
Michal Vaner committed
360
            RRTTL(1200))),
Michal Vaner's avatar
Michal Vaner committed
361
        rrcase_(new RRset(Name(MIXED_EXAMPLE_CO_UK), RRClass::IN(),
Michal Vaner's avatar
Michal Vaner committed
362
            RRType::A(), RRTTL(1200))),
Michal Vaner's avatar
Michal Vaner committed
363
        rrch_(new RRset(Name(EXAMPLE_CO_UK), RRClass::CH(), RRType::A(),
Michal Vaner's avatar
Michal Vaner committed
364
            RRTTL(1200))),
Michal Vaner's avatar
Michal Vaner committed
365
        rrns_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::NS(),
Michal Vaner's avatar
Michal Vaner committed
366
            RRTTL(1200))),
Michal Vaner's avatar
Michal Vaner committed
367
        rr_single_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
Michal Vaner's avatar
Michal Vaner committed
368
            RRType::NS(), RRTTL(600))),
Michal Vaner's avatar
Michal Vaner committed
369
        rr_empty_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
Michal Vaner's avatar
Michal Vaner committed
370
            RRType::NS(), RRTTL(600))),
Michal Vaner's avatar
Michal Vaner committed
371
        rrv6_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
Michal Vaner's avatar
Michal Vaner committed
372
            RRType::AAAA(), RRTTL(900))),
Michal Vaner's avatar
Michal Vaner committed
373
        rrnet_(new RRset(Name(EXAMPLE_NET), RRClass::IN(), RRType::A(),
Michal Vaner's avatar
Michal Vaner committed
374 375
            RRTTL(600))),
        ns_name_("ns.example.net.")
Michal Vaner's avatar
Michal Vaner committed
376 377 378 379 380 381 382 383 384
    {}

    /// \brief Add Rdata to RRsets
    ///
    /// The data are added as const pointers to avoid the stricter type checking
    /// applied by the Rdata code.  There is no need for it in these tests.
    virtual void SetUp() {

        // A records
Michal Vaner's avatar
Michal Vaner committed
385 386 387
        rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("1.2.3.4")));
        rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("5.6.7.8")));
        rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("9.10.11.12")));
Michal Vaner's avatar
Michal Vaner committed
388 389

        // A records
Michal Vaner's avatar
Michal Vaner committed
390
        rrcase_->addRdata(ConstRdataPtr(new RdataTest<A>("13.14.15.16")));
Michal Vaner's avatar
Michal Vaner committed
391 392 393

        // No idea what Chaosnet address look like other than they are 16 bits
        // The fact that they are type A is probably also incorrect.
Michal Vaner's avatar
Michal Vaner committed
394
        rrch_->addRdata(ConstRdataPtr(new RdataTest<A>("1324")));
Michal Vaner's avatar
Michal Vaner committed
395 396

        // NS records take a single name
397 398
        rrns_->addRdata(rdata::generic::NS("example.fr"));
        rrns_->addRdata(rdata::generic::NS("example.de"));
Michal Vaner's avatar
Michal Vaner committed
399 400

        // Single NS record with 0 TTL
Michal Vaner's avatar
Michal Vaner committed
401
        rr_single_->addRdata(rdata::generic::NS(ns_name_));
Michal Vaner's avatar
Michal Vaner committed
402 403

        // AAAA records
Michal Vaner's avatar
Michal Vaner committed
404 405
        rrv6_->addRdata(ConstRdataPtr(new RdataTest<AAAA>("2001::1002")));
        rrv6_->addRdata(ConstRdataPtr(new RdataTest<AAAA>("dead:beef:feed::")));
Michal Vaner's avatar
Michal Vaner committed
406 407

        // A record for example.net
Michal Vaner's avatar
Michal Vaner committed
408
        rrnet_->addRdata(ConstRdataPtr(new RdataTest<A>("17.18.18.20")));
Michal Vaner's avatar
Michal Vaner committed
409 410 411
    }

    /// \brief Data for the tests
Michal Vaner's avatar
Michal Vaner committed
412 413 414 415 416 417 418 419
    RRsetPtr rrv4_;           ///< Standard RRSet - IN, A, lowercase name
    RRsetPtr rrcase_;         ///< Mixed-case name
    RRsetPtr rrch_;           ///< Non-IN RRset (Chaos in this case)
    RRsetPtr rrns_;           ///< NS RRset
    RRsetPtr rr_single_;      ///< NS RRset with single NS
    RRsetPtr rr_empty_;       ///< NS RRset without any nameservers
    RRsetPtr rrv6_;           ///< Standard RRset, IN, AAAA, lowercase name
    RRsetPtr rrnet_;          ///< example.net A RRset
Michal Vaner's avatar
Michal Vaner committed
420
    Name ns_name_;  ///< Nameserver name of ns.example.net
Michal Vaner's avatar
Michal Vaner committed
421 422
};

423 424
} // Empty namespace

425
#endif // __NSAS_TEST_H