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

[master] Merge branch 'trac813' with fixing conflicts

Conflicts:
	src/lib/dns/tests/tsigrecord_unittest.cc
parents 9a2ca8e5 25bc145b
......@@ -83,7 +83,7 @@ const unsigned int HEADERFLAG_MASK = 0x87b0;
const uint16_t MESSAGE_REPLYPRESERVE = (Message::HEADERFLAG_RD |
Message::HEADERFLAG_CD);
const char *sectiontext[] = {
const char* const sectiontext[] = {
"QUESTION",
"ANSWER",
"AUTHORITY",
......@@ -116,8 +116,8 @@ public:
vector<QuestionPtr> questions_;
vector<RRsetPtr> rrsets_[NUM_SECTIONS];
ConstEDNSPtr edns_;
ConstTSIGRecordPtr tsig_rr_;
// tsig/sig0: TODO
// RRsetsSorter* sorter_; : TODO
void init();
......@@ -125,6 +125,16 @@ public:
void setRcode(const Rcode& rcode);
int parseQuestion(InputBuffer& buffer);
int parseSection(const Message::Section section, InputBuffer& buffer);
void addRR(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, ConstRdataPtr rdata);
void addEDNS(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, const Rdata& rdata);
void addTSIG(Message::Section section, unsigned int count,
const InputBuffer& buffer, size_t start_position,
const Name& name, const RRClass& rrclass,
const RRTTL& ttl, const Rdata& rdata);
void toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx);
};
......@@ -143,6 +153,7 @@ MessageImpl::init() {
rcode_ = NULL;
opcode_ = NULL;
edns_ = EDNSPtr();
tsig_rr_ = ConstTSIGRecordPtr();
for (int i = 0; i < NUM_SECTIONS; ++i) {
counts_[i] = 0;
......@@ -413,6 +424,16 @@ Message::setEDNS(ConstEDNSPtr edns) {
impl_->edns_ = edns;
}
const TSIGRecord*
Message::getTSIGRecord() const {
if (impl_->mode_ != Message::PARSE) {
isc_throw(InvalidMessageOperation,
"getTSIGRecord performed in non-parse mode");
}
return (impl_->tsig_rr_.get());
}
unsigned int
Message::getRRCount(const Section section) const {
if (section >= MessageImpl::NUM_SECTIONS) {
......@@ -649,6 +670,9 @@ MessageImpl::parseSection(const Message::Section section,
unsigned int added = 0;
for (unsigned int count = 0; count < counts_[section]; ++count) {
// We need to remember the start position for TSIG processing
const size_t start_position = buffer.getPosition();
const Name name(buffer);
// buffer must store at least RR TYPE, RR CLASS, TTL, and RDLEN.
......@@ -666,32 +690,12 @@ MessageImpl::parseSection(const Message::Section section,
ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen);
if (rrtype == RRType::OPT()) {
if (section != Message::SECTION_ADDITIONAL) {
isc_throw(DNSMessageFORMERR,
"EDNS OPT RR found in an invalid section");
}
if (edns_) {
isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
}
uint8_t extended_rcode;
edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl,
*rdata, extended_rcode));
setRcode(Rcode(rcode_->getCode(), extended_rcode));
continue;
addEDNS(section, name, rrclass, rrtype, ttl, *rdata);
} else if (rrtype == RRType::TSIG()) {
addTSIG(section, count, buffer, start_position, name, rrclass, ttl,
*rdata);
} else {
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 =
RRsetPtr(new RRset(name, rrclass, rrtype, ttl));
rrset->addRdata(rdata);
rrsets_[section].push_back(rrset);
}
addRR(section, name, rrclass, rrtype, ttl, rdata);
++added;
}
}
......@@ -699,6 +703,65 @@ MessageImpl::parseSection(const Message::Section section,
return (added);
}
void
MessageImpl::addRR(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, ConstRdataPtr rdata)
{
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);
}
}
void
MessageImpl::addEDNS(Message::Section section, const Name& name,
const RRClass& rrclass, const RRType& rrtype,
const RRTTL& ttl, const Rdata& rdata)
{
if (section != Message::SECTION_ADDITIONAL) {
isc_throw(DNSMessageFORMERR,
"EDNS OPT RR found in an invalid section");
}
if (edns_) {
isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
}
uint8_t extended_rcode;
edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl, rdata,
extended_rcode));
setRcode(Rcode(rcode_->getCode(), extended_rcode));
}
void
MessageImpl::addTSIG(Message::Section section, unsigned int count,
const InputBuffer& buffer, size_t start_position,
const Name& name, const RRClass& rrclass,
const RRTTL& ttl, const Rdata& rdata)
{
if (section != Message::SECTION_ADDITIONAL) {
isc_throw(DNSMessageFORMERR,
"TSIG RR found in an invalid section");
}
if (count != counts_[section] - 1) {
isc_throw(DNSMessageFORMERR, "TSIG RR is not the last record");
}
if (tsig_rr_) {
isc_throw(DNSMessageFORMERR, "multiple TSIG RRs found");
}
tsig_rr_ = ConstTSIGRecordPtr(new TSIGRecord(name, rrclass,
ttl, rdata,
buffer.getPosition() -
start_position));
}
namespace {
template <typename T>
struct SectionFormatter {
......@@ -732,31 +795,31 @@ Message::toText() const {
// for simplicity we don't consider extended rcode (unlike BIND9)
s += ", status: " + impl_->rcode_->toText();
s += ", id: " + boost::lexical_cast<string>(impl_->qid_);
s += "\n;; flags: ";
s += "\n;; flags:";
if (getHeaderFlag(HEADERFLAG_QR)) {
s += "qr ";
s += " qr";
}
if (getHeaderFlag(HEADERFLAG_AA)) {
s += "aa ";
s += " aa";
}
if (getHeaderFlag(HEADERFLAG_TC)) {
s += "tc ";
s += " tc";
}
if (getHeaderFlag(HEADERFLAG_RD)) {
s += "rd ";
s += " rd";
}
if (getHeaderFlag(HEADERFLAG_RA)) {
s += "ra ";
s += " ra";
}
if (getHeaderFlag(HEADERFLAG_AD)) {
s += "ad ";
s += " ad";
}
if (getHeaderFlag(HEADERFLAG_CD)) {
s += "cd ";
s += " cd";
}
// for simplicity, don't consider the update case for now
s += "; QUESTION: " +
s += "; QUERY: " + // note: not "QUESTION" to be compatible with BIND 9 dig
lexical_cast<string>(impl_->counts_[SECTION_QUESTION]);
s += ", ANSWER: " +
lexical_cast<string>(impl_->counts_[SECTION_ANSWER]);
......@@ -767,6 +830,9 @@ Message::toText() const {
if (impl_->edns_ != NULL) {
++arcount;
}
if (impl_->tsig_rr_ != NULL) {
++arcount;
}
s += ", ADDITIONAL: " + lexical_cast<string>(arcount) + "\n";
if (impl_->edns_ != NULL) {
......@@ -803,6 +869,11 @@ Message::toText() const {
SectionFormatter<RRsetPtr>(SECTION_ADDITIONAL, s));
}
if (impl_->tsig_rr_ != NULL) {
s += "\n;; TSIG PSEUDOSECTION:\n";
s += impl_->tsig_rr_->toText();
}
return (s);
}
......
......@@ -34,6 +34,7 @@ class InputBuffer;
namespace dns {
class TSIGContext;
class TSIGRecord;
///
/// \brief A standard DNS module exception that is thrown if a wire format
......@@ -369,6 +370,25 @@ public:
/// \c Message.
void setEDNS(ConstEDNSPtr edns);
/// \brief Return, if any, the TSIG record contained in the received
/// message.
///
/// Currently, this method is only intended to return a TSIG record
/// for an incoming message built via the \c fromWire() method in the
/// PARSE mode. A call to this method in the RENDER mode is invalid and
/// result in an exception. Also, calling this method is meaningless
/// unless \c fromWire() is performed.
///
/// The returned pointer is valid only during the lifetime of the
/// \c Message object and until \c clear() is called. The \c Message
/// object retains the ownership of \c TSIGRecord; the caller must not
/// try to delete it.
///
/// \exception InvalidMessageOperation Message is not in the PARSE mode.
///
/// \return A pointer to the stored \c TSIGRecord or \c NULL.
const TSIGRecord* getTSIGRecord() const;
/// \brief Returns the number of RRs contained in the given section.
///
/// In the \c PARSE mode, the returned value may not be identical to
......@@ -582,6 +602,16 @@ private:
/// that originated the asynchronous call falls out of scope.
typedef boost::shared_ptr<Message> MessagePtr;
/// Insert the \c Message as a string into stream.
///
/// This method convert \c message into a string and inserts it into the
/// output stream \c os.
///
/// \param os A \c std::ostream object on which the insertion operation is
/// performed.
/// \param record A \c Message object output by the operation.
/// \return A reference to the same \c std::ostream object referenced by
/// parameter \c os after the insertion operation.
std::ostream& operator<<(std::ostream& os, const Message& message);
}
}
......
......@@ -317,7 +317,7 @@ class MessageTest(unittest.TestCase):
msg_str =\
""";; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4149
;; flags: qr aa rd ; QUESTION: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;test.example.com. IN A
......
......@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <fstream>
#include <boost/scoped_ptr.hpp>
#include <exceptions/exceptions.h>
......@@ -19,6 +21,9 @@
#include <util/buffer.h>
#include <util/time_utilities.h>
#include <util/unittests/testdata.h>
#include <util/unittests/textdata.h>
#include <dns/edns.h>
#include <dns/exceptions.h>
#include <dns/message.h>
......@@ -67,6 +72,10 @@ 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:
......@@ -185,6 +194,70 @@ TEST_F(MessageTest, setEDNS) {
EXPECT_EQ(edns, message_render.getEDNS());
}
TEST_F(MessageTest, fromWireWithTSIG) {
// Initially there should be no TSIG
EXPECT_EQ(static_cast<void*>(NULL), message_parse.getTSIGRecord());
// getTSIGRecord() is only valid in the parse mode.
EXPECT_THROW(message_render.getTSIGRecord(), InvalidMessageOperation);
factoryFromFile(message_parse, "message_toWire2.wire");
const char 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_NE(static_cast<void*>(NULL), 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());
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
tsig_rr->getRdata().getMAC(),
tsig_rr->getRdata().getMACSize(),
expected_mac, sizeof(expected_mac));
EXPECT_EQ(0, tsig_rr->getRdata().getError());
EXPECT_EQ(0, tsig_rr->getRdata().getOtherLen());
EXPECT_EQ(static_cast<void*>(NULL), tsig_rr->getRdata().getOtherData());
// If we clear the message for reuse, the recorded TSIG will be cleared.
message_parse.clear(Message::PARSE);
EXPECT_EQ(static_cast<void*>(NULL), 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_NE(static_cast<void*>(NULL), 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));
......@@ -607,6 +680,44 @@ TEST_F(MessageTest, toWireWithoutRcode) {
EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
}
TEST_F(MessageTest, toText) {
// Check toText() output for a typical DNS response with records in
// all sections
ifstream ifs;
unittests::openTestData("message_toText1.txt", ifs);
factoryFromFile(message_parse, "message_toText1.wire");
{
SCOPED_TRACE("Message toText test (basic case)");
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.
ifs.close();
message_parse.clear(Message::PARSE);
unittests::openTestData("message_toText2.txt", ifs);
factoryFromFile(message_parse, "message_toText2.wire");
{
SCOPED_TRACE("Message toText test with EDNS");
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.
ifs.close();
message_parse.clear(Message::PARSE);
unittests::openTestData("message_toText3.txt", ifs);
factoryFromFile(message_parse, "message_toText3.wire");
{
SCOPED_TRACE("Message toText test with TSIG");
unittests::matchTextData(ifs, message_parse.toText());
}
}
TEST_F(MessageTest, toTextWithoutOpcode) {
message_render.setRcode(Rcode::NOERROR());
EXPECT_THROW(message_render.toText(), InvalidMessageOperation);
......
......@@ -14,13 +14,16 @@
#include <gtest/gtest.h>
#include <util/unittests/testdata.h>
#include <dns/tests/unittest_util.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
isc::util::unittests::addTestDataPath(TEST_DATA_SRCDIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
isc::util::unittests::addTestDataPath(TEST_DATA_BUILDDIR);
return (RUN_ALL_TESTS());
}
......@@ -3,7 +3,12 @@ CLEANFILES = *.wire
BUILT_SOURCES = edns_toWire1.wire edns_toWire2.wire edns_toWire3.wire
BUILT_SOURCES += edns_toWire4.wire
BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire
BUILT_SOURCES += message_fromWire12.wire message_fromWire13.wire
BUILT_SOURCES += message_fromWire14.wire message_fromWire15.wire
BUILT_SOURCES += message_fromWire16.wire
BUILT_SOURCES += message_toWire2.wire message_toWire3.wire
BUILT_SOURCES += message_toText1.wire message_toText2.wire
BUILT_SOURCES += message_toText3.wire
BUILT_SOURCES += name_toWire5.wire name_toWire6.wire
BUILT_SOURCES += rdatafields1.wire rdatafields2.wire rdatafields3.wire
BUILT_SOURCES += rdatafields4.wire rdatafields5.wire rdatafields6.wire
......@@ -47,8 +52,13 @@ EXTRA_DIST += message_fromWire3 message_fromWire4
EXTRA_DIST += message_fromWire5 message_fromWire6
EXTRA_DIST += message_fromWire7 message_fromWire8
EXTRA_DIST += message_fromWire9 message_fromWire10.spec
EXTRA_DIST += message_fromWire11.spec
EXTRA_DIST += message_fromWire11.spec message_fromWire12.spec
EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec
EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec
EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec
EXTRA_DIST += message_toText1.txt message_toText1.spec
EXTRA_DIST += message_toText2.txt message_toText2.spec
EXTRA_DIST += message_toText3.txt message_toText3.spec
EXTRA_DIST += name_fromWire1 name_fromWire2 name_fromWire3_1 name_fromWire3_2
EXTRA_DIST += name_fromWire4 name_fromWire6 name_fromWire7 name_fromWire8
EXTRA_DIST += name_fromWire9 name_fromWire10 name_fromWire11 name_fromWire12
......
......@@ -15,7 +15,7 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import configparser, re, time, sys
import configparser, re, time, socket, sys
from datetime import datetime
from optparse import OptionParser
......@@ -215,6 +215,74 @@ class EDNS:
f.write('# RDLEN=%d\n' % self.rdlen)
f.write('%04x\n' % self.rdlen)
class RR:
'''This is a base class for various types of RR test data.
For each RR type (A, AAAA, NS, etc), we define a derived class of RR
to dump type specific RDATA parameters. This class defines parameters
common to all types of RDATA, namely the owner name, RR class and TTL.
The dump() method of derived classes are expected to call dump_header(),
whose default implementation is provided in this class. This method
decides whether to dump the test data as an RR (with name, type, class)
or only as RDATA (with its length), and dumps the corresponding data
via the specified file object.
By convention we assume derived classes are named after the common
standard mnemonic of the corresponding RR types. For example, the
derived class for the RR type SOA should be named "SOA".
Configurable parameters are as follows:
- as_rr (bool): Whether or not the data is to be dumped as an RR. False
by default.
- rr_class (string): The RR class of the data. Only meaningful when the
data is dumped as an RR. Default is 'IN'.
- rr_ttl (integer): The TTL value of the RR. Only meaningful when the
data is dumped as an RR. Default is 86400 (1 day).
'''
def __init__(self):
self.as_rr = False
# only when as_rr is True, same for class/TTL:
self.rr_name = 'example.com'
self.rr_class = 'IN'
self.rr_ttl = 86400
def dump_header(self, f, rdlen):
type_txt = self.__class__.__name__
type_code = parse_value(type_txt, dict_rrtype)
if self.as_rr:
rrclass = parse_value(self.rr_class, dict_rrclass)
f.write('\n# %s RR (QNAME=%s Class=%s TTL=%d RDLEN=%d)\n' %
(type_txt, self.rr_name,
code_totext(rrclass, rdict_rrclass), self.rr_ttl, rdlen))
f.write('%s %04x %04x %08x %04x\n' %
(encode_name(self.rr_name), type_code, rrclass,
self.rr_ttl, rdlen))
else:
f.write('\n# %s RDATA (RDLEN=%d)\n' % (type_txt, rdlen))
f.write('%04x\n' % rdlen)
class A(RR):
rdlen = 4 # fixed by default
address = '192.0.2.1'
def dump(self, f):
self.dump_header(f, self.rdlen)
f.write('# Address=%s\n' % (self.address))
bin_address = socket.inet_aton(self.address)
f.write('%02x%02x%02x%02x\n' % (bin_address[0], bin_address[1],
bin_address[2], bin_address[3]))
class NS(RR):
rdlen = None # auto calculate
nsname = 'ns.example.com'
def dump(self, f):
nsname_wire = encode_name(self.nsname)
if self.rdlen is None:
self.rdlen = len(nsname_wire) / 2
self.dump_header(f, self.rdlen)
f.write('# NS name=%s\n' % (self.nsname))
f.write('%s\n' % nsname_wire)
class SOA:
# this currently doesn't support name compression within the RDATA.
rdlen = -1 # auto-calculate
......@@ -432,12 +500,7 @@ class RRSIG:
f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer))
f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire))
class TSIG:
as_rr = False
rr_name = 'example.com' # only when as_rr is True, same for class/TTL
rr_class = parse_value('ANY', dict_rrclass)
rr_ttl = 0
class TSIG(RR):
rdlen = None # auto-calculate
algorithm = 'hmac-sha256'
time_signed = 1286978795 # arbitrarily chosen default
......@@ -449,12 +512,18 @@ class TSIG:
other_len = None # 6 if error is BADTIME; otherwise 0
other_data = None # use time_signed + fudge + 1 for BADTIME
dict_macsize = { 'hmac-md5' : 16, 'hmac-sha1' : 20, 'hmac-sha256' : 32 }
# TSIG has some special defaults
def __init__(self):
super().__init__()
self.rr_class = 'ANY'
self.rr_ttl = 0
def dump(self, f):
if str(self.algorithm) == 'hmac-md5':
name_wire = encode_name('hmac-md5.sig-alg.reg.int')
else:
name_wire = encode_name(self.algorithm)
rdlen = self.rdlen
mac_size = self.mac_size
if mac_size is None:
if self.algorithm in self.dict_macsize.keys():
......@@ -473,19 +542,10 @@ class TSIG:
if self.error == 18 else ''
else:
other_data = encode_string(self.other_data, other_len)
if rdlen is None:
rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
len(other_data) / 2)
if self.as_rr:
f.write('\n# TSIG RR (QNAME=%s Class=%s TTL=%d RDLEN=%d)\n' %
(self.rr_name, rdict_rrclass[self.rr_class],
self.rr_ttl, rdlen))
f.write('%s %04x %04x %08x %04x\n' %
(encode_name(self.rr_name), dict_rrtype['tsig'],
self.rr_class, self.rr_ttl, rdlen))
else:
f.write('\n# TSIG RDATA (RDLEN=%d)\n' % rdlen)