// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <fstream>

#include <boost/scoped_ptr.hpp>

#include <exceptions/exceptions.h>

#include <util/buffer.h>

#include <util/unittests/testdata.h>
#include <util/unittests/textdata.h>

#include <dns/edns.h>
#include <dns/exceptions.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <dns/question.h>
#include <dns/opcode.h>
#include <dns/rcode.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
#include <dns/rrttl.h>
#include <dns/rrtype.h>
#include <dns/tsig.h>
#include <dns/tsigkey.h>

#include <gtest/gtest.h>

#include <dns/tests/unittest_util.h>
#include <util/unittests/wiredata.h>

using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
using isc::UnitTestUtil;
using isc::util::unittests::matchWireData;

//
// Note: we need more tests, including:
// parsing malformed headers
// more complete tests about parsing/rendering header flags, opcode, rcode, etc.
// tests for adding RRsets
// tests for RRset/Question iterators
// But, we'll ship with the current set of tests for now, partly because many
// of the above are covered as part of other tests, and partly due to time
// limitation.  We also expect to revisit the fundamental design of the Message
// class, at which point we'll also revise the tests including more cases.
//

const uint16_t Message::DEFAULT_MAX_UDPSIZE;

namespace isc {
namespace util {
namespace detail {
extern int64_t (*getTimeFunction)();
}
}
}

// XXX: this is defined as class static constants, but some compilers
// seemingly cannot find the symbol when used in the EXPECT_xxx macros.
const uint16_t TSIGContext::DEFAULT_FUDGE;

namespace {
class MessageTest : public ::testing::Test {
protected:
    MessageTest() : test_name("test.example.com"), obuffer(0),
                    message_parse(Message::PARSE),
                    message_render(Message::RENDER),
                    bogus_section(static_cast<Message::Section>(
                                      Message::SECTION_ADDITIONAL + 1)),
                    tsig_ctx(TSIGKey("www.example.com:"
                                     "SFuWd/q99SzF8Yzd1QbB9g==")) {
        rrset_a = RRsetPtr(new RRset(test_name, RRClass::IN(),
                                     RRType::A(), RRTTL(3600)));
        rrset_a->addRdata(in::A("192.0.2.1"));
        rrset_a->addRdata(in::A("192.0.2.2"));

        rrset_aaaa = RRsetPtr(new RRset(test_name, RRClass::IN(),
                                        RRType::AAAA(), RRTTL(3600)));
        rrset_aaaa->addRdata(in::AAAA("2001:db8::1234"));

        rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(),
                                        RRType::RRSIG(), RRTTL(3600)));
        rrset_rrsig->addRdata(generic::RRSIG("AAAA 5 3 7200 20100322084538 "
                                             "20100220084538 1 example.com. "
                                             "FAKEFAKEFAKEFAKE"));
        rrset_aaaa->addRRsig(rrset_rrsig);
    }

    static Question factoryFromFile(const char* datafile);
    const Name test_name;
    OutputBuffer obuffer;
    MessageRenderer renderer;
    Message message_parse;
    Message message_render;
    const Message::Section bogus_section;
    RRsetPtr rrset_a;           // A RRset with two RDATAs
    RRsetPtr rrset_aaaa;        // AAAA RRset with one RDATA with RRSIG
    RRsetPtr rrset_rrsig;       // RRSIG for the AAAA RRset
    TSIGContext tsig_ctx;
    vector<unsigned char> received_data;
    vector<unsigned char> expected_data;

    void factoryFromFile(Message& message, const char* datafile,
                         Message::ParseOptions options =
                         Message::PARSE_DEFAULT);
};

void
MessageTest::factoryFromFile(Message& message, const char* datafile,
                             Message::ParseOptions options)
{
    received_data.clear();
    UnitTestUtil::readWireData(datafile, received_data);

    InputBuffer buffer(&received_data[0], received_data.size());
    message.fromWire(buffer, options);
}

TEST_F(MessageTest, headerFlag) {
    // by default no flag is set
    EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_QR));
    EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AA));
    EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_TC));
    EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_RD));
    EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_RA));
    EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AD));
    EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_CD));

    // set operation: by default it will be on
    message_render.setHeaderFlag(Message::HEADERFLAG_QR);
    EXPECT_TRUE(message_render.getHeaderFlag(Message::HEADERFLAG_QR));

    // it can be set to on explicitly, too
    message_render.setHeaderFlag(Message::HEADERFLAG_AA, true);
    EXPECT_TRUE(message_render.getHeaderFlag(Message::HEADERFLAG_AA));

    // the bit can also be cleared
    message_render.setHeaderFlag(Message::HEADERFLAG_AA, false);
    EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AA));

    // Invalid flag values
    EXPECT_THROW(message_render.setHeaderFlag(
                     static_cast<Message::HeaderFlag>(0)), InvalidParameter);
    EXPECT_THROW(message_render.setHeaderFlag(
                     static_cast<Message::HeaderFlag>(0x7000)),
                 InvalidParameter);
    EXPECT_THROW(message_render.setHeaderFlag(
                     static_cast<Message::HeaderFlag>(0x0800)),
                 InvalidParameter);
    EXPECT_THROW(message_render.setHeaderFlag(
                     static_cast<Message::HeaderFlag>(0x0040)),
                 InvalidParameter);
    EXPECT_THROW(message_render.setHeaderFlag(
                     static_cast<Message::HeaderFlag>(0x10000)),
                 InvalidParameter);
    EXPECT_THROW(message_render.setHeaderFlag(
                     static_cast<Message::HeaderFlag>(0x80000000)),
                 InvalidParameter);

    // set operation isn't allowed in the parse mode.
    EXPECT_THROW(message_parse.setHeaderFlag(Message::HEADERFLAG_QR),
                 InvalidMessageOperation);
}
TEST_F(MessageTest, getEDNS) {
    EXPECT_FALSE(message_parse.getEDNS()); // by default EDNS isn't set

    factoryFromFile(message_parse, "message_fromWire10.wire");
    EXPECT_TRUE(message_parse.getEDNS());
    EXPECT_EQ(0, message_parse.getEDNS()->getVersion());
    EXPECT_EQ(4096, message_parse.getEDNS()->getUDPSize());
    EXPECT_TRUE(message_parse.getEDNS()->getDNSSECAwareness());
}

