// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. // $Id$ #include #include #include #include #include #include namespace isc { namespace dns { namespace { // hide internal-only names from the public namespaces /// /// \brief The \c NameCompressNode class represents a pointer to a name /// rendered in the internal buffer for the \c MessageRendererImpl object. /// /// A \c MessageRendererImpl object maintains a set of the \c NameCompressNode /// objects, and searches the set for the position of the longest match /// (ancestor) name against each new name to be rendered into the buffer. struct NameCompressNode { NameCompressNode(const MessageRenderer& renderer, const OutputBuffer& buffer, const size_t pos, const size_t len) : renderer_(renderer), buffer_(buffer), pos_(pos), len_(len) {} /// The renderer that performs name compression using the node. /// This is kept in each node to detect the compression mode /// (case-sensitive or not) in the comparison functor (\c NameCompare). const MessageRenderer& renderer_; /// The buffer in which the corresponding name is rendered. const OutputBuffer& buffer_; /// The position (offset from the beginning) in the buffer where the /// name starts. uint16_t pos_; /// The length of the corresponding name. uint16_t len_; }; /// /// \brief The \c NameCompare class is a functor that gives ordering among /// \c NameCompressNode objects stored in \c MessageRendererImpl::nodeset_. /// /// Its only public method as a functor, \c operator(), gives the ordering /// between two \c NameCompressNode objects in terms of equivalence, that is, /// returns whether one is "less than" the other. /// For our purpose we only need to distinguish two different names, so the /// ordering is different from the canonical DNS name order used in DNSSEC; /// basically, it gives the case-insensitive ordering of the two names as their /// textual representation. struct NameCompare : public std::binary_function { /// /// Returns true if n1 < n2 as a result of case-insensitive comparison; /// otherwise return false. /// /// The name corresponding to \c n1 or \c n2 may be compressed, in which /// case we must follow the compression pointer in the associated buffer. /// The helper private method \c nextPosition() gives the position in the /// buffer for the next character, taking into account compression. /// bool operator()(const NameCompressNode& n1, const NameCompressNode& n2) const { if (n1.len_ < n2.len_) { return (true); } else if (n1.len_ > n2.len_) { return (false); } const bool case_sensitive = (n1.renderer_.getCompressMode() == MessageRenderer::CASE_SENSITIVE); uint16_t pos1 = n1.pos_; uint16_t pos2 = n2.pos_; uint16_t l1 = 0; uint16_t l2 = 0; for (uint16_t i = 0; i < n1.len_; i++, pos1++, pos2++) { pos1 = nextPosition(n1.buffer_, pos1, l1); pos2 = nextPosition(n2.buffer_, pos2, l2); if (case_sensitive) { if (n1.buffer_[pos1] < n2.buffer_[pos2]) { return (true); } else if (n1.buffer_[pos1] > n2.buffer_[pos2]) { return (false); } } else { if (tolower(n1.buffer_[pos1]) < tolower(n2.buffer_[pos2])) { return (true); } else if (tolower(n1.buffer_[pos1]) > tolower(n2.buffer_[pos2])) { return (false); } } } return (false); } private: uint16_t nextPosition(const OutputBuffer& buffer, uint16_t pos, uint16_t& llen) const { if (llen == 0) { int i = 0; while ((buffer[pos] & Name::COMPRESS_POINTER_MARK8) == Name::COMPRESS_POINTER_MARK8) { pos = (buffer[pos] & ~Name::COMPRESS_POINTER_MARK8) * 256 + buffer[pos + 1]; // This loop should stop as long as the buffer has been // constructed validly and the search/insert argument is based // on a valid name, which is an assumption for this class. // But we'll abort if a bug could cause an infinite loop. i += 2; assert(i < Name::MAX_WIRE); } llen = buffer[pos]; } else { --llen; } return (pos); } }; } /// /// \brief The \c MessageRendererImpl class is the actual implementation of /// \c MessageRenderer. /// /// The implementation is hidden from applications. We can refer to specific /// members of this class only within the implementation source file. /// struct MessageRenderer::MessageRendererImpl { /// \brief Constructor from an output buffer. /// /// \param buffer An \c OutputBuffer object to which wire format data is /// written. MessageRendererImpl(OutputBuffer& buffer) : buffer_(buffer), nbuffer_(Name::MAX_WIRE), msglength_limit_(512), truncated_(false), compress_mode_(MessageRenderer::CASE_INSENSITIVE) {} /// The buffer that holds the entire DNS message. OutputBuffer& buffer_; /// A local working buffer to convert each given name into wire format. /// This could be a local variable of the \c writeName() method, but /// we keep it in the class so that we can reuse it and avoid construction /// overhead. OutputBuffer nbuffer_; /// A set of compression pointers. std::set nodeset_; /// The maximum length of rendered data that can fit without /// truncation. uint16_t msglength_limit_; /// A boolean flag that indicates truncation has occurred while rendering /// the data. bool truncated_; /// The name compression mode. CompressMode compress_mode_; }; MessageRenderer::MessageRenderer(OutputBuffer& buffer) : impl_(new MessageRendererImpl(buffer)) {} MessageRenderer::~MessageRenderer() { delete impl_; } void MessageRenderer::skip(const size_t len) { impl_->buffer_.skip(len); } void MessageRenderer::trim(const size_t len) { impl_->buffer_.trim(len); } void MessageRenderer::clear() { impl_->buffer_.clear(); impl_->nbuffer_.clear(); impl_->nodeset_.clear(); impl_->msglength_limit_ = 512; impl_->truncated_ = false; impl_->compress_mode_ = CASE_INSENSITIVE; } void MessageRenderer::writeUint8(const uint8_t data) { impl_->buffer_.writeUint8(data); } void MessageRenderer::writeUint16(const uint16_t data) { impl_->buffer_.writeUint16(data); } void MessageRenderer::writeUint16At(const uint16_t data, const size_t pos) { impl_->buffer_.writeUint16At(data, pos); } void MessageRenderer::writeUint32(const uint32_t data) { impl_->buffer_.writeUint32(data); } void MessageRenderer::writeData(const void* const data, const size_t len) { impl_->buffer_.writeData(data, len); } const void* MessageRenderer::getData() const { return (impl_->buffer_.getData()); } size_t MessageRenderer::getLength() const { return (impl_->buffer_.getLength()); } size_t MessageRenderer::getLengthLimit() const { return (impl_->msglength_limit_); } void MessageRenderer::setLengthLimit(const size_t len) { impl_->msglength_limit_ = len; } bool MessageRenderer::isTruncated() const { return (impl_->truncated_); } void MessageRenderer::setTruncated() { impl_->truncated_ = true; } MessageRenderer::CompressMode MessageRenderer::getCompressMode() const { return (impl_->compress_mode_); } void MessageRenderer::setCompressMode(const CompressMode mode) { impl_->compress_mode_ = mode; } void MessageRenderer::writeName(const Name& name, const bool compress) { impl_->nbuffer_.clear(); name.toWire(impl_->nbuffer_); unsigned int i; std::set::const_iterator notfound = impl_->nodeset_.end(); std::set::const_iterator n = notfound; // Find the longest ancestor name in the rendered set that matches the // given name. for (i = 0; i < impl_->nbuffer_.getLength(); i += impl_->nbuffer_[i] + 1) { // skip the trailing null label if (impl_->nbuffer_[i] == 0) { continue; } n = impl_->nodeset_.find(NameCompressNode(*this, impl_->nbuffer_, i, impl_->nbuffer_.getLength() - i)); if (n != notfound) { break; } } // Record the current offset before extending the buffer. const size_t offset = impl_->buffer_.getLength(); // Write uncompress part... impl_->buffer_.writeData(impl_->nbuffer_.getData(), compress ? i : impl_->nbuffer_.getLength()); if (compress && n != notfound) { // ...and compression pointer if available. uint16_t pointer = (*n).pos_; pointer |= Name::COMPRESS_POINTER_MARK16; impl_->buffer_.writeUint16(pointer); } // Finally, add to the set the newly rendered name and its ancestors that // have not been in the set. for (unsigned int j = 0; j < i; j += impl_->nbuffer_[j] + 1) { if (impl_->nbuffer_[j] == 0) { continue; } if (offset + j > Name::MAX_COMPRESS_POINTER) { break; } impl_->nodeset_.insert(NameCompressNode(*this, impl_->buffer_, offset + j, impl_->nbuffer_.getLength() - j)); } } } }