// Copyright (C) 2012  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.

#include <exceptions/exceptions.h>

#include <util/buffer.h>
#include <util/memory_segment_local.h>

#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rrset.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <dns/rrttl.h>

#include <datasrc/memory/segment_object_holder.h>
#include <datasrc/memory/rdata_serialization.h>
#include <datasrc/memory/rdataset.h>

#include <testutils/dnsmessage_test.h>

#include <gtest/gtest.h>

#include <boost/lexical_cast.hpp>

#include <string>

using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::datasrc::memory;
using namespace isc::testutils;
using isc::datasrc::memory::detail::SegmentObjectHolder;
using boost::lexical_cast;

namespace {

class RdataSetTest : public ::testing::Test {
protected:
    RdataSetTest() :
        // 1076895760 = 0x40302010.  Use this so we fill in all 8-bit "field"
        // of the 32-bit TTL
        a_rrset_(textToRRset("www.example.com. 1076895760 IN A 192.0.2.1")),
        rrsig_rrset_(textToRRset("www.example.com. 1076895760 IN RRSIG "
                                 "A 5 2 3600 20120814220826 20120715220826 "
                                 "1234 example.com. FAKE"))
    {}
    void TearDown() {
        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
    }

    ConstRRsetPtr a_rrset_, rrsig_rrset_;
    isc::util::MemorySegmentLocal mem_sgmt_;
    RdataEncoder encoder_;
};

// Convert the given 32-bit integer (network byte order) to the corresponding
// RRTTL object.
RRTTL
restoreTTL(const void* ttl_data) {
    isc::util::InputBuffer b(ttl_data, sizeof(uint32_t));
    return (RRTTL(b));
}

// A helper callback for checkRdataSet.  This confirms the given data
// is the expected in::A RDATA (the value is taken from the RdataSetTest
// constructor).
void
checkData(const void* data, size_t size) {
    isc::util::InputBuffer b(data, size);
    EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1")));
}

// This is a set of checks for an RdataSet created with some simple
// conditions.  with_rrset/with_rrsig is true iff the RdataSet is supposed to
// contain normal/RRSIG RDATA.
void
checkRdataSet(const RdataSet& rdataset, bool with_rrset, bool with_rrsig) {
    EXPECT_FALSE(rdataset.next); // by default the next pointer should be NULL
    EXPECT_EQ(RRType::A(), rdataset.type);
    // See the RdataSetTest constructor for the magic number.
    EXPECT_EQ(RRTTL(1076895760), restoreTTL(rdataset.getTTLData()));
    EXPECT_EQ(with_rrset ? 1 : 0, rdataset.getRdataCount());
    EXPECT_EQ(with_rrsig ? 1 : 0, rdataset.getSigRdataCount());

    // A simple test for the data content.  Details tests for the encoder/
    // reader should be basically sufficient for various cases of the data,
    // and the fact that this test doesn't detect memory leak should be
    // reasonably sufficient that the implementation handles the data region
    // correctly.  Here we check one simple case for a simple form of RDATA,
    // mainly for checking the behavior of getDataBuf().
    RdataReader reader(RRClass::IN(), RRType::A(),
                       reinterpret_cast<const uint8_t*>(
                           rdataset.getDataBuf()),
                       rdataset.getRdataCount(), rdataset.getSigRdataCount(),
                       &RdataReader::emptyNameAction, checkData);
    reader.iterate();
}

TEST_F(RdataSetTest, create) {
    // A simple case of creating an RdataSet.  Confirming the resulting
    // fields have the expected values, and then destroying it (TearDown()
    // would detect any memory leak)
    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
                                          ConstRRsetPtr());
    checkRdataSet(*rdataset, true, false);
    RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());
}

TEST_F(RdataSetTest, getNext) {
    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
                                          ConstRRsetPtr());

    // By default, the next pointer should be NULL (already tested in other
    // test cases), which should be the case with getNext().  We test both
    // mutable and immutable versions of getNext().
    EXPECT_EQ(static_cast<RdataSet*>(NULL), rdataset->getNext());
    EXPECT_EQ(static_cast<const RdataSet*>(NULL),
              static_cast<const RdataSet*>(rdataset)->getNext());

    // making a link (it would form an infinite loop, but it doesn't matter
    // in this test), and check the pointer returned by getNext().
    rdataset->next = rdataset;
    EXPECT_EQ(rdataset, static_cast<const RdataSet*>(rdataset)->getNext());

    RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());
}