TEST_F(MessageTest, setEDNS) {
    // setEDNS() isn't allowed in the parse mode
    EXPECT_THROW(message_parse.setEDNS(EDNSPtr(new EDNS())),
                 InvalidMessageOperation);

    EDNSPtr edns = EDNSPtr(new EDNS());
    message_render.setEDNS(edns);
    EXPECT_EQ(edns, message_render.getEDNS());
}

TEST_F(MessageTest, fromWireWithTSIG) {
    // Initially there should be no TSIG
    EXPECT_FALSE(message_parse.getTSIGRecord());

    // getTSIGRecord() is only valid in the parse mode.
    EXPECT_THROW(message_render.getTSIGRecord(), InvalidMessageOperation);

    factoryFromFile(message_parse, "message_toWire2.wire");
    const uint8_t expected_mac[] = {
        0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7,
        0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3
    };
    const TSIGRecord* tsig_rr = message_parse.getTSIGRecord();
    ASSERT_TRUE(tsig_rr);
    EXPECT_EQ(Name("www.example.com"), tsig_rr->getName());
    EXPECT_EQ(85, tsig_rr->getLength()); // see TSIGRecordTest.getLength
    EXPECT_EQ(TSIGKey::HMACMD5_NAME(), tsig_rr->getRdata().getAlgorithm());
    EXPECT_EQ(0x4da8877a, tsig_rr->getRdata().getTimeSigned());
    EXPECT_EQ(TSIGContext::DEFAULT_FUDGE, tsig_rr->getRdata().getFudge());
    matchWireData(expected_mac, sizeof(expected_mac),
                  tsig_rr->getRdata().getMAC(),
                  tsig_rr->getRdata().getMACSize());
    EXPECT_EQ(0, tsig_rr->getRdata().getError());
    EXPECT_EQ(0, tsig_rr->getRdata().getOtherLen());
    EXPECT_FALSE(tsig_rr->getRdata().getOtherData());

    // If we clear the message for reuse, the recorded TSIG will be cleared.
    message_parse.clear(Message::PARSE);
    EXPECT_FALSE(message_parse.getTSIGRecord());
}

TEST_F(MessageTest, fromWireWithTSIGCompressed) {
    // Mostly same as fromWireWithTSIG, but the TSIG owner name is compressed.
    factoryFromFile(message_parse, "message_fromWire12.wire");
    const TSIGRecord* tsig_rr = message_parse.getTSIGRecord();
    ASSERT_TRUE(tsig_rr);
    EXPECT_EQ(Name("www.example.com"), tsig_rr->getName());
    // len(www.example.com) = 17, but when fully compressed, the length is
    // 2 bytes.  So the length of the record should be 15 bytes shorter.
    EXPECT_EQ(70, tsig_rr->getLength());
}

TEST_F(MessageTest, fromWireWithBadTSIG) {
    // Multiple TSIG RRs
    EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire13.wire"),
                 DNSMessageFORMERR);
    message_parse.clear(Message::PARSE);

    // TSIG in the answer section (must be in additional)
    EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire14.wire"),
                 DNSMessageFORMERR);
    message_parse.clear(Message::PARSE);

    // TSIG is not the last record.
    EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire15.wire"),
                 DNSMessageFORMERR);
    message_parse.clear(Message::PARSE);

    // Unexpected RR Class (this will fail in constructing TSIGRecord)
    EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire16.wire"),
                 DNSMessageFORMERR);
}

TEST_F(MessageTest, getRRCount) {
    // by default all counters should be 0
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_QUESTION));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL));

    message_render.addQuestion(Question(Name("test.example.com"),
                                        RRClass::IN(), RRType::A()));
    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));

    // rrset_a contains two RRs
    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
    EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));

    // parse a message containing a Question and EDNS OPT RR.
    // OPT shouldn't be counted as normal RR, so result of getRRCount
    // shouldn't change.
    factoryFromFile(message_parse, "message_fromWire11.wire");
    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL));

    // out-of-band section ID
    EXPECT_THROW(message_parse.getRRCount(bogus_section), isc::OutOfRange);
}

TEST_F(MessageTest, addRRset) {
    // initially, we have 0
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER));

    // add two A RRs (unsigned)
    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
    EXPECT_EQ(rrset_a,
              *message_render.beginSection(Message::SECTION_ANSWER));
    EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));

    message_render.clear(Message::RENDER);

    // add one AAAA RR (signed)
    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
    EXPECT_EQ(rrset_aaaa,
              *message_render.beginSection(Message::SECTION_ANSWER));
    EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));
}

TEST_F(MessageTest, badAddRRset) {
    // addRRset() isn't allowed in the parse mode.
    EXPECT_THROW(message_parse.addRRset(Message::SECTION_ANSWER,
                                        rrset_a), InvalidMessageOperation);
    // out-of-band section ID
    EXPECT_THROW(message_render.addRRset(bogus_section, rrset_a), isc::OutOfRange);

    // NULL RRset
    EXPECT_THROW(message_render.addRRset(Message::SECTION_ANSWER, RRsetPtr()),
                 InvalidParameter);
}

