LCOV - code coverage report
Current view: top level - dns - messagerenderer.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 115 119 96.6 %
Date: 2012-05-15 Functions: 16 19 84.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 88 122 72.1 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
       2                 :            : //
       3                 :            : // Permission to use, copy, modify, and/or distribute this software for any
       4                 :            : // purpose with or without fee is hereby granted, provided that the above
       5                 :            : // copyright notice and this permission notice appear in all copies.
       6                 :            : //
       7                 :            : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
       8                 :            : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       9                 :            : // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
      10                 :            : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
      11                 :            : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
      12                 :            : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
      13                 :            : // PERFORMANCE OF THIS SOFTWARE.
      14                 :            : 
      15                 :            : #include <exceptions/exceptions.h>
      16                 :            : #include <util/buffer.h>
      17                 :            : #include <dns/name.h>
      18                 :            : #include <dns/name_internal.h>
      19                 :            : #include <dns/labelsequence.h>
      20                 :            : #include <dns/messagerenderer.h>
      21                 :            : 
      22                 :            : #include <boost/array.hpp>
      23                 :            : #include <boost/static_assert.hpp>
      24                 :            : 
      25                 :            : #include <limits>
      26                 :            : #include <cassert>
      27                 :            : #include <vector>
      28                 :            : 
      29                 :            : using namespace std;
      30                 :            : using namespace isc::util;
      31                 :            : using isc::dns::name::internal::maptolower;
      32                 :            : 
      33                 :            : namespace isc {
      34                 :            : namespace dns {
      35                 :            : 
      36                 :            : namespace {     // hide internal-only names from the public namespaces
      37                 :            : ///
      38                 :            : /// \brief The \c OffsetItem class represents a pointer to a name
      39                 :            : /// rendered in the internal buffer for the \c MessageRendererImpl object.
      40                 :            : ///
      41                 :            : /// A \c MessageRendererImpl object maintains a set of \c OffsetItem
      42                 :            : /// objects in a hash table, and searches the table for the position of the
      43                 :            : /// longest match (ancestor) name against each new name to be rendered into
      44                 :            : /// the buffer.
      45                 :          0 : struct OffsetItem {
      46                 :            :     OffsetItem(size_t hash, size_t pos, size_t len) :
      47                 :       3777 :         hash_(hash), pos_(pos), len_(len)
      48                 :            :     {}
      49                 :            : 
      50                 :            :     /// The hash value for the stored name calculated by LabelSequence.getHash.
      51                 :            :     /// This will help make name comparison in \c NameCompare more efficient.
      52                 :            :     size_t hash_;
      53                 :            : 
      54                 :            :     /// The position (offset from the beginning) in the buffer where the
      55                 :            :     /// name starts.
      56                 :            :     uint16_t pos_;
      57                 :            : 
      58                 :            :     /// The length of the corresponding sequence (which is a domain name).
      59                 :            :     uint16_t len_;
      60                 :            : };
      61                 :            : 
      62                 :            : /// \brief The \c NameCompare class is a functor that checks equality
      63                 :            : /// between the name corresponding to an \c OffsetItem object and the name
      64                 :            : /// consists of labels represented by a \c LabelSequence object.
      65                 :            : ///
      66                 :            : /// Template parameter CASE_SENSITIVE determines whether to ignore the case
      67                 :            : /// of the names.  This policy doesn't change throughout the lifetime of
      68                 :            : /// this object, so we separate these using template to avoid unnecessary
      69                 :            : /// condition check.
      70                 :            : template <bool CASE_SENSITIVE>
      71                 :            : struct NameCompare {
      72                 :            :     /// \brief Constructor
      73                 :            :     ///
      74                 :            :     /// \param buffer The buffer for rendering used in the caller renderer
      75                 :            :     /// \param name_buf An input buffer storing the wire-format data of the
      76                 :            :     /// name to be newly rendered (and only that data).
      77                 :            :     /// \param hash The hash value for the name.
      78                 :            :     NameCompare(const OutputBuffer& buffer, InputBuffer& name_buf,
      79                 :            :                 size_t hash) :
      80                 :            :         buffer_(&buffer), name_buf_(&name_buf), hash_(hash)
      81                 :            :     {}
      82                 :            : 
      83                 :      20105 :     bool operator()(const OffsetItem& item) const {
      84                 :            :         // Trivial inequality check.  If either the hash or the total length
      85                 :            :         // doesn't match, the names are obviously different.
      86 [ +  + ][ +  - ]:      20105 :         if (item.hash_  != hash_ || item.len_ != name_buf_->getLength()) {
         [ +  + ][ +  + ]
         [ +  - ][ +  + ]
      87                 :            :             return (false);
      88                 :            :         }
      89                 :            : 
      90                 :            :         // Compare the name data, character-by-character.
      91                 :            :         // item_pos keeps track of the position in the buffer corresponding to
      92                 :            :         // the character to compare.  item_label_len is the number of
      93                 :            :         // characters in the labels where the character pointed by item_pos
      94                 :            :         // belongs.  When it reaches zero, nextPosition() identifies the
      95                 :            :         // position for the subsequent label, taking into account name
      96                 :            :         // compression, and resets item_label_len to the length of the new
      97                 :            :         // label.
      98                 :       2275 :         name_buf_->setPosition(0); // buffer can be reused, so reset position
      99                 :       2275 :         uint16_t item_pos = item.pos_;
     100                 :       2275 :         uint16_t item_label_len = 0;
     101 [ +  + ][ +  + ]:      47487 :         for (size_t i = 0; i < item.len_; ++i, ++item_pos) {
     102                 :      54764 :             item_pos = nextPosition(*buffer_, item_pos, item_label_len);
     103                 :      27382 :             const unsigned char ch1 = (*buffer_)[item_pos];
     104                 :      27382 :             const unsigned char ch2 = name_buf_->readUint8();
     105                 :            :             if (CASE_SENSITIVE) {
     106         [ +  - ]:       2999 :                 if (ch1 != ch2) {
     107                 :            :                     return (false);
     108                 :            :                 }
     109                 :            :             } else {
     110         [ +  - ]:      24383 :                 if (maptolower[ch1] != maptolower[ch2]) {
     111                 :            :                     return (false);
     112                 :            :                 }
     113                 :            :             }
     114                 :            :         }
     115                 :            : 
     116                 :            :         return (true);
     117                 :            :     }
     118                 :            : 
     119                 :            : private:
     120                 :          0 :     uint16_t nextPosition(const OutputBuffer& buffer,
     121                 :            :                           uint16_t pos, uint16_t& llen) const
     122                 :            :     {
     123 [ #  # ][ #  # ]:      27382 :         if (llen == 0) {
         [ +  + ][ +  + ]
     124                 :            :             size_t i = 0;
     125                 :            : 
     126 [ #  # ][ #  # ]:       6370 :             while ((buffer[pos] & Name::COMPRESS_POINTER_MARK8) ==
         [ +  + ][ +  + ]
     127                 :            :                    Name::COMPRESS_POINTER_MARK8) {
     128                 :        134 :                 pos = (buffer[pos] & ~Name::COMPRESS_POINTER_MARK8) *
     129                 :            :                     256 + buffer[pos + 1];
     130                 :            : 
     131                 :            :                 // This loop should stop as long as the buffer has been
     132                 :            :                 // constructed validly and the search/insert argument is based
     133                 :            :                 // on a valid name, which is an assumption for this class.
     134                 :            :                 // But we'll abort if a bug could cause an infinite loop.
     135                 :        134 :                 i += 2;
     136 [ #  # ][ #  # ]:       6370 :                 assert(i < Name::MAX_WIRE);
         [ -  + ][ -  + ]
     137                 :            :             }
     138                 :       6236 :             llen = buffer[pos];
     139                 :            :         } else {
     140                 :      21146 :             --llen;
     141                 :            :         }
     142                 :          0 :         return (pos);
     143                 :            :     }
     144                 :            : 
     145                 :            :     const OutputBuffer* buffer_;
     146                 :            :     InputBuffer* name_buf_;
     147                 :            :     const size_t hash_;
     148                 :            : };
     149                 :            : }
     150                 :            : 
     151                 :            : ///
     152                 :            : /// \brief The \c MessageRendererImpl class is the actual implementation of
     153                 :            : /// \c MessageRenderer.
     154                 :            : ///
     155                 :            : /// The implementation is hidden from applications.  We can refer to specific
     156                 :            : /// members of this class only within the implementation source file.
     157                 :            : ///
     158                 :            : /// It internally holds a hash table for OffsetItem objects corresponding
     159                 :            : /// to portions of names rendered in this renderer.  The offset information
     160                 :            : /// is used to compress subsequent names to be rendered.
     161 [ +  - ][ +  + ]:      79560 : struct MessageRenderer::MessageRendererImpl {
     162                 :            :     // The size of hash buckets and number of hash entries per bucket for
     163                 :            :     // which space is preallocated and kept reserved for subsequent rendering
     164                 :            :     // to provide better performance.  These values are derived from the
     165                 :            :     // BIND 9 implementation that uses a similar hash table.
     166                 :            :     static const size_t BUCKETS = 64;
     167                 :            :     static const size_t RESERVED_ITEMS = 16;
     168                 :            :     static const uint16_t NO_OFFSET = 65535; // used as a marker of 'not found'
     169                 :            : 
     170                 :            :     /// \brief Constructor
     171                 :            :     MessageRendererImpl() :
     172                 :            :         msglength_limit_(512), truncated_(false),
     173         [ +  + ]:      79560 :         compress_mode_(MessageRenderer::CASE_INSENSITIVE)
     174                 :            :     {
     175                 :            :         // Reserve some spaces for hash table items.
     176         [ +  + ]:      79560 :         for (size_t i = 0; i < BUCKETS; ++i) {
     177         [ +  - ]:      78336 :             table_[i].reserve(RESERVED_ITEMS);
     178                 :            :         }
     179 [ #  # ][ #  # ]:          0 :     }
     180                 :            : 
     181                 :            :     uint16_t findOffset(const OutputBuffer& buffer, InputBuffer& name_buf,
     182                 :            :                         size_t hash, bool case_sensitive) const
     183                 :            :     {
     184                 :            :         // Find a matching entry, if any.  We use some heuristics here: often
     185                 :            :         // the same name appers consecutively (like repeating the same owner
     186                 :            :         // name for a single RRset), so in case there's a collision in the
     187                 :            :         // bucket it will be more likely to find it in the tail side of the
     188                 :            :         // bucket.
     189                 :       6069 :         const size_t bucket_id = hash % BUCKETS;
     190                 :            :         vector<OffsetItem>::const_reverse_iterator found;
     191         [ +  + ]:       6069 :         if (case_sensitive) {
     192                 :            :             found = find_if(table_[bucket_id].rbegin(),
     193                 :            :                             table_[bucket_id].rend(),
     194                 :       1087 :                             NameCompare<true>(buffer, name_buf, hash));
     195                 :            :         } else {
     196                 :            :             found = find_if(table_[bucket_id].rbegin(),
     197                 :            :                             table_[bucket_id].rend(),
     198                 :       4982 :                             NameCompare<false>(buffer, name_buf, hash));
     199                 :            :         }
     200         [ +  + ]:       6069 :         if (found != table_[bucket_id].rend()) {
     201                 :       2275 :             return (found->pos_);
     202                 :            :         }
     203                 :            :         return (NO_OFFSET);
     204                 :            :     }
     205                 :            : 
     206                 :            :     void addOffset(size_t hash, size_t offset, size_t len) {
     207                 :       3777 :         table_[hash % BUCKETS].push_back(OffsetItem(hash, offset, len));
     208                 :            :     }
     209                 :            : 
     210                 :            :     // The hash table for the (offset + position in the buffer) entries
     211                 :            :     vector<OffsetItem> table_[BUCKETS];
     212                 :            :     /// The maximum length of rendered data that can fit without
     213                 :            :     /// truncation.
     214                 :            :     uint16_t msglength_limit_;
     215                 :            :     /// A boolean flag that indicates truncation has occurred while rendering
     216                 :            :     /// the data.
     217                 :            :     bool truncated_;
     218                 :            :     /// The name compression mode.
     219                 :            :     CompressMode compress_mode_;
     220                 :            : 
     221                 :            :     // Placeholder for hash values as they are calculated in writeName().
     222                 :            :     // Note: we may want to make it a local variable of writeName() if it
     223                 :            :     // works more efficiently.
     224                 :            :     boost::array<size_t, Name::MAX_LABELS> seq_hashes_;
     225                 :            : };
     226                 :            : 
     227                 :       1224 : MessageRenderer::MessageRenderer() :
     228                 :            :     AbstractMessageRenderer(),
     229         [ +  - ]:       1224 :     impl_(new MessageRendererImpl)
     230                 :       1224 : {}
     231                 :            : 
     232                 :       1224 : MessageRenderer::~MessageRenderer() {
     233         [ +  - ]:       2448 :     delete impl_;
     234                 :       1683 : }
     235                 :            : 
     236                 :            : void
     237                 :        630 : MessageRenderer::clear() {
     238                 :        630 :     AbstractMessageRenderer::clear();
     239                 :        630 :     impl_->msglength_limit_ = 512;
     240                 :        630 :     impl_->truncated_ = false;
     241                 :        630 :     impl_->compress_mode_ = CASE_INSENSITIVE;
     242                 :            : 
     243                 :            :     // Clear the hash table.  We reserve the minimum space for possible
     244                 :            :     // subsequent use of the renderer.
     245         [ +  + ]:      40950 :     for (size_t i = 0; i < MessageRendererImpl::BUCKETS; ++i) {
     246         [ +  + ]:      40320 :         if (impl_->table_[i].size() > MessageRendererImpl::RESERVED_ITEMS) {
     247                 :            :             // Trim excessive capacity: swap ensures the new capacity is only
     248                 :            :             // reasonably large for the reserved space.
     249                 :            :             vector<OffsetItem> new_table;
     250         [ +  - ]:         28 :             new_table.reserve(MessageRendererImpl::RESERVED_ITEMS);
     251                 :         28 :             new_table.swap(impl_->table_[i]);
     252                 :            :         }
     253                 :      40320 :         impl_->table_[i].clear();
     254                 :            :     }
     255                 :        630 : }
     256                 :            : 
     257                 :            : size_t
     258                 :       3662 : MessageRenderer::getLengthLimit() const {
     259                 :       3662 :     return (impl_->msglength_limit_);
     260                 :            : }
     261                 :            : 
     262                 :            : void
     263                 :        502 : MessageRenderer::setLengthLimit(const size_t len) {
     264                 :        502 :     impl_->msglength_limit_ = len;
     265                 :        502 : }
     266                 :            : 
     267                 :            : bool
     268                 :       5177 : MessageRenderer::isTruncated() const {
     269                 :       5177 :     return (impl_->truncated_);
     270                 :            : }
     271                 :            : 
     272                 :            : void
     273                 :         21 : MessageRenderer::setTruncated() {
     274                 :         21 :     impl_->truncated_ = true;
     275                 :         21 : }
     276                 :            : 
     277                 :            : MessageRenderer::CompressMode
     278                 :        927 : MessageRenderer::getCompressMode() const {
     279                 :        927 :     return (impl_->compress_mode_);
     280                 :            : }
     281                 :            : 
     282                 :            : void
     283                 :        152 : MessageRenderer::setCompressMode(const CompressMode mode) {
     284         [ +  + ]:        152 :     if (getLength() != 0) {
     285 [ +  - ][ +  - ]:          2 :         isc_throw(isc::InvalidParameter,
     286                 :            :                   "compress mode cannot be changed during rendering");
     287                 :            :     }
     288                 :        151 :     impl_->compress_mode_ = mode;
     289                 :        151 : }
     290                 :            : 
     291                 :            : void
     292                 :       3441 : MessageRenderer::writeName(const Name& name, const bool compress) {
     293                 :            :     LabelSequence sequence(name);
     294                 :       6882 :     const size_t nlabels = sequence.getLabelCount();
     295                 :            :     size_t data_len;
     296                 :            :     const char* data;
     297                 :            : 
     298                 :            :     // Find the offset in the offset table whose name gives the longest
     299                 :            :     // match against the name to be rendered.
     300                 :            :     size_t nlabels_uncomp;
     301                 :       3441 :     uint16_t ptr_offset = MessageRendererImpl::NO_OFFSET;
     302                 :            :     const bool case_sensitive = (impl_->compress_mode_ ==
     303                 :       3441 :                                  MessageRenderer::CASE_SENSITIVE);
     304         [ +  - ]:       7235 :     for (nlabels_uncomp = 0; nlabels_uncomp < nlabels; ++nlabels_uncomp) {
     305                 :       7235 :         data = sequence.getData(&data_len);
     306         [ +  + ]:       7235 :         if (data_len == 1) { // trailing dot.
     307                 :       1166 :             ++nlabels_uncomp;
     308                 :       1166 :             break;
     309                 :            :         }
     310                 :            :         // write with range check for safety
     311                 :       6069 :         impl_->seq_hashes_.at(nlabels_uncomp) =
     312                 :       6069 :             sequence.getHash(impl_->compress_mode_);
     313                 :       6069 :         InputBuffer name_buf(data, data_len);
     314                 :            :         ptr_offset = impl_->findOffset(getBuffer(), name_buf,
     315                 :       6069 :                                        impl_->seq_hashes_[nlabels_uncomp],
     316                 :      18207 :                                        case_sensitive);
     317         [ +  + ]:       6069 :         if (ptr_offset != MessageRendererImpl::NO_OFFSET) {
     318                 :            :             break;
     319                 :            :         }
     320                 :       3794 :         sequence.stripLeft(1);
     321                 :            :     }
     322                 :            : 
     323                 :            :     // Record the current offset before updating the offset table
     324                 :       3441 :     size_t offset = getLength();
     325                 :            :     // Write uncompress part:
     326 [ +  + ][ +  + ]:       3441 :     if (nlabels_uncomp > 0 || !compress) {
     327                 :            :         LabelSequence uncomp_sequence(name);
     328 [ +  + ][ +  + ]:       2660 :         if (compress && nlabels > nlabels_uncomp) {
     329                 :            :             // If there's compressed part, strip off that part.
     330                 :       1338 :             uncomp_sequence.stripRight(nlabels - nlabels_uncomp);
     331                 :            :         }
     332                 :       2660 :         data = uncomp_sequence.getData(&data_len);
     333                 :       2660 :         writeData(data, data_len);
     334                 :            :     }
     335                 :            :     // And write compression pointer if available:
     336 [ +  + ][ +  + ]:       3441 :     if (compress && ptr_offset != MessageRendererImpl::NO_OFFSET) {
     337                 :       2119 :         ptr_offset |= Name::COMPRESS_POINTER_MARK16;
     338                 :       2119 :         writeUint16(ptr_offset);
     339                 :            :     }
     340                 :            : 
     341                 :            :     // Finally, record the offset and length for each uncompressed sequence
     342                 :            :     // in the hash table.  The renderer's buffer has just stored the
     343                 :            :     // corresponding data, so we use the rendered data to get the length
     344                 :            :     // of each label of the names.
     345                 :       3441 :     size_t seqlen = name.getLength();
     346         [ +  + ]:       7218 :     for (size_t i = 0; i < nlabels_uncomp; ++i) {
     347                 :       4943 :         const uint8_t label_len = getBuffer()[offset];
     348         [ +  + ]:       4943 :         if (label_len == 0) { // offset for root doesn't need to be stored.
     349                 :            :             break;
     350                 :            :         }
     351         [ +  + ]:       3783 :         if (offset > Name::MAX_COMPRESS_POINTER) {
     352                 :            :             break;
     353                 :            :         }
     354                 :            :         // Store the tuple of <hash, offset, len> to the table.  Note that we
     355                 :            :         // already know the hash value for each name.
     356                 :       7554 :         impl_->addOffset(impl_->seq_hashes_[i], offset, seqlen);
     357                 :       3777 :         offset += (label_len + 1);
     358                 :       3777 :         seqlen -= (label_len + 1);
     359                 :            :     }
     360                 :       3441 : }
     361                 :            : 
     362                 :       1232 : AbstractMessageRenderer::AbstractMessageRenderer() :
     363                 :       2464 :     local_buffer_(0), buffer_(&local_buffer_)
     364                 :            : {
     365                 :       1232 : }
     366                 :            : 
     367                 :            : void
     368                 :        674 : AbstractMessageRenderer::setBuffer(OutputBuffer* buffer) {
     369 [ +  + ][ +  + ]:        674 :     if (buffer != NULL && buffer_->getLength() != 0) {
                 [ +  + ]
     370 [ +  - ][ +  - ]:          4 :         isc_throw(isc::InvalidParameter,
     371                 :            :                   "MessageRenderer buffer cannot be set when in use");
     372 [ +  + ][ +  + ]:        672 :     } if (buffer == NULL && buffer_ == &local_buffer_) {
     373 [ +  - ][ +  - ]:          2 :         isc_throw(isc::InvalidParameter,
     374                 :            :                   "Default MessageRenderer buffer cannot be reset");
     375                 :            :     }
     376                 :            : 
     377         [ +  + ]:        671 :     if (buffer == NULL) {
     378                 :            :         // Reset to the default buffer, then clear other internal resources.
     379                 :            :         // The order is important; we need to keep the used buffer intact.
     380                 :        322 :         buffer_ = &local_buffer_;
     381                 :        322 :         clear();
     382                 :            :     } else {
     383                 :        349 :         buffer_ = buffer;
     384                 :            :     }
     385                 :        671 : }
     386                 :            : 
     387                 :            : void
     388                 :        630 : AbstractMessageRenderer::clear() {
     389                 :        630 :     buffer_->clear();
     390                 :        630 : }
     391                 :            : 
     392                 :            : }
     393                 :     414193 : }

Generated by: LCOV version 1.9