Commit 7c54055c authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac871'

parents 5c497dfc 89fcb0f0
......@@ -87,6 +87,7 @@ libdns___la_SOURCES += question.h question.cc
libdns___la_SOURCES += tsig.h tsig.cc
libdns___la_SOURCES += tsigerror.h tsigerror.cc
libdns___la_SOURCES += tsigkey.h tsigkey.cc
libdns___la_SOURCES += tsigrecord.h tsigrecord.cc
libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
......
......@@ -110,19 +110,25 @@ EDNS::toText() const {
return (ret);
}
namespace {
/// Helper function to define unified implementation for the public versions
/// of toWire().
template <typename Output>
int
EDNS::toWire(Output& output, const uint8_t extended_rcode) const {
toWireCommon(Output& output, const uint8_t version,
const uint16_t udp_size, const bool dnssec_aware,
const uint8_t extended_rcode)
{
// Render EDNS OPT RR
uint32_t extrcode_flags = extended_rcode << EXTRCODE_SHIFT;
extrcode_flags |= (version_ << VERSION_SHIFT) & VERSION_MASK;
if (dnssec_aware_) {
extrcode_flags |= (version << VERSION_SHIFT) & VERSION_MASK;
if (dnssec_aware) {
extrcode_flags |= EXTFLAG_DO;
}
// Construct an RRset corresponding to the EDNS.
// We don't support any options for now, so the OPT RR can be empty.
RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size_),
RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size),
RRType::OPT(), RRTTL(extrcode_flags)));
edns_rrset->addRdata(ConstRdataPtr(new generic::OPT()));
......@@ -130,9 +136,12 @@ EDNS::toWire(Output& output, const uint8_t extended_rcode) const {
return (1);
}
}
unsigned int
EDNS::toWire(MessageRenderer& renderer, const uint8_t extended_rcode) const {
EDNS::toWire(AbstractMessageRenderer& renderer,
const uint8_t extended_rcode) const
{
// If adding the OPT RR would exceed the size limit, don't do it.
// 11 = len(".") + type(2byte) + class(2byte) + TTL(4byte) + RDLEN(2byte)
// (RDATA is empty in this simple implementation)
......@@ -140,12 +149,16 @@ EDNS::toWire(MessageRenderer& renderer, const uint8_t extended_rcode) const {
return (0);
}
return (toWire<MessageRenderer>(renderer, extended_rcode));
return (toWireCommon(renderer, version_, udp_size_, dnssec_aware_,
extended_rcode));
}
unsigned int
EDNS::toWire(isc::util::OutputBuffer& buffer, const uint8_t extended_rcode) const {
return (toWire<isc::util::OutputBuffer>(buffer, extended_rcode));
EDNS::toWire(isc::util::OutputBuffer& buffer,
const uint8_t extended_rcode) const
{
return (toWireCommon(buffer, version_, udp_size_, dnssec_aware_,
extended_rcode));
}
EDNS*
......
......@@ -32,7 +32,7 @@ namespace dns {
class EDNS;
class Name;
class MessageRenderer;
class AbstractMessageRenderer;
class RRClass;
class RRTTL;
class RRType;
......@@ -314,7 +314,7 @@ public:
/// \param extended_rcode Upper 8 bits of extended RCODE to be rendered as
/// part of the EDNS OPT RR.
/// \return 1 if the OPT RR fits in the message size limit; otherwise 0.
unsigned int toWire(MessageRenderer& renderer,
unsigned int toWire(AbstractMessageRenderer& renderer,
const uint8_t extended_rcode) const;
/// \brief Render the \c EDNS in the wire format.
......@@ -354,12 +354,6 @@ public:
// something like this.
//void addOption();
private:
/// Helper method to define unified implementation for the public versions
/// of toWire().
template <typename Output>
int toWire(Output& output, const uint8_t extended_rcode) const;
public:
/// \brief The highest EDNS version this implementation supports.
static const uint8_t SUPPORTED_VERSION = 0;
......
......@@ -15,6 +15,7 @@
#include <stdint.h>
#include <algorithm>
#include <cassert>
#include <string>
#include <sstream>
#include <vector>
......@@ -40,6 +41,7 @@
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rrset.h>
#include <dns/tsig.h>
using namespace std;
using namespace boost;
......@@ -123,6 +125,7 @@ public:
void setRcode(const Rcode& rcode);
int parseQuestion(InputBuffer& buffer);
int parseSection(const Message::Section section, InputBuffer& buffer);
void toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx);
};
MessageImpl::MessageImpl(Message::Mode mode) :
......@@ -164,6 +167,154 @@ MessageImpl::setRcode(const Rcode& rcode) {
rcode_ = &rcode_placeholder_;
}
namespace {
// This helper class is used by MessageImpl::toWire() to render a set of
// RRsets of a specific section of message to a given MessageRenderer.
//
// A RenderSection object is expected to be used with a QuestionIterator or
// SectionIterator. Its operator() is called for each RRset as the iterator
// iterates over the corresponding section, and it renders the RRset to
// the given MessageRenderer, while counting the number of RRs (note: not
// RRsets) successfully rendered. If the MessageRenderer reports the need
// for truncation (via its isTruncated() method), the RenderSection object
// stops rendering further RRsets. In addition, unless partial_ok (given on
// construction) is true, it removes any RRs that are partially rendered
// from the MessageRenderer.
//
// On the completion of rendering the entire section, the owner of the
// RenderSection object can get the number of rendered RRs via the
// getTotalCount() method.
template <typename T>
struct RenderSection {
RenderSection(AbstractMessageRenderer& renderer, const bool partial_ok) :
counter_(0), renderer_(renderer), partial_ok_(partial_ok),
truncated_(false)
{}
void operator()(const T& entry) {
// If it's already truncated, ignore the rest of the section.
if (truncated_) {
return;
}
const size_t pos0 = renderer_.getLength();
counter_ += entry->toWire(renderer_);
if (renderer_.isTruncated()) {
truncated_ = true;
if (!partial_ok_) {
// roll back to the end of the previous RRset.
renderer_.trim(renderer_.getLength() - pos0);
}
}
}
unsigned int getTotalCount() { return (counter_); }
unsigned int counter_;
AbstractMessageRenderer& renderer_;
const bool partial_ok_;
bool truncated_;
};
}
void
MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
if (mode_ != Message::RENDER) {
isc_throw(InvalidMessageOperation,
"Message rendering attempted in non render mode");
}
if (rcode_ == NULL) {
isc_throw(InvalidMessageOperation,
"Message rendering attempted without Rcode set");
}
if (opcode_ == NULL) {
isc_throw(InvalidMessageOperation,
"Message rendering attempted without Opcode set");
}
// reserve room for the header
renderer.skip(HEADERLEN);
uint16_t qdcount =
for_each(questions_.begin(), questions_.end(),
RenderSection<QuestionPtr>(renderer, false)).getTotalCount();
// TODO: sort RRsets in each section based on configuration policy.
uint16_t ancount = 0;
if (!renderer.isTruncated()) {
ancount =
for_each(rrsets_[Message::SECTION_ANSWER].begin(),
rrsets_[Message::SECTION_ANSWER].end(),
RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
}
uint16_t nscount = 0;
if (!renderer.isTruncated()) {
nscount =
for_each(rrsets_[Message::SECTION_AUTHORITY].begin(),
rrsets_[Message::SECTION_AUTHORITY].end(),
RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
}
uint16_t arcount = 0;
if (renderer.isTruncated()) {
flags_ |= Message::HEADERFLAG_TC;
} else {
arcount =
for_each(rrsets_[Message::SECTION_ADDITIONAL].begin(),
rrsets_[Message::SECTION_ADDITIONAL].end(),
RenderSection<RRsetPtr>(renderer, false)).getTotalCount();
}
// Add EDNS OPT RR if necessary. Basically, we add it only when EDNS
// has been explicitly set. However, if the RCODE would require it and
// no EDNS has been set we generate a temporary local EDNS and use it.
if (!renderer.isTruncated()) {
ConstEDNSPtr local_edns = edns_;
if (!local_edns && rcode_->getExtendedCode() != 0) {
local_edns = ConstEDNSPtr(new EDNS());
}
if (local_edns) {
arcount += local_edns->toWire(renderer, rcode_->getExtendedCode());
}
}
// Adjust the counter buffer.
// XXX: these may not be equal to the number of corresponding entries
// in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
// was inserted. This is not good, and we should revisit the entire
// design.
counts_[Message::SECTION_QUESTION] = qdcount;
counts_[Message::SECTION_ANSWER] = ancount;
counts_[Message::SECTION_AUTHORITY] = nscount;
counts_[Message::SECTION_ADDITIONAL] = arcount;
// fill in the header
size_t header_pos = 0;
renderer.writeUint16At(qid_, header_pos);
header_pos += sizeof(uint16_t);
uint16_t codes_and_flags =
(opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK;
codes_and_flags |= (rcode_->getCode() & RCODE_MASK);
codes_and_flags |= (flags_ & HEADERFLAG_MASK);
renderer.writeUint16At(codes_and_flags, header_pos);
header_pos += sizeof(uint16_t);
// TODO: should avoid repeated pattern
renderer.writeUint16At(qdcount, header_pos);
header_pos += sizeof(uint16_t);
renderer.writeUint16At(ancount, header_pos);
header_pos += sizeof(uint16_t);
renderer.writeUint16At(nscount, header_pos);
header_pos += sizeof(uint16_t);
renderer.writeUint16At(arcount, header_pos);
// Add TSIG, if necessary, at the end of the message.
// TODO: truncate case consideration
if (tsig_ctx != NULL) {
tsig_ctx->sign(qid_, renderer.getData(),
renderer.getLength())->toWire(renderer);
// update the ARCOUNT for the TSIG RR. Note that for a sane DNS
// message arcount should never overflow to 0.
renderer.writeUint16At(++arcount, header_pos);
}
}
Message::Message(Mode mode) :
impl_(new MessageImpl(mode))
{}
......@@ -363,129 +514,14 @@ Message::addQuestion(const Question& question) {
addQuestion(QuestionPtr(new Question(question)));
}
namespace {
template <typename T>
struct RenderSection {
RenderSection(MessageRenderer& renderer, const bool partial_ok) :
counter_(0), renderer_(renderer), partial_ok_(partial_ok),
truncated_(false)
{}
void operator()(const T& entry) {
// If it's already truncated, ignore the rest of the section.
if (truncated_) {
return;
}
const size_t pos0 = renderer_.getLength();
counter_ += entry->toWire(renderer_);
if (renderer_.isTruncated()) {
truncated_ = true;
if (!partial_ok_) {
// roll back to the end of the previous RRset.
renderer_.trim(renderer_.getLength() - pos0);
}
}
}
unsigned int getTotalCount() { return (counter_); }
unsigned int counter_;
MessageRenderer& renderer_;
const bool partial_ok_;
bool truncated_;
};
void
Message::toWire(AbstractMessageRenderer& renderer) {
impl_->toWire(renderer, NULL);
}
void
Message::toWire(MessageRenderer& renderer) {
if (impl_->mode_ != Message::RENDER) {
isc_throw(InvalidMessageOperation,
"Message rendering attempted in non render mode");
}
if (impl_->rcode_ == NULL) {
isc_throw(InvalidMessageOperation,
"Message rendering attempted without Rcode set");
}
if (impl_->opcode_ == NULL) {
isc_throw(InvalidMessageOperation,
"Message rendering attempted without Opcode set");
}
// reserve room for the header
renderer.skip(HEADERLEN);
uint16_t qdcount =
for_each(impl_->questions_.begin(), impl_->questions_.end(),
RenderSection<QuestionPtr>(renderer, false)).getTotalCount();
// TBD: sort RRsets in each section based on configuration policy.
uint16_t ancount = 0;
if (!renderer.isTruncated()) {
ancount =
for_each(impl_->rrsets_[SECTION_ANSWER].begin(),
impl_->rrsets_[SECTION_ANSWER].end(),
RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
}
uint16_t nscount = 0;
if (!renderer.isTruncated()) {
nscount =
for_each(impl_->rrsets_[SECTION_AUTHORITY].begin(),
impl_->rrsets_[SECTION_AUTHORITY].end(),
RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
}
uint16_t arcount = 0;
if (renderer.isTruncated()) {
setHeaderFlag(HEADERFLAG_TC, true);
} else {
arcount =
for_each(impl_->rrsets_[SECTION_ADDITIONAL].begin(),
impl_->rrsets_[SECTION_ADDITIONAL].end(),
RenderSection<RRsetPtr>(renderer, false)).getTotalCount();
}
// Add EDNS OPT RR if necessary. Basically, we add it only when EDNS
// has been explicitly set. However, if the RCODE would require it and
// no EDNS has been set we generate a temporary local EDNS and use it.
if (!renderer.isTruncated()) {
ConstEDNSPtr local_edns = impl_->edns_;
if (!local_edns && impl_->rcode_->getExtendedCode() != 0) {
local_edns = ConstEDNSPtr(new EDNS());
}
if (local_edns) {
arcount += local_edns->toWire(renderer,
impl_->rcode_->getExtendedCode());
}
}
// Adjust the counter buffer.
// XXX: these may not be equal to the number of corresponding entries
// in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
// was inserted. This is not good, and we should revisit the entire
// design.
impl_->counts_[SECTION_QUESTION] = qdcount;
impl_->counts_[SECTION_ANSWER] = ancount;
impl_->counts_[SECTION_AUTHORITY] = nscount;
impl_->counts_[SECTION_ADDITIONAL] = arcount;
// TBD: TSIG, SIG(0) etc.
// fill in the header
size_t header_pos = 0;
renderer.writeUint16At(impl_->qid_, header_pos);
header_pos += sizeof(uint16_t);
uint16_t codes_and_flags =
(impl_->opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK;
codes_and_flags |= (impl_->rcode_->getCode() & RCODE_MASK);
codes_and_flags |= (impl_->flags_ & HEADERFLAG_MASK);
renderer.writeUint16At(codes_and_flags, header_pos);
header_pos += sizeof(uint16_t);
// XXX: should avoid repeated pattern (TODO)
renderer.writeUint16At(qdcount, header_pos);
header_pos += sizeof(uint16_t);
renderer.writeUint16At(ancount, header_pos);
header_pos += sizeof(uint16_t);
renderer.writeUint16At(nscount, header_pos);
header_pos += sizeof(uint16_t);
renderer.writeUint16At(arcount, header_pos);
header_pos += sizeof(uint16_t);
Message::toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx) {
impl_->toWire(renderer, &tsig_ctx);
}
void
......
......@@ -33,6 +33,7 @@ class InputBuffer;
}
namespace dns {
class TSIGContext;
///
/// \brief A standard DNS module exception that is thrown if a wire format
......@@ -80,7 +81,7 @@ public:
typedef uint16_t qid_t;
class MessageRenderer;
class AbstractMessageRenderer;
class Message;
class MessageImpl;
class Opcode;
......@@ -523,13 +524,31 @@ public:
/// class \c InvalidMessageOperation will be thrown.
std::string toText() const;
/// \brief Render the message in wire formant into a \c MessageRenderer
/// \brief Render the message in wire formant into a message renderer
/// object.
///
/// This \c Message must be in the \c RENDER mode and both \c Opcode and
/// \c Rcode must have been set beforehand; otherwise, an exception of
/// class \c InvalidMessageOperation will be thrown.
void toWire(MessageRenderer& renderer);
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
void toWire(AbstractMessageRenderer& renderer);
/// \brief Render the message in wire formant into a message renderer
/// object with TSIG.
///
/// This method is similar to the other version of \c toWire(), but
/// it will also add a TSIG RR with (in many cases) the TSIG MAC for
/// the message along with the given TSIG context (\c tsig_ctx).
/// The TSIG RR will be placed at the end of \c renderer.
/// \c tsig_ctx will be updated based on the fact it was used for signing
/// and with the latest MAC.
///
/// \param renderer See the other version
/// \param tsig_ctx A TSIG context that is to be used for signing the
/// message
void toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx);
/// \brief Parse the header section of the \c Message.
void parseHeader(isc::util::InputBuffer& buffer);
......
......@@ -56,7 +56,7 @@ Question::toWire(OutputBuffer& buffer) const {
}
unsigned int
Question::toWire(MessageRenderer& renderer) const {
Question::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(name_);
rrtype_.toWire(renderer);
rrclass_.toWire(renderer);
......
......@@ -32,7 +32,7 @@ class OutputBuffer;
namespace dns {
class MessageRenderer;
class AbstractMessageRenderer;
class Question;
/// \brief A pointer-like type pointing to an \c Question object.
......@@ -218,13 +218,13 @@ public:
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
/// \return 1
unsigned int toWire(MessageRenderer& renderer) const;
unsigned int toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the Question in the wire format without name compression.
///
/// This method behaves like the render version except it doesn't compress
/// the owner name.
/// See \c toWire(MessageRenderer& renderer)const.
/// See \c toWire(AbstractMessageRenderer& renderer)const.
///
/// \param buffer An output buffer to store the wire data.
/// \return 1
......
......@@ -24,7 +24,7 @@
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/tsigerror.h>
using namespace std;
using namespace boost;
......@@ -313,15 +313,7 @@ TSIG::toText() const {
result += encodeBase64(impl_->mac_) + " ";
}
result += lexical_cast<string>(impl_->original_id_) + " ";
if (impl_->error_ == 16) { // XXX: we'll soon introduce generic converter.
result += "BADSIG ";
} else if (impl_->error_ == 17) {
result += "BADKEY ";
} else if (impl_->error_ == 18) {
result += "BADTIME ";
} else {
result += lexical_cast<string>(impl_->error_) + " ";
}
result += TSIGError(impl_->error_).toText() + " ";
result += lexical_cast<string>(impl_->other_data_.size());
if (impl_->other_data_.size() > 0) {
result += " " + encodeBase64(impl_->other_data_);
......
......@@ -31,7 +31,7 @@ class OutputBuffer;
namespace dns {
// forward declarations
class MessageRenderer;
class AbstractMessageRenderer;
///
/// \brief A standard DNS module exception that is thrown if an RRClass object
......@@ -169,7 +169,7 @@ public:
/// standard exception will be thrown.
///
/// \param buffer An output buffer to store the wire data.
void toWire(MessageRenderer& renderer) const;
void toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the \c RRClass in the wire format.
///
/// This method renders the class code in network byte order into the
......
......@@ -52,7 +52,7 @@ RRClass::toWire(OutputBuffer& buffer) const {
}
void
RRClass::toWire(MessageRenderer& renderer) const {
RRClass::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint16(classcode_);
}
......
......@@ -104,8 +104,8 @@ AbstractRRset::toWire(OutputBuffer& buffer) const {
}
unsigned int
AbstractRRset::toWire(MessageRenderer& renderer) const {
const unsigned int rrs_written = rrsetToWire<MessageRenderer>(
AbstractRRset::toWire(AbstractMessageRenderer& renderer) const {
const unsigned int rrs_written = rrsetToWire<AbstractMessageRenderer>(
*this, renderer, renderer.getLengthLimit());
if (getRdataCount() > rrs_written) {
renderer.setTruncated();
......@@ -202,7 +202,7 @@ BasicRRset::toWire(OutputBuffer& buffer) const {
}
unsigned int
BasicRRset::toWire(MessageRenderer& renderer) const {
BasicRRset::toWire(AbstractMessageRenderer& renderer) const {
return (AbstractRRset::toWire(renderer));
}
......
......@@ -47,7 +47,7 @@ class Name;
class RRType;
class RRClass;
class RRTTL;
class MessageRenderer;
class AbstractMessageRenderer;
class AbstractRRset;
class BasicRRset;
class RdataIterator;
......@@ -311,7 +311,7 @@ public:
/// \return The number of RRs rendered. If the truncation is necessary
/// this value may be different from the number of RDATA objects contained
/// in the RRset.
virtual unsigned int toWire(MessageRenderer& renderer) const = 0;
virtual unsigned int toWire(AbstractMessageRenderer& renderer) const = 0;
/// \brief Render the RRset in the wire format without any compression.
///
......@@ -617,7 +617,7 @@ public:
///
/// This method simply uses the default implementation.
/// See \c AbstractRRset::toWire(MessageRenderer&)const.
virtual unsigned int toWire(MessageRenderer& renderer) const;
virtual unsigned int toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the RRset in the wire format without any compression.
///
......
......@@ -63,7 +63,7 @@ RRTTL::toWire(OutputBuffer& buffer) const {
}
void
RRTTL::toWire(MessageRenderer& renderer) const {
RRTTL::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint32(ttlval_);
}
......
......@@ -28,7 +28,7 @@ class OutputBuffer;
namespace dns {
// forward declarations
class MessageRenderer;</