TEST_F(MessageTest, hasRRset) {
    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
    EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                        RRClass::IN(), RRType::A()));
    // section doesn't match
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
                                         RRClass::IN(), RRType::A()));
    // name doesn't match
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER,
                                         Name("nomatch.example"),
                                         RRClass::IN(), RRType::A()));
    // RR class doesn't match
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                         RRClass::CH(), RRType::A()));
    // RR type doesn't match
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                         RRClass::IN(), RRType::AAAA()));

    // out-of-band section ID
    EXPECT_THROW(message_render.hasRRset(bogus_section, test_name,
                                         RRClass::IN(), RRType::A()),
                 isc::OutOfRange);

    // Repeat the checks having created an RRset of the appropriate type.

    RRsetPtr rrs1(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(60)));
    EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, rrs1));
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, rrs1));

    RRsetPtr rrs2(new RRset(Name("nomatch.example"), RRClass::IN(), RRType::A(),
        RRTTL(5)));
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs2));

    RRsetPtr rrs3(new RRset(test_name, RRClass::CH(), RRType::A(), RRTTL(60)));
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs3));

    RRsetPtr rrs4(new RRset(test_name, RRClass::IN(), RRType::AAAA(), RRTTL(5)));
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs4));

    RRsetPtr rrs5(new RRset(test_name, RRClass::IN(), RRType::AAAA(), RRTTL(5)));
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs4));

    EXPECT_THROW(message_render.hasRRset(bogus_section, rrs1), isc::OutOfRange);
}

TEST_F(MessageTest, removeRRset) {
    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
    EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                        RRClass::IN(), RRType::A()));
    EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                        RRClass::IN(), RRType::AAAA()));
    EXPECT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER));

    // Locate the AAAA RRset and remove it and any associated RRSIGs
    RRsetIterator i = message_render.beginSection(Message::SECTION_ANSWER);
    if ((*i)->getType() == RRType::A()) {
        ++i;
    }
    EXPECT_EQ(RRType::AAAA(), (*i)->getType());
    message_render.removeRRset(Message::SECTION_ANSWER, i);

    EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                        RRClass::IN(), RRType::A()));
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                         RRClass::IN(), RRType::AAAA()));
    EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));
}

TEST_F(MessageTest, clearQuestionSection) {
    QuestionPtr q(new Question(Name("www.example.com"), RRClass::IN(),
                               RRType::A()));
    message_render.addQuestion(q);
    ASSERT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));

    message_render.clearSection(Message::SECTION_QUESTION);
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_QUESTION));
    EXPECT_TRUE(message_render.beginQuestion() ==
                message_render.endQuestion());
}


TEST_F(MessageTest, clearAnswerSection) {
    // Add two RRsets, check they are present, clear the section,
    // check if they are gone.
    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                        RRClass::IN(), RRType::A()));
    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                        RRClass::IN(), RRType::AAAA()));
    ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER));

    message_render.clearSection(Message::SECTION_ANSWER);
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                         RRClass::IN(), RRType::A()));
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
                                         RRClass::IN(), RRType::AAAA()));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER));
}

TEST_F(MessageTest, clearAuthoritySection) {
    // Add two RRsets, check they are present, clear the section,
    // check if they are gone.
    message_render.addRRset(Message::SECTION_AUTHORITY, rrset_a);
    message_render.addRRset(Message::SECTION_AUTHORITY, rrset_aaaa);
    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
                                        RRClass::IN(), RRType::A()));
    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
                                        RRClass::IN(), RRType::AAAA()));
    ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_AUTHORITY));

    message_render.clearSection(Message::SECTION_AUTHORITY);
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
                                         RRClass::IN(), RRType::A()));
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
                                         RRClass::IN(), RRType::AAAA()));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY));
}

TEST_F(MessageTest, clearAdditionalSection) {
    // Add two RRsets, check they are present, clear the section,
    // check if they are gone.
    message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_a);
    message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_aaaa);
    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
                                        RRClass::IN(), RRType::A()));
    ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
                                        RRClass::IN(), RRType::AAAA()));
    ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_ADDITIONAL));

    message_render.clearSection(Message::SECTION_ADDITIONAL);
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
                                         RRClass::IN(), RRType::A()));
    EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
                                         RRClass::IN(), RRType::AAAA()));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL));
}

TEST_F(MessageTest, badClearSection) {
    // attempt of clearing a message in the parse mode.
    EXPECT_THROW(message_parse.clearSection(Message::SECTION_QUESTION),
                 InvalidMessageOperation);
    // attempt of clearing out-of-range section
    EXPECT_THROW(message_render.clearSection(bogus_section), isc::OutOfRange);
}

TEST_F(MessageTest, badBeginSection) {
    // valid cases are tested via other tests
    EXPECT_THROW(message_render.beginSection(Message::SECTION_QUESTION),
                 InvalidMessageSection);
    EXPECT_THROW(message_render.beginSection(bogus_section), isc::OutOfRange);
}

TEST_F(MessageTest, badEndSection) {
    // valid cases are tested via other tests
    EXPECT_THROW(message_render.endSection(Message::SECTION_QUESTION),
                 InvalidMessageSection);
    EXPECT_THROW(message_render.endSection(bogus_section), isc::OutOfRange);
}