TEST_F(RdataSetTest, find) {
    // Create some RdataSets and make a chain of them.
    SegmentObjectHolder<RdataSet, RRClass> holder1(
        mem_sgmt_,
        RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr()),
        RRClass::IN());
    ConstRRsetPtr aaaa_rrset =
        textToRRset("www.example.com. 1076895760 IN AAAA 2001:db8::1");
    SegmentObjectHolder<RdataSet, RRClass> holder2(
        mem_sgmt_,
        RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset, ConstRRsetPtr()),
        RRClass::IN());
    ConstRRsetPtr sigonly_rrset =
        textToRRset("www.example.com. 1076895760 IN RRSIG "
                    "TXT 5 2 3600 20120814220826 20120715220826 "
                    "1234 example.com. FAKE");
    SegmentObjectHolder<RdataSet, RRClass> holder3(
        mem_sgmt_,
        RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(), sigonly_rrset),
        RRClass::IN());

    RdataSet* rdataset_a = holder1.get();
    RdataSet* rdataset_aaaa = holder2.get();
    RdataSet* rdataset_sigonly = holder3.get();
    RdataSet* rdataset_null = NULL;
    rdataset_a->next = rdataset_aaaa;
    rdataset_aaaa->next = rdataset_sigonly;

    // If a non-RRSIG part of rdataset exists for the given type, it will be
    // returned regardless of the value of sigonly_ok.  If it's RRSIG-only
    // rdataset, it returns non NULL iff sigonly_ok is explicitly set to true.
    EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a, RRType::AAAA()));
    EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a, RRType::AAAA(), true));
    EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a, RRType::AAAA(), false));

    EXPECT_EQ(rdataset_null, RdataSet::find(rdataset_a, RRType::TXT()));
    EXPECT_EQ(rdataset_sigonly, RdataSet::find(rdataset_a, RRType::TXT(),
                                               true));
    EXPECT_EQ(rdataset_null, RdataSet::find(rdataset_a, RRType::TXT(), false));

    // Same tests for the const version of find().
    const RdataSet* rdataset_a_const = holder1.get();
    EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a_const, RRType::AAAA()));
    EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a_const, RRType::AAAA(),
                                            true));
    EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a_const, RRType::AAAA(),
                                            false));

    EXPECT_EQ(rdataset_null, RdataSet::find(rdataset_a_const, RRType::TXT()));
    EXPECT_EQ(rdataset_sigonly, RdataSet::find(rdataset_a_const, RRType::TXT(),
                                               true));
    EXPECT_EQ(rdataset_null, RdataSet::find(rdataset_a_const, RRType::TXT(),
                                            false));
}

// A helper function to create an RRset containing the given number of
// unique RDATAs.
ConstRRsetPtr
getRRsetWithRdataCount(size_t rdata_count) {
    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::TXT(),
                             RRTTL(3600)));
    for (size_t i = 0; i < rdata_count; ++i) {
        rrset->addRdata(rdata::createRdata(RRType::TXT(), RRClass::IN(),
                                           lexical_cast<std::string>(i)));
    }
    return (rrset);
}

TEST_F(RdataSetTest, createManyRRs) {
    // RRset with possible maximum number of RDATAs
    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_,
                                          getRRsetWithRdataCount(8191),
                                          ConstRRsetPtr());
    EXPECT_EQ(8191, rdataset->getRdataCount());
    EXPECT_EQ(0, rdataset->getSigRdataCount());
    RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());

    // Exceeding that will result in an exception.
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
                                  getRRsetWithRdataCount(8192),
                                  ConstRRsetPtr()),
                 RdataSetError);
    // To be very sure even try larger number than the threshold
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
                                  getRRsetWithRdataCount(65535),
                                  ConstRRsetPtr()),
                 RdataSetError);
}

TEST_F(RdataSetTest, createWithRRSIG) {
    // Normal case.
    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
                                          rrsig_rrset_);
    checkRdataSet(*rdataset, true, true);
    RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());

    // Unusual case: TTL doesn't match.  This implementation accepts that,
    // using the TTL of the covered RRset.
    ConstRRsetPtr rrsig_badttl(textToRRset(
                                   "www.example.com. 3600 IN RRSIG "
                                   "A 5 2 3600 20120814220826 "
                                   "20120715220826 1234 example.com. FAKE"));
    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, rrsig_badttl);
    checkRdataSet(*rdataset, true, true);
    RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());
}

