Commit c874cb05 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac1258' with fixing conflict in Changelog.

parents c430e464 12fd115d
......@@ -124,10 +124,12 @@ public:
void setOpcode(const Opcode& opcode);
void setRcode(const Rcode& rcode);
int parseQuestion(InputBuffer& buffer);
int parseSection(const Message::Section section, InputBuffer& buffer);
int parseSection(const Message::Section section, InputBuffer& buffer,
Message::ParseOptions options);
void addRR(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, ConstRdataPtr rdata);
const RRTTL& ttl, ConstRdataPtr rdata,
Message::ParseOptions options);
void addEDNS(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, const Rdata& rdata);
......@@ -614,7 +616,7 @@ Message::parseHeader(InputBuffer& buffer) {
}
void
Message::fromWire(InputBuffer& buffer) {
Message::fromWire(InputBuffer& buffer, ParseOptions options) {
if (impl_->mode_ != Message::PARSE) {
isc_throw(InvalidMessageOperation,
"Message parse attempted in non parse mode");
......@@ -626,11 +628,11 @@ Message::fromWire(InputBuffer& buffer) {
impl_->counts_[SECTION_QUESTION] = impl_->parseQuestion(buffer);
impl_->counts_[SECTION_ANSWER] =
impl_->parseSection(SECTION_ANSWER, buffer);
impl_->parseSection(SECTION_ANSWER, buffer, options);
impl_->counts_[SECTION_AUTHORITY] =
impl_->parseSection(SECTION_AUTHORITY, buffer);
impl_->parseSection(SECTION_AUTHORITY, buffer, options);
impl_->counts_[SECTION_ADDITIONAL] =
impl_->parseSection(SECTION_ADDITIONAL, buffer);
impl_->parseSection(SECTION_ADDITIONAL, buffer, options);
}
int
......@@ -706,7 +708,7 @@ struct MatchRR : public unary_function<RRsetPtr, bool> {
// is hardcoded here.
int
MessageImpl::parseSection(const Message::Section section,
InputBuffer& buffer)
InputBuffer& buffer, Message::ParseOptions options)
{
assert(section < MessageImpl::NUM_SECTIONS);
......@@ -738,7 +740,7 @@ MessageImpl::parseSection(const Message::Section section,
addTSIG(section, count, buffer, start_position, name, rrclass, ttl,
*rdata);
} else {
addRR(section, name, rrclass, rrtype, ttl, rdata);
addRR(section, name, rrclass, rrtype, ttl, rdata, options);
++added;
}
}
......@@ -749,19 +751,22 @@ MessageImpl::parseSection(const Message::Section section,
void
MessageImpl::addRR(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, ConstRdataPtr rdata)
const RRTTL& ttl, ConstRdataPtr rdata,
Message::ParseOptions options)
{
vector<RRsetPtr>::iterator it =
find_if(rrsets_[section].begin(), rrsets_[section].end(),
MatchRR(name, rrtype, rrclass));
if (it != rrsets_[section].end()) {
(*it)->setTTL(min((*it)->getTTL(), ttl));
(*it)->addRdata(rdata);
} else {
RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
rrset->addRdata(rdata);
rrsets_[section].push_back(rrset);
if ((options & Message::PRESERVE_ORDER) == 0) {
vector<RRsetPtr>::iterator it =
find_if(rrsets_[section].begin(), rrsets_[section].end(),
MatchRR(name, rrtype, rrclass));
if (it != rrsets_[section].end()) {
(*it)->setTTL(min((*it)->getTTL(), ttl));
(*it)->addRdata(rdata);
return;
}
}
RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
rrset->addRdata(rdata);
rrsets_[section].push_back(rrset);
}
void
......
......@@ -581,11 +581,58 @@ public:
/// message
void toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx);
/// Parse options.
///
/// describe PRESERVE_ORDER: note doesn't affect EDNS or TSIG.
///
/// The option values are used as a parameter for \c fromWire().
/// These are values of a bitmask type. Bitwise operations can be
/// performed on these values to express compound options.
enum ParseOptions {
PARSE_DEFAULT = 0, ///< The default options
PRESERVE_ORDER = 1 ///< Preserve RR order and don't combine them
};
/// \brief Parse the header section of the \c Message.
void parseHeader(isc::util::InputBuffer& buffer);
/// \brief Parse the \c Message.
void fromWire(isc::util::InputBuffer& buffer);
/// \brief (Re)build a \c Message object from wire-format data.
///
/// This method parses the given wire format data to build a
/// complete Message object. On success, the values of the header section
/// fields can be accessible via corresponding get methods, and the
/// question and following sections can be accessible via the
/// corresponding iterators. If the message contains an EDNS or TSIG,
/// they can be accessible via \c getEDNS() and \c getTSIGRecord(),
/// respectively.
///
/// This \c Message must be in the \c PARSE mode.
///
/// This method performs strict validation on the given message based
/// on the DNS protocol specifications. If the given message data is
/// invalid, this method throws an exception (see the exception list).
///
/// By default, this method combines RRs of the same name, RR type and
/// RR class in a section into a single RRset, even if they are interleaved
/// with a different type of RR (though it would be a rare case in
/// practice). If the \c PRESERVE_ORDER option is specified, it handles
/// each RR separately, in the appearing order, and converts it to a
/// separate RRset (so this RRset should contain exactly one Rdata).
/// This mode will be necessary when the higher level protocol is
/// ordering conscious. For example, in AXFR and IXFR, the position of
/// the SOA RRs are crucial.
///
/// \exception InvalidMessageOperation \c Message is in the RENDER mode
/// \exception DNSMessageFORMERR The given message data is syntactically
/// \exception MessageTooShort The given data is shorter than a valid
/// header section
/// \exception std::bad_alloc Memory allocation failure
/// \exception Others \c Name, \c Rdata, and \c EDNS classes can also throw
///
/// \param buffer A input buffer object that stores the wire data
/// \param options Parse options
void fromWire(isc::util::InputBuffer& buffer, ParseOptions options
= PARSE_DEFAULT);
///
/// \name Protocol constants
......@@ -629,6 +676,6 @@ std::ostream& operator<<(std::ostream& os, const Message& message);
}
#endif // __MESSAGE_H
// Local Variables:
// Local Variables:
// mode: c++
// End:
// End:
......@@ -39,6 +39,7 @@ pydnspp_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
EXTRA_DIST = tsigerror_python_inc.cc
EXTRA_DIST += message_python_inc.cc
# Python prefers .so, while some OSes (specifically MacOS) use a different
# suffix for dynamic objects. -module is necessary to work this around.
......
......@@ -39,6 +39,9 @@ using namespace isc::dns;
using namespace isc::dns::python;
using namespace isc::util;
// Import pydoc text
#include "message_python_inc.cc"
namespace {
class s_Message : public PyObject {
public:
......@@ -75,7 +78,7 @@ PyObject* Message_makeResponse(s_Message* self);
PyObject* Message_toText(s_Message* self);
PyObject* Message_str(PyObject* self);
PyObject* Message_toWire(s_Message* self, PyObject* args);
PyObject* Message_fromWire(s_Message* self, PyObject* args);
PyObject* Message_fromWire(PyObject* const pyself, PyObject* args);
// This list contains the actual set of functions we have in
// python. Each entry has
......@@ -157,14 +160,7 @@ PyMethodDef Message_methods[] = {
"If the given message is not in RENDER mode, an "
"InvalidMessageOperation is raised.\n"
},
{ "from_wire", reinterpret_cast<PyCFunction>(Message_fromWire), METH_VARARGS,
"Parses the given wire format to a Message object.\n"
"The first argument is a Message to parse the data into.\n"
"The second argument must implement the buffer interface.\n"
"If the given message is not in PARSE mode, an "
"InvalidMessageOperation is raised.\n"
"Raises MessageTooShort, DNSMessageFORMERR or DNSMessageBADVERS "
" if there is a problem parsing the message." },
{ "from_wire", Message_fromWire, METH_VARARGS, Message_fromWire_doc },
{ NULL, NULL, 0, NULL }
};
......@@ -646,30 +642,54 @@ Message_toWire(s_Message* self, PyObject* args) {
}
PyObject*
Message_fromWire(s_Message* self, PyObject* args) {
Message_fromWire(PyObject* const pyself, PyObject* args) {
s_Message* self = static_cast<s_Message*>(pyself);
const char* b;
Py_ssize_t len;
if (!PyArg_ParseTuple(args, "y#", &b, &len)) {
return (NULL);
}
unsigned int options = Message::PARSE_DEFAULT;
if (PyArg_ParseTuple(args, "y#", &b, &len) ||
PyArg_ParseTuple(args, "y#I", &b, &len, &options)) {
// We need to clear the error in case the first call to ParseTuple
// fails.
PyErr_Clear();
InputBuffer inbuf(b, len);
try {
self->cppobj->fromWire(inbuf);
Py_RETURN_NONE;
} catch (const InvalidMessageOperation& imo) {
PyErr_SetString(po_InvalidMessageOperation, imo.what());
return (NULL);
} catch (const DNSMessageFORMERR& dmfe) {
PyErr_SetString(po_DNSMessageFORMERR, dmfe.what());
return (NULL);
} catch (const DNSMessageBADVERS& dmfe) {
PyErr_SetString(po_DNSMessageBADVERS, dmfe.what());
return (NULL);
} catch (const MessageTooShort& mts) {
PyErr_SetString(po_MessageTooShort, mts.what());
return (NULL);
InputBuffer inbuf(b, len);
try {
self->cppobj->fromWire(
inbuf, static_cast<Message::ParseOptions>(options));
Py_RETURN_NONE;
} catch (const InvalidMessageOperation& imo) {
PyErr_SetString(po_InvalidMessageOperation, imo.what());
return (NULL);
} catch (const DNSMessageFORMERR& dmfe) {
PyErr_SetString(po_DNSMessageFORMERR, dmfe.what());
return (NULL);
} catch (const DNSMessageBADVERS& dmfe) {
PyErr_SetString(po_DNSMessageBADVERS, dmfe.what());
return (NULL);
} catch (const MessageTooShort& mts) {
PyErr_SetString(po_MessageTooShort, mts.what());
return (NULL);
} catch (const InvalidBufferPosition& ex) {
PyErr_SetString(po_DNSMessageFORMERR, ex.what());
return (NULL);
} catch (const exception& ex) {
const string ex_what =
"Error in Message.from_wire: " + string(ex.what());
PyErr_SetString(PyExc_RuntimeError, ex_what.c_str());
return (NULL);
} catch (...) {
PyErr_SetString(PyExc_RuntimeError,
"Unexpected exception in Message.from_wire");
return (NULL);
}
}
PyErr_SetString(PyExc_TypeError,
"from_wire() arguments must be a byte object and "
"(optional) parse options");
return (NULL);
}
} // end of unnamed namespace
......
namespace {
const char* const Message_fromWire_doc = "\
from_wire(data, options=PARSE_DEFAULT)\n\
\n\
(Re)build a Message object from wire-format data.\n\
\n\
This method parses the given wire format data to build a complete\n\
Message object. On success, the values of the header section fields\n\
can be accessible via corresponding get methods, and the question and\n\
following sections can be accessible via the corresponding iterators.\n\
If the message contains an EDNS or TSIG, they can be accessible via\n\
get_edns() and get_tsig_record(), respectively.\n\
\n\
This Message must be in the PARSE mode.\n\
\n\
This method performs strict validation on the given message based on\n\
the DNS protocol specifications. If the given message data is invalid,\n\
this method throws an exception (see the exception list).\n\
\n\
By default, this method combines RRs of the same name, RR type and RR\n\
class in a section into a single RRset, even if they are interleaved\n\
with a different type of RR (though it would be a rare case in\n\
practice). If the PRESERVE_ORDER option is specified, it handles each\n\
RR separately, in the appearing order, and converts it to a separate\n\
RRset (so this RRset should contain exactly one Rdata). This mode will\n\
be necessary when the higher level protocol is ordering conscious. For\n\
example, in AXFR and IXFR, the position of the SOA RRs are crucial.\n\
\n\
Exceptions:\n\
InvalidMessageOperation Message is in the RENDER mode\n\
DNSMessageFORMERR The given message data is syntactically\n\
MessageTooShort The given data is shorter than a valid header\n\
section\n\
Others Name, Rdata, and EDNS classes can also throw\n\
\n\
Parameters:\n\
data A byte object of the wire data\n\
options Parse options\n\
\n\
";
} // unnamed namespace
......@@ -89,64 +89,91 @@ initModulePart_Message(PyObject* mod) {
if (PyType_Ready(&message_type) < 0) {
return (false);
}
void* p = &message_type;
if (PyModule_AddObject(mod, "Message", static_cast<PyObject*>(p)) < 0) {
return (false);
}
Py_INCREF(&message_type);
// Class variables
// These are added to the tp_dict of the type object
//
addClassVariable(message_type, "PARSE",
Py_BuildValue("I", Message::PARSE));
addClassVariable(message_type, "RENDER",
Py_BuildValue("I", Message::RENDER));
addClassVariable(message_type, "HEADERFLAG_QR",
Py_BuildValue("I", Message::HEADERFLAG_QR));
addClassVariable(message_type, "HEADERFLAG_AA",
Py_BuildValue("I", Message::HEADERFLAG_AA));
addClassVariable(message_type, "HEADERFLAG_TC",
Py_BuildValue("I", Message::HEADERFLAG_TC));
addClassVariable(message_type, "HEADERFLAG_RD",
Py_BuildValue("I", Message::HEADERFLAG_RD));
addClassVariable(message_type, "HEADERFLAG_RA",
Py_BuildValue("I", Message::HEADERFLAG_RA));
addClassVariable(message_type, "HEADERFLAG_AD",
Py_BuildValue("I", Message::HEADERFLAG_AD));
addClassVariable(message_type, "HEADERFLAG_CD",
Py_BuildValue("I", Message::HEADERFLAG_CD));
addClassVariable(message_type, "SECTION_QUESTION",
Py_BuildValue("I", Message::SECTION_QUESTION));
addClassVariable(message_type, "SECTION_ANSWER",
Py_BuildValue("I", Message::SECTION_ANSWER));
addClassVariable(message_type, "SECTION_AUTHORITY",
Py_BuildValue("I", Message::SECTION_AUTHORITY));
addClassVariable(message_type, "SECTION_ADDITIONAL",
Py_BuildValue("I", Message::SECTION_ADDITIONAL));
addClassVariable(message_type, "DEFAULT_MAX_UDPSIZE",
Py_BuildValue("I", Message::DEFAULT_MAX_UDPSIZE));
/* Class-specific exceptions */
po_MessageTooShort = PyErr_NewException("pydnspp.MessageTooShort", NULL,
NULL);
PyModule_AddObject(mod, "MessageTooShort", po_MessageTooShort);
po_InvalidMessageSection =
PyErr_NewException("pydnspp.InvalidMessageSection", NULL, NULL);
PyModule_AddObject(mod, "InvalidMessageSection", po_InvalidMessageSection);
po_InvalidMessageOperation =
PyErr_NewException("pydnspp.InvalidMessageOperation", NULL, NULL);
PyModule_AddObject(mod, "InvalidMessageOperation",
po_InvalidMessageOperation);
po_InvalidMessageUDPSize =
PyErr_NewException("pydnspp.InvalidMessageUDPSize", NULL, NULL);
PyModule_AddObject(mod, "InvalidMessageUDPSize", po_InvalidMessageUDPSize);
po_DNSMessageBADVERS = PyErr_NewException("pydnspp.DNSMessageBADVERS",
NULL, NULL);
PyModule_AddObject(mod, "DNSMessageBADVERS", po_DNSMessageBADVERS);
PyModule_AddObject(mod, "Message",
reinterpret_cast<PyObject*>(&message_type));
try {
//
// Constant class variables
//
// Parse mode
installClassVariable(message_type, "PARSE",
Py_BuildValue("I", Message::PARSE));
installClassVariable(message_type, "RENDER",
Py_BuildValue("I", Message::RENDER));
// Parse options
installClassVariable(message_type, "PARSE_DEFAULT",
Py_BuildValue("I", Message::PARSE_DEFAULT));
installClassVariable(message_type, "PRESERVE_ORDER",
Py_BuildValue("I", Message::PRESERVE_ORDER));
// Header flags
installClassVariable(message_type, "HEADERFLAG_QR",
Py_BuildValue("I", Message::HEADERFLAG_QR));
installClassVariable(message_type, "HEADERFLAG_AA",
Py_BuildValue("I", Message::HEADERFLAG_AA));
installClassVariable(message_type, "HEADERFLAG_TC",
Py_BuildValue("I", Message::HEADERFLAG_TC));
installClassVariable(message_type, "HEADERFLAG_RD",
Py_BuildValue("I", Message::HEADERFLAG_RD));
installClassVariable(message_type, "HEADERFLAG_RA",
Py_BuildValue("I", Message::HEADERFLAG_RA));
installClassVariable(message_type, "HEADERFLAG_AD",
Py_BuildValue("I", Message::HEADERFLAG_AD));
installClassVariable(message_type, "HEADERFLAG_CD",
Py_BuildValue("I", Message::HEADERFLAG_CD));
// Sections
installClassVariable(message_type, "SECTION_QUESTION",
Py_BuildValue("I", Message::SECTION_QUESTION));
installClassVariable(message_type, "SECTION_ANSWER",
Py_BuildValue("I", Message::SECTION_ANSWER));
installClassVariable(message_type, "SECTION_AUTHORITY",
Py_BuildValue("I", Message::SECTION_AUTHORITY));
installClassVariable(message_type, "SECTION_ADDITIONAL",
Py_BuildValue("I", Message::SECTION_ADDITIONAL));
// Protocol constant
installClassVariable(message_type, "DEFAULT_MAX_UDPSIZE",
Py_BuildValue("I", Message::DEFAULT_MAX_UDPSIZE));
/* Class-specific exceptions */
po_MessageTooShort =
PyErr_NewException("pydnspp.MessageTooShort", NULL, NULL);
PyObjectContainer(po_MessageTooShort).installToModule(
mod, "MessageTooShort");
po_InvalidMessageSection =
PyErr_NewException("pydnspp.InvalidMessageSection", NULL, NULL);
PyObjectContainer(po_InvalidMessageSection).installToModule(
mod, "InvalidMessageSection");
po_InvalidMessageOperation =
PyErr_NewException("pydnspp.InvalidMessageOperation", NULL, NULL);
PyObjectContainer(po_InvalidMessageOperation).installToModule(
mod, "InvalidMessageOperation");
po_InvalidMessageUDPSize =
PyErr_NewException("pydnspp.InvalidMessageUDPSize", NULL, NULL);
PyObjectContainer(po_InvalidMessageUDPSize).installToModule(
mod, "InvalidMessageUDPSize");
po_DNSMessageBADVERS =
PyErr_NewException("pydnspp.DNSMessageBADVERS", NULL, NULL);
PyObjectContainer(po_DNSMessageBADVERS).installToModule(
mod, "DNSMessageBADVERS");
} catch (const std::exception& ex) {
const std::string ex_what =
"Unexpected failure in Message initialization: " +
std::string(ex.what());
PyErr_SetString(po_IscException, ex_what.c_str());
return (false);
} catch (...) {
PyErr_SetString(PyExc_SystemError,
"Unexpected failure in Message initialization");
return (false);
}
return (true);
}
......
......@@ -29,9 +29,9 @@ if "TESTDATA_PATH" in os.environ:
else:
testdata_path = "../tests/testdata"
def factoryFromFile(message, file):
def factoryFromFile(message, file, parse_options=Message.PARSE_DEFAULT):
data = read_wire_data(file)
message.from_wire(data)
message.from_wire(data, parse_options)
return data
# we don't have direct comparison for rrsets right now (should we?
......@@ -466,6 +466,54 @@ test.example.com. 3600 IN A 192.0.2.2
self.assertEqual("192.0.2.2", rdata[1].to_text())
self.assertEqual(2, len(rdata))
def test_from_wire_short_buffer(self):
data = read_wire_data("message_fromWire22.wire")
self.assertRaises(DNSMessageFORMERR, self.p.from_wire, data[:-1])
def test_from_wire_combind_rrs(self):
factoryFromFile(self.p, "message_fromWire19.wire")
rrset = self.p.get_section(Message.SECTION_ANSWER)[0]
self.assertEqual(RRType("A"), rrset.get_type())
self.assertEqual(2, len(rrset.get_rdata()))
rrset = self.p.get_section(Message.SECTION_ANSWER)[1]
self.assertEqual(RRType("AAAA"), rrset.get_type())
self.assertEqual(1, len(rrset.get_rdata()))
def check_preserve_rrs(self, message, section):
rrset = message.get_section(section)[0]
self.assertEqual(RRType("A"), rrset.get_type())
rdata = rrset.get_rdata()
self.assertEqual(1, len(rdata))
self.assertEqual('192.0.2.1', rdata[0].to_text())
rrset = message.get_section(section)[1]
self.assertEqual(RRType("AAAA"), rrset.get_type())
rdata = rrset.get_rdata()
self.assertEqual(1, len(rdata))
self.assertEqual('2001:db8::1', rdata[0].to_text())
rrset = message.get_section(section)[2]
self.assertEqual(RRType("A"), rrset.get_type())
rdata = rrset.get_rdata()
self.assertEqual(1, len(rdata))
self.assertEqual('192.0.2.2', rdata[0].to_text())
def test_from_wire_preserve_answer(self):
factoryFromFile(self.p, "message_fromWire19.wire",
Message.PRESERVE_ORDER)
self.check_preserve_rrs(self.p, Message.SECTION_ANSWER)
def test_from_wire_preserve_authority(self):
factoryFromFile(self.p, "message_fromWire20.wire",
Message.PRESERVE_ORDER)
self.check_preserve_rrs(self.p, Message.SECTION_AUTHORITY)
def test_from_wire_preserve_additional(self):
factoryFromFile(self.p, "message_fromWire21.wire",
Message.PRESERVE_ORDER)
self.check_preserve_rrs(self.p, Message.SECTION_ADDITIONAL)
def test_EDNS0ExtCode(self):
# Extended Rcode = BADVERS
message_parse = Message(Message.PARSE)
......
......@@ -118,16 +118,20 @@ protected:
vector<unsigned char> received_data;
vector<unsigned char> expected_data;
void factoryFromFile(Message& message, const char* datafile);
void factoryFromFile(Message& message, const char* datafile,
Message::ParseOptions options =
Message::PARSE_DEFAULT);
};
void
MessageTest::factoryFromFile(Message& message, const char* datafile) {
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);
message.fromWire(buffer, options);
}
TEST_F(MessageTest, headerFlag) {
......@@ -175,7 +179,6 @@ TEST_F(MessageTest, headerFlag) {
EXPECT_THROW(message_parse.setHeaderFlag(Message::HEADERFLAG_QR),
InvalidMessageOperation);
}
TEST_F(MessageTest, getEDNS) {
EXPECT_FALSE(message_parse.getEDNS()); // by default EDNS isn't set
......@@ -532,7 +535,46 @@ TEST_F(MessageTest, appendSection) {
}
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));