TEST_F(MessageTest, appendSection) {
    Message target(Message::RENDER);

    // Section check
    EXPECT_THROW(target.appendSection(bogus_section, message_render),
                 isc::OutOfRange);

    // Make sure nothing is copied if there is nothing to copy
    target.appendSection(Message::SECTION_QUESTION, message_render);
    EXPECT_EQ(0, target.getRRCount(Message::SECTION_QUESTION));
    target.appendSection(Message::SECTION_ANSWER, message_render);
    EXPECT_EQ(0, target.getRRCount(Message::SECTION_ANSWER));
    target.appendSection(Message::SECTION_AUTHORITY, message_render);
    EXPECT_EQ(0, target.getRRCount(Message::SECTION_AUTHORITY));
    target.appendSection(Message::SECTION_ADDITIONAL, message_render);
    EXPECT_EQ(0, target.getRRCount(Message::SECTION_ADDITIONAL));

    // Now add some data, copy again, and see if it got added
    message_render.addQuestion(Question(Name("test.example.com"),
                                        RRClass::IN(), RRType::A()));
    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
    message_render.addRRset(Message::SECTION_AUTHORITY, rrset_a);
    message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_a);
    message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_aaaa);

    target.appendSection(Message::SECTION_QUESTION, message_render);
    EXPECT_EQ(1, target.getRRCount(Message::SECTION_QUESTION));

    target.appendSection(Message::SECTION_ANSWER, message_render);
    EXPECT_EQ(2, target.getRRCount(Message::SECTION_ANSWER));
    EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
                                RRClass::IN(), RRType::A()));

    target.appendSection(Message::SECTION_AUTHORITY, message_render);
    EXPECT_EQ(2, target.getRRCount(Message::SECTION_AUTHORITY));
    EXPECT_TRUE(target.hasRRset(Message::SECTION_AUTHORITY, test_name,
                                RRClass::IN(), RRType::A()));

    target.appendSection(Message::SECTION_ADDITIONAL, message_render);
    EXPECT_EQ(4, target.getRRCount(Message::SECTION_ADDITIONAL));
    EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name,
                                RRClass::IN(), RRType::A()));
    EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name,
                                RRClass::IN(), RRType::AAAA()));

    // One more test, test to see if the section gets added, not replaced
    Message source2(Message::RENDER);
    source2.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
    target.appendSection(Message::SECTION_ANSWER, source2);
    EXPECT_EQ(4, target.getRRCount(Message::SECTION_ANSWER));
    EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
                                RRClass::IN(), RRType::A()));
    EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
                                RRClass::IN(), RRType::AAAA()));
}

TEST_F(MessageTest, parseHeader) {
    received_data.clear();
    UnitTestUtil::readWireData("message_fromWire1", received_data);

    // parseHeader() isn't allowed in the render mode.
    InputBuffer buffer(&received_data[0], received_data.size());
    EXPECT_THROW(message_render.parseHeader(buffer), InvalidMessageOperation);

    message_parse.parseHeader(buffer);
    EXPECT_EQ(0x1035, message_parse.getQid());
    EXPECT_EQ(Opcode::QUERY(), message_parse.getOpcode());
    EXPECT_EQ(Rcode::NOERROR(), message_parse.getRcode());
    EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_QR));
    EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_AA));
    EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_TC));
    EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_RD));
    EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_RA));
    EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_AD));
    EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_CD));
    EXPECT_EQ(1, message_parse.getRRCount(Message::SECTION_QUESTION));
    EXPECT_EQ(2, message_parse.getRRCount(Message::SECTION_ANSWER));
    EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_AUTHORITY));
    EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_ADDITIONAL));

    // Only the header part should have been examined.
    EXPECT_EQ(12, buffer.getPosition()); // 12 = size of the header section
    EXPECT_TRUE(message_parse.beginQuestion() == message_parse.endQuestion());
    EXPECT_TRUE(message_parse.beginSection(Message::SECTION_ANSWER) ==
                message_parse.endSection(Message::SECTION_ANSWER));
    EXPECT_TRUE(message_parse.beginSection(Message::SECTION_AUTHORITY) ==
                message_parse.endSection(Message::SECTION_AUTHORITY));
    EXPECT_TRUE(message_parse.beginSection(Message::SECTION_ADDITIONAL) ==
                message_parse.endSection(Message::SECTION_ADDITIONAL));
}

void
checkMessageFromWire(const Message& message_parse,
                     const Name& test_name)
{
    EXPECT_EQ(0x1035, message_parse.getQid());
    EXPECT_EQ(Opcode::QUERY(), message_parse.getOpcode());
    EXPECT_EQ(Rcode::NOERROR(), message_parse.getRcode());
    EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_QR));
    EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_RD));
    EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_AA));

    QuestionPtr q = *message_parse.beginQuestion();
    EXPECT_EQ(test_name, q->getName());
    EXPECT_EQ(RRType::A(), q->getType());
    EXPECT_EQ(RRClass::IN(), q->getClass());
    EXPECT_EQ(1, message_parse.getRRCount(Message::SECTION_QUESTION));
    EXPECT_EQ(2, message_parse.getRRCount(Message::SECTION_ANSWER));
    EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_AUTHORITY));
    EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_ADDITIONAL));

    RRsetPtr rrset = *message_parse.beginSection(Message::SECTION_ANSWER);
    EXPECT_EQ(test_name, rrset->getName());
    EXPECT_EQ(RRType::A(), rrset->getType());
    EXPECT_EQ(RRClass::IN(), rrset->getClass());
    // TTL should be 3600, even though that of the 2nd RR is 7200
    EXPECT_EQ(RRTTL(3600), rrset->getTTL());
    RdataIteratorPtr it = rrset->getRdataIterator();
    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
    it->next();
    EXPECT_EQ("192.0.2.2", it->getCurrent().toText());
    it->next();
    EXPECT_TRUE(it->isLast());
}