// A helper function to create an RRSIG RRset containing the given number of
// unique RDATAs.
ConstRRsetPtr
getRRSIGWithRdataCount(size_t sig_count) {
    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(),
                             RRType::RRSIG(), RRTTL(3600)));
    // We use a base wire-format image and tweak the original TTL field to
    // generate unique RDATAs in the loop.  (Creating them from corresponding
    // text is simpler, but doing so for a large number of RRSIGs is
    // relatively heavy and could be too long for unittests).
    ConstRdataPtr rrsig_base =
        rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
                           "A 5 2 3600 20120814220826 20120715220826 1234 "
                           "example.com. FAKE");
    isc::util::OutputBuffer ob(0);
    rrsig_base->toWire(ob);
    for (size_t i = 0; i < sig_count; ++i) {
        ob.writeUint16At((i >> 16) & 0xffff, 4);
        ob.writeUint16At(i & 0xffff, 6);
        isc::util::InputBuffer ib(ob.getData(), ob.getLength());
        rrset->addRdata(rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
                                           ib, ib.getLength()));
    }
    return (rrset);
}

TEST_F(RdataSetTest, createManyRRSIGs) {
    // 7 has a special meaning in the implementation: if the number of the
    // RRSIGs reaches this value, an extra 'sig count' field will be created.
    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
                                          getRRSIGWithRdataCount(7));
    EXPECT_EQ(7, rdataset->getSigRdataCount());
    RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());

    // 8 would cause overflow in the normal 3-bit field if there were no extra
    // count field.
    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
                                getRRSIGWithRdataCount(8));
    EXPECT_EQ(8, rdataset->getSigRdataCount());
    RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());

    // Up to 2^16-1 RRSIGs are allowed (although that would be useless
    // in practice)
    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
                                getRRSIGWithRdataCount(65535));
    EXPECT_EQ(65535, rdataset->getSigRdataCount());
    RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());

    // Exceeding this limit will result in an exception.
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
                                  getRRSIGWithRdataCount(65536)),
                 RdataSetError);
    // To be very sure even try larger number than the threshold
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
                                  getRRSIGWithRdataCount(70000)),
                 RdataSetError);
}

TEST_F(RdataSetTest, createWithRRSIGOnly) {
    // A rare, but allowed, case: RdataSet without the main RRset but with
    // RRSIG.
    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
                                          rrsig_rrset_);
    checkRdataSet(*rdataset, false, true);
    RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());
}

TEST_F(RdataSetTest, badCeate) {
    // Neither the RRset nor RRSIG RRset is given
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
                                  ConstRRsetPtr()), isc::BadValue);

    // Empty RRset (An RRset without RDATA)
    ConstRRsetPtr empty_rrset(new RRset(Name("example.com"), RRClass::IN(),
                                        RRType::A(), RRTTL(3600)));
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset,
                                  ConstRRsetPtr()), isc::BadValue);
    ConstRRsetPtr empty_rrsig(new RRset(Name("example.com"), RRClass::IN(),
                                        RRType::RRSIG(), RRTTL(3600)));
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
                                  empty_rrsig), isc::BadValue);

    // The RRset type and RRSIG's type covered don't match
    ConstRRsetPtr bad_rrsig(textToRRset(
                                "www.example.com. 1076895760 IN RRSIG "
                                "NS 5 2 3600 20120814220826 20120715220826 "
                                "1234 example.com. FAKE"));
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, bad_rrsig),
                 isc::BadValue);

    // Pass non RRSIG for the sig parameter
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, a_rrset_),
                 isc::BadValue);

    // Pass RRSIG for normal RRset (the RdataEncoder will catch this and throw)
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, rrsig_rrset_,
                                  rrsig_rrset_),
                 isc::BadValue);

    // RR class doesn't match between RRset and RRSIG
    ConstRRsetPtr badclass_rrsig(textToRRset(
                                     "www.example.com. 1076895760 CH RRSIG "
                                     "A 5 2 3600 20120814220826 "
                                     "20120715220826 1234 example.com. FAKE",
                                     RRClass::CH()));
    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
                                  badclass_rrsig),
                 isc::BadValue);
}
}