TEST_F(MessageTest, fromWire) {
    // fromWire() isn't allowed in the render mode.
    EXPECT_THROW(factoryFromFile(message_render, "message_fromWire1"),
                 InvalidMessageOperation);

    factoryFromFile(message_parse, "message_fromWire1");
    checkMessageFromWire(message_parse, test_name);
}

TEST_F(MessageTest, fromWireMultiple) {
    // Parse from wire multiple times.
    factoryFromFile(message_parse, "message_fromWire1");
    factoryFromFile(message_parse, "message_fromWire1");
    factoryFromFile(message_parse, "message_fromWire1");
    factoryFromFile(message_parse, "message_fromWire1");
    checkMessageFromWire(message_parse, test_name);

    // Calling parseHeader() directly before fromWire() should not cause
    // any problems.
    received_data.clear();
    UnitTestUtil::readWireData("message_fromWire1", received_data);

    InputBuffer buffer(&received_data[0], received_data.size());
    message_parse.parseHeader(buffer);
    message_parse.fromWire(buffer);
    message_parse.parseHeader(buffer);
    message_parse.fromWire(buffer);
    checkMessageFromWire(message_parse, test_name);
}

TEST_F(MessageTest, fromWireShortBuffer) {
    // We trim a valid message (ending with an SOA RR) for one byte.
    // fromWire() should throw an exception while parsing the trimmed RR.
    UnitTestUtil::readWireData("message_fromWire22.wire", received_data);
    InputBuffer buffer(&received_data[0], received_data.size() - 1);
    EXPECT_THROW(message_parse.fromWire(buffer), isc::OutOfRange);
}

TEST_F(MessageTest, fromWireCombineRRs) {
    // This message contains 3 RRs in the answer section in the order of
    // A, AAAA, A types.  fromWire() should combine the two A RRs into a
    // single RRset by default.
    factoryFromFile(message_parse, "message_fromWire19.wire");

    RRsetIterator it = message_parse.beginSection(Message::SECTION_ANSWER);
    RRsetIterator it_end = message_parse.endSection(Message::SECTION_ANSWER);
    ASSERT_TRUE(it != it_end);
    EXPECT_EQ(RRType::A(), (*it)->getType());
    EXPECT_EQ(2, (*it)->getRdataCount());

    ++it;
    ASSERT_TRUE(it != it_end);
    EXPECT_EQ(RRType::AAAA(), (*it)->getType());
    EXPECT_EQ(1, (*it)->getRdataCount());
}

// A helper function for a test pattern commonly used in several tests below.
void
preserveRRCheck(const Message& message, Message::Section section) {
    RRsetIterator it = message.beginSection(section);
    RRsetIterator it_end = message.endSection(section);
    ASSERT_TRUE(it != it_end);
    EXPECT_EQ(RRType::A(), (*it)->getType());
    EXPECT_EQ(1, (*it)->getRdataCount());
    EXPECT_EQ("192.0.2.1", (*it)->getRdataIterator()->getCurrent().toText());

    ++it;
    ASSERT_TRUE(it != it_end);
    EXPECT_EQ(RRType::AAAA(), (*it)->getType());
    EXPECT_EQ(1, (*it)->getRdataCount());
    EXPECT_EQ("2001:db8::1", (*it)->getRdataIterator()->getCurrent().toText());

    ++it;
    ASSERT_TRUE(it != it_end);
    EXPECT_EQ(RRType::A(), (*it)->getType());
    EXPECT_EQ(1, (*it)->getRdataCount());
    EXPECT_EQ("192.0.2.2", (*it)->getRdataIterator()->getCurrent().toText());
}

TEST_F(MessageTest, fromWirePreserveAnswer) {
    // Using the same data as the previous test, but specify the PRESERVE_ORDER
    // option.  The received order of RRs should be preserved, and each RR
    // should be stored in a single RRset.
    factoryFromFile(message_parse, "message_fromWire19.wire",
                    Message::PRESERVE_ORDER);
    {
        SCOPED_TRACE("preserve answer RRs");
        preserveRRCheck(message_parse, Message::SECTION_ANSWER);
    }
}

TEST_F(MessageTest, fromWirePreserveAuthority) {
    // Same for the previous test, but for the authority section.
    factoryFromFile(message_parse, "message_fromWire20.wire",
                    Message::PRESERVE_ORDER);
    {
        SCOPED_TRACE("preserve authority RRs");
        preserveRRCheck(message_parse, Message::SECTION_AUTHORITY);
    }
}

TEST_F(MessageTest, fromWirePreserveAdditional) {
    // Same for the previous test, but for the additional section.
    factoryFromFile(message_parse, "message_fromWire21.wire",
                    Message::PRESERVE_ORDER);
    {
        SCOPED_TRACE("preserve additional RRs");
        preserveRRCheck(message_parse, Message::SECTION_ADDITIONAL);
    }
}

TEST_F(MessageTest, EDNS0ExtRcode) {
    // Extended Rcode = BADVERS
    factoryFromFile(message_parse, "message_fromWire10.wire");
    EXPECT_EQ(Rcode::BADVERS(), message_parse.getRcode());

    // Maximum extended Rcode
    message_parse.clear(Message::PARSE);
    factoryFromFile(message_parse, "message_fromWire11.wire");
    EXPECT_EQ(0xfff, message_parse.getRcode().getCode());
}

TEST_F(MessageTest, BadEDNS0) {
    // OPT RR in the answer section
    EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire4"),
                 DNSMessageFORMERR);
    // multiple OPT RRs (in the additional section)
    message_parse.clear(Message::PARSE);
    EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire5"),
                 DNSMessageFORMERR);
}

TEST_F(MessageTest, toWire) {
    message_render.setQid(0x1035);
    message_render.setOpcode(Opcode::QUERY());
    message_render.setRcode(Rcode::NOERROR());
    message_render.setHeaderFlag(Message::HEADERFLAG_QR, true);
    message_render.setHeaderFlag(Message::HEADERFLAG_RD, true);
    message_render.setHeaderFlag(Message::HEADERFLAG_AA, true);
    message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(),
                                        RRType::A()));
    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);

    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));
    EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL));

    message_render.toWire(renderer);
    vector<unsigned char> data;
    UnitTestUtil::readWireData("message_toWire1", data);
    matchWireData(&data[0], data.size(),
                  renderer.getData(), renderer.getLength());
}

TEST_F(MessageTest, toWireSigned) {
    message_render.setQid(0x75c1);
    message_render.setOpcode(Opcode::QUERY());
    message_render.setRcode(Rcode::NOERROR());
    message_render.setHeaderFlag(Message::HEADERFLAG_QR, true);
    message_render.setHeaderFlag(Message::HEADERFLAG_RD, true);
    message_render.setHeaderFlag(Message::HEADERFLAG_AA, true);
    message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(),
                                        RRType::A()));

    rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(),
                                     RRType::RRSIG(), RRTTL(3600)));
    // one signature algorithm (5 = RSA/SHA-1)
    rrset_rrsig->addRdata(generic::RRSIG("A 5 3 3600 "
                                         "20000101000000 20000201000000 "
                                         "12345 example.com. FAKEFAKEFAKE"));
    // another signature algorithm (3 = DSA/SHA-1)
    rrset_rrsig->addRdata(generic::RRSIG("A 3 3 3600 "
                                         "20000101000000 20000201000000 "
                                         "12345 example.com. FAKEFAKEFAKE"));
    rrset_a->addRRsig(rrset_rrsig);
    EXPECT_EQ(2, rrset_a->getRRsigDataCount());

    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);

    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));
    EXPECT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL));

    message_render.toWire(renderer);
    vector<unsigned char> data;
    UnitTestUtil::readWireData("message_toWire6", data);
    matchWireData(&data[0], data.size(),
                  renderer.getData(), renderer.getLength());
}

TEST_F(MessageTest, toWireSignedAndTruncated) {
    message_render.setQid(0x75c1);
    message_render.setOpcode(Opcode::QUERY());
    message_render.setRcode(Rcode::NOERROR());
    message_render.setHeaderFlag(Message::HEADERFLAG_QR, true);
    message_render.setHeaderFlag(Message::HEADERFLAG_RD, true);
    message_render.setHeaderFlag(Message::HEADERFLAG_AA, true);
    message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(),
                                        RRType::TXT()));

    RRsetPtr rrset_txt = RRsetPtr(new RRset(test_name, RRClass::IN(),
                                            RRType::TXT(), RRTTL(3600)));
    rrset_txt->addRdata(generic::TXT(string(255, 'a')));
    rrset_txt->addRdata(generic::TXT(string(255, 'b')));
    rrset_txt->addRdata(generic::TXT(string(255, 'c')));
    rrset_txt->addRdata(generic::TXT(string(255, 'd')));
    rrset_txt->addRdata(generic::TXT(string(255, 'e')));
    rrset_txt->addRdata(generic::TXT(string(255, 'f')));
    rrset_txt->addRdata(generic::TXT(string(255, 'g')));
    rrset_txt->addRdata(generic::TXT(string(255, 'h')));

    rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(),
                                     RRType::RRSIG(), RRTTL(3600)));
    // one signature algorithm (5 = RSA/SHA-1)
    rrset_rrsig->addRdata(generic::RRSIG("TXT 5 3 3600 "
                                         "20000101000000 20000201000000 "
                                         "12345 example.com. FAKEFAKEFAKE"));
    rrset_txt->addRRsig(rrset_rrsig);
    EXPECT_EQ(1, rrset_txt->getRRsigDataCount());

    message_render.addRRset(Message::SECTION_ANSWER, rrset_txt);

    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));
    EXPECT_EQ(9, message_render.getRRCount(Message::SECTION_ANSWER));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY));
    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL));

    message_render.toWire(renderer);
    vector<unsigned char> data;
    UnitTestUtil::readWireData("message_toWire7", data);
    matchWireData(&data[0], data.size(),
                  renderer.getData(), renderer.getLength());
}

TEST_F(MessageTest, toWireInParseMode) {
    // toWire() isn't allowed in the parse mode.
    EXPECT_THROW(message_parse.toWire(renderer), InvalidMessageOperation);
}

// See dnssectime_unittest.cc
template <int64_t NOW>
int64_t
testGetTime() {
    return (NOW);
}

// bit-wise constant flags to configure DNS header flags for test
// messages.
const unsigned int QR_FLAG = 0x1;
const unsigned int AA_FLAG = 0x2;
const unsigned int RD_FLAG = 0x4;

void
commonTSIGToWireCheck(Message& message, MessageRenderer& renderer,
                      TSIGContext& tsig_ctx, const char* const expected_file,
                      unsigned int message_flags = RD_FLAG,
                      RRType qtype = RRType::A(),
                      const vector<const char*>* answer_data = 0) {
    message.setOpcode(Opcode::QUERY());
    message.setRcode(Rcode::NOERROR());
    if ((message_flags & QR_FLAG) != 0) {
        message.setHeaderFlag(Message::HEADERFLAG_QR);
    }
    if ((message_flags & AA_FLAG) != 0) {
        message.setHeaderFlag(Message::HEADERFLAG_AA);
    }
    if ((message_flags & RD_FLAG) != 0) {
        message.setHeaderFlag(Message::HEADERFLAG_RD);
    }
    message.addQuestion(Question(Name("www.example.com"), RRClass::IN(),
                                 qtype));

    if (answer_data) {
        RRsetPtr ans_rrset(new RRset(Name("www.example.com"), RRClass::IN(),
                                     qtype, RRTTL(86400)));
        for (auto const& it : *answer_data) {
            ans_rrset->addRdata(createRdata(qtype, RRClass::IN(), it));
        }
        message.addRRset(Message::SECTION_ANSWER, ans_rrset);
    }

    message.toWire(renderer, &tsig_ctx);
    vector<unsigned char> expected_data;
    UnitTestUtil::readWireData(expected_file, expected_data);
    matchWireData(&expected_data[0], expected_data.size(),
                  renderer.getData(), renderer.getLength());
}

TEST_F(MessageTest, toWireWithTSIG) {
    // Rendering a message with TSIG.  Various special cases specific to
    // TSIG are tested in the tsig tests.  We only check the message contains
    // a TSIG at the end and the ARCOUNT of the header is updated.

    isc::util::detail::getTimeFunction = testGetTime<0x4da8877a>;

    message_render.setQid(0x2d65);

    {
        SCOPED_TRACE("Message sign with TSIG");
        commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
                              "message_toWire2.wire");
    }
}

TEST_F(MessageTest, toWireWithEDNSAndTSIG) {
    // Similar to the previous test, but with an EDNS before TSIG.
    // The wire data check will confirm the ordering.
    isc::util::detail::getTimeFunction = testGetTime<0x4db60d1f>;

    message_render.setQid(0x6cd);

    EDNSPtr edns(new EDNS());
    edns->setUDPSize(4096);
    message_render.setEDNS(edns);

    {
        SCOPED_TRACE("Message sign with TSIG and EDNS");
        commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
                              "message_toWire3.wire");
    }
}

// Some of the following tests involve truncation.  We use the query name
// "www.example.com" and some TXT question/answers.  The length of the
// header and question will be 33 bytes.  If we also try to include a
// TSIG of the same key name (not compressed) with HMAC-MD5, the TSIG RR
// will be 85 bytes.

// A long TXT RDATA.  With a fully compressed owner name, the corresponding
// RR will be 268 bytes.
const char* const long_txt1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde";

// With a fully compressed owner name, the corresponding RR will be 212 bytes.
// It should result in truncation even without TSIG (33 + 268 + 212 = 513)
const char* const long_txt2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456";

// With a fully compressed owner name, the corresponding RR will be 127 bytes.
// So, it can fit in the standard 512 bytes with txt1 and without TSIG, but
// adding a TSIG would result in truncation (33 + 268 + 127 + 85 = 513)
const char* const long_txt3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01";

// This is 1 byte shorter than txt3, which will result in a possible longest
// message containing answer RRs and TSIG.
const char* const long_txt4 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0";

// Example output generated by
// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com txt
// QID: 0x22c2
// Time Signed: 0x00004e179212
TEST_F(MessageTest, toWireTSIGTruncation) {
    isc::util::detail::getTimeFunction = testGetTime<0x4e179212>;

    // Verify a validly signed query so that we can use the TSIG context

    factoryFromFile(message_parse, "message_fromWire17.wire");
    EXPECT_EQ(TSIGError::NOERROR(),
              tsig_ctx.verify(message_parse.getTSIGRecord(),
                              &received_data[0], received_data.size()));

    message_render.setQid(0x22c2);
    vector<const char*> answer_data;
    answer_data.push_back(long_txt1);
    answer_data.push_back(long_txt2);
    {
        SCOPED_TRACE("Message sign with TSIG and TC bit on");
        commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
                              "message_toWire4.wire",
                              QR_FLAG|AA_FLAG|RD_FLAG,
                              RRType::TXT(), &answer_data);
    }
}

TEST_F(MessageTest, toWireTSIGTruncation2) {
    // Similar to the previous test, but without TSIG it wouldn't cause
    // truncation.
    isc::util::detail::getTimeFunction = testGetTime<0x4e179212>;
    factoryFromFile(message_parse, "message_fromWire17.wire");
    EXPECT_EQ(TSIGError::NOERROR(),
              tsig_ctx.verify(message_parse.getTSIGRecord(),
                              &received_data[0], received_data.size()));

    message_render.setQid(0x22c2);
    vector<const char*> answer_data;
    answer_data.push_back(long_txt1);
    answer_data.push_back(long_txt3);
    {
        SCOPED_TRACE("Message sign with TSIG and TC bit on (2)");
        commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
                              "message_toWire4.wire",
                              QR_FLAG|AA_FLAG|RD_FLAG,
                              RRType::TXT(), &answer_data);
    }
}

TEST_F(MessageTest, toWireTSIGTruncation3) {
    // Similar to previous ones, but truncation occurs due to too many
    // Questions (very unusual, but not necessarily illegal).

    // We are going to create a message starting with a standard
    // header (12 bytes) and multiple questions in the Question
    // section of the same owner name (changing the RRType, just so
    // that it would be the form that would be accepted by the BIND 9
    // parser).  The first Question is 21 bytes in length, and the subsequent
    // ones are 6 bytes.  We'll also use a TSIG whose size is 85 bytes.
    // Up to 66 questions can fit in the standard 512-byte buffer
    // (12 + 21 + 6 * 65 + 85 = 508).  If we try to add one more it would
    // result in truncation.
    message_render.setOpcode(Opcode::QUERY());
    message_render.setRcode(Rcode::NOERROR());
    for (int i = 1; i <= 67; ++i) {
        message_render.addQuestion(Question(Name("www.example.com"),
                                            RRClass::IN(), RRType(i)));
    }
    message_render.toWire(renderer, &tsig_ctx);

    // Check the rendered data by parsing it.  We only check it has the
    // TC bit on, has the correct number of questions, and has a TSIG RR.
    // Checking the signature wouldn't be necessary for this rare case
    // scenario.
    InputBuffer buffer(renderer.getData(), renderer.getLength());
    message_parse.fromWire(buffer);
    EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_TC));
    // Note that the number of questions are 66, not 67 as we tried to add.
    EXPECT_EQ(66, message_parse.getRRCount(Message::SECTION_QUESTION));
    EXPECT_TRUE(message_parse.getTSIGRecord());
}

TEST_F(MessageTest, toWireTSIGNoTruncation) {
    // A boundary case that shouldn't cause truncation: the resulting
    // response message with a TSIG will be 512 bytes long.
    isc::util::detail::getTimeFunction = testGetTime<0x4e17b38d>;
    factoryFromFile(message_parse, "message_fromWire18.wire");
    EXPECT_EQ(TSIGError::NOERROR(),
              tsig_ctx.verify(message_parse.getTSIGRecord(),
                              &received_data[0], received_data.size()));

    message_render.setQid(0xd6e2);
    vector<const char*> answer_data;
    answer_data.push_back(long_txt1);
    answer_data.push_back(long_txt4);
    {
        SCOPED_TRACE("Message sign with TSIG, no truncation");
        commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
                              "message_toWire5.wire",
                              QR_FLAG|AA_FLAG|RD_FLAG,
                              RRType::TXT(), &answer_data);
    }
}

// This is a buggy renderer for testing.  It behaves like the straightforward
// MessageRenderer, but once it has some data, its setLengthLimit() ignores
// the given parameter and resets the limit to the current length, making
// subsequent insertion result in truncation, which would make TSIG RR
// rendering fail unexpectedly in the test that follows.
class BadRenderer : public MessageRenderer {
public:
    virtual void setLengthLimit(size_t len) {
        if (getLength() > 0) {
            MessageRenderer::setLengthLimit(getLength());
        } else {
            MessageRenderer::setLengthLimit(len);
        }
    }
};

TEST_F(MessageTest, toWireTSIGLengthErrors) {
    // specify an unusual short limit that wouldn't be able to hold
    // the TSIG.
    renderer.setLengthLimit(tsig_ctx.getTSIGLength() - 1);
    // Use commonTSIGToWireCheck() only to call toWire() with otherwise valid
    // conditions.  The checks inside it don't matter because we expect an
    // exception before any of the checks.
    EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
                                       "message_toWire2.wire"),
                 InvalidParameter);

    // This one is large enough for TSIG, but the remaining limit isn't
    // even enough for the Header section.
    renderer.clear();
    message_render.clear(Message::RENDER);
    renderer.setLengthLimit(tsig_ctx.getTSIGLength() + 1);
    EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
                                       "message_toWire2.wire"),
                 InvalidParameter);

    // Trying to render a message with TSIG using a buggy renderer.
    BadRenderer bad_renderer;
    bad_renderer.setLengthLimit(512);
    message_render.clear(Message::RENDER);
    EXPECT_THROW(commonTSIGToWireCheck(message_render, bad_renderer, tsig_ctx,
                                       "message_toWire2.wire"),
                 Unexpected);
}

TEST_F(MessageTest, toWireWithoutOpcode) {
    message_render.setRcode(Rcode::NOERROR());
    EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
}

TEST_F(MessageTest, toWireWithoutRcode) {
    message_render.setOpcode(Opcode::QUERY());
    EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
}

TEST_F(MessageTest, toText) {
    // Check toText() output for a typical DNS response with records in
    // all sections

    factoryFromFile(message_parse, "message_toText1.wire");
    {
        SCOPED_TRACE("Message toText test (basic case)");
        ifstream ifs;
        unittests::openTestData("message_toText1.txt", ifs);
        unittests::matchTextData(ifs, message_parse.toText());
    }

    // Another example with EDNS.  The expected data was slightly modified
    // from the dig output (other than replacing tabs with a space): adding
    // a newline after the "OPT PSEUDOSECTION".  This is an intentional change
    // in our version for better readability.
    message_parse.clear(Message::PARSE);
    factoryFromFile(message_parse, "message_toText2.wire");
    {
        SCOPED_TRACE("Message toText test with EDNS");
        ifstream ifs;
        unittests::openTestData("message_toText2.txt", ifs);
        unittests::matchTextData(ifs, message_parse.toText());
    }

    // Another example with TSIG.  The expected data was slightly modified
    // from the dig output (other than replacing tabs with a space): removing
    // a redundant white space at the end of TSIG RDATA.  We'd rather consider
    // it a dig's defect than a feature.
    message_parse.clear(Message::PARSE);
    factoryFromFile(message_parse, "message_toText3.wire");
    {
        SCOPED_TRACE("Message toText test with TSIG");
        ifstream ifs;
        unittests::openTestData("message_toText3.txt", ifs);
        unittests::matchTextData(ifs, message_parse.toText());
    }
}

TEST_F(MessageTest, toTextWithoutOpcode) {
    message_render.setRcode(Rcode::NOERROR());
    EXPECT_THROW(message_render.toText(), InvalidMessageOperation);
}

TEST_F(MessageTest, toTextWithoutRcode) {
    message_render.setOpcode(Opcode::QUERY());
    EXPECT_THROW(message_render.toText(), InvalidMessageOperation);
}
}
