messagerenderer.cc 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. #include <exceptions/exceptions.h>
  15. #include <util/buffer.h>
  16. #include <dns/name.h>
  17. #include <dns/labelsequence.h>
  18. #include <dns/messagerenderer.h>
  19. #include <cassert>
  20. #include <vector>
  21. using namespace std;
  22. using namespace isc::util;
  23. namespace isc {
  24. namespace dns {
  25. ///
  26. /// \brief The \c MessageRendererImpl class is the actual implementation of
  27. /// \c MessageRenderer.
  28. ///
  29. /// The implementation is hidden from applications. We can refer to specific
  30. /// members of this class only within the implementation source file.
  31. ///
  32. /// It internally holds a hash table for LabelSequence objects corresponding
  33. /// to portions of names rendered in this renderer with their offset from
  34. /// the beginning to the entire rendered data. It's used to handle name
  35. /// compression.
  36. struct MessageRenderer::MessageRendererImpl {
  37. // The size of hash buckets
  38. static const size_t BUCKETS = 64;
  39. // Number of hash entries per bucket for which space is preallocated and
  40. // keep reserved for subsequent rendering, inneding to provide better
  41. // performance.
  42. static const size_t RESERVED_ITEMS = 16;
  43. static const uint16_t NO_OFFSET = 65535; // used as a marker of 'not found'
  44. // Structure used as hash entries
  45. struct OffsetItem {
  46. OffsetItem(const LabelSequence& labels_param, uint16_t offset_param) :
  47. labels(labels_param), offset(offset_param)
  48. {}
  49. LabelSequence labels;
  50. uint16_t offset;
  51. };
  52. MessageRendererImpl() :
  53. msglength_limit_(512), truncated_(false),
  54. compress_mode_(MessageRenderer::CASE_INSENSITIVE)
  55. {
  56. // Reserve some spaces for hash and name placeholders.
  57. for (size_t i = 0; i < BUCKETS; ++i) {
  58. table_[i].reserve(RESERVED_ITEMS);
  59. }
  60. names_.reserve(BUCKETS);
  61. }
  62. // A helper structure to find the hash entry whose labelsequence is
  63. // equal to the search key ("target").
  64. struct SequenceComp {
  65. SequenceComp(const LabelSequence& target, bool case_sensitive) :
  66. target_(target), case_sensitive_(case_sensitive)
  67. {}
  68. bool operator()(const OffsetItem& item) const {
  69. return (item.labels.equals(target_, case_sensitive_));
  70. }
  71. private:
  72. const LabelSequence& target_;
  73. bool case_sensitive_;
  74. };
  75. uint16_t findOffset(const LabelSequence& sequence) const {
  76. const bool case_sensitive = (compress_mode_ ==
  77. MessageRenderer::CASE_SENSITIVE);
  78. const size_t bucket = (sequence.getHash(case_sensitive) % BUCKETS);
  79. // Find a matching entry, if any. We use some heuristics here: often
  80. // the same name appers consecutively (like repeating the same owner
  81. // name for a single RRset), so in case there's a collision in the
  82. // bucket it will be more likely to find it in the tail side of the
  83. // bucket.
  84. vector<OffsetItem>::const_reverse_iterator found =
  85. find_if(table_[bucket].rbegin(), table_[bucket].rend(),
  86. SequenceComp(sequence, case_sensitive));
  87. if (found != table_[bucket].rend()) {
  88. return (found->offset);
  89. }
  90. return (NO_OFFSET);
  91. }
  92. void addOffset(const LabelSequence& sequence, uint16_t offset) {
  93. const bool case_sensitive = (compress_mode_ ==
  94. MessageRenderer::CASE_SENSITIVE);
  95. const size_t bucket = (sequence.getHash(case_sensitive) % BUCKETS);
  96. table_[bucket].push_back(OffsetItem(sequence, offset));
  97. }
  98. /// The maximum length of rendered data that can fit without
  99. /// truncation.
  100. uint16_t msglength_limit_;
  101. /// A boolean flag that indicates truncation has occurred while rendering
  102. /// the data.
  103. bool truncated_;
  104. /// The name compression mode.
  105. CompressMode compress_mode_;
  106. // The hash table for the (LabelSequence * offset) entries
  107. vector<OffsetItem> table_[BUCKETS];
  108. // Placeholder for names referenced from the stored LabelSequences
  109. vector<Name> names_;
  110. };
  111. MessageRenderer::MessageRenderer() :
  112. AbstractMessageRenderer(),
  113. impl_(new MessageRendererImpl)
  114. {}
  115. MessageRenderer::~MessageRenderer() {
  116. delete impl_;
  117. }
  118. void
  119. MessageRenderer::clear() {
  120. AbstractMessageRenderer::clear();
  121. impl_->msglength_limit_ = 512;
  122. impl_->truncated_ = false;
  123. impl_->compress_mode_ = CASE_INSENSITIVE;
  124. // Clear the hash table and name placeholders. We reserve the minimum
  125. // space for possible subsequent use of the renderer.
  126. for (size_t i = 0; i < MessageRendererImpl::BUCKETS; ++i) {
  127. if (impl_->table_[i].size() > MessageRendererImpl::RESERVED_ITEMS) {
  128. impl_->table_[i].reserve(MessageRendererImpl::RESERVED_ITEMS);
  129. vector<MessageRendererImpl::OffsetItem>(impl_->table_[i].begin(),
  130. impl_->table_[i].end()).
  131. swap(impl_->table_[i]);
  132. }
  133. impl_->table_[i].clear();
  134. }
  135. if (impl_->names_.size() > MessageRendererImpl::BUCKETS) {
  136. impl_->names_.reserve(MessageRendererImpl::BUCKETS);
  137. vector<Name>(impl_->names_.begin(), impl_->names_.end()).
  138. swap(impl_->names_);
  139. }
  140. impl_->names_.clear();
  141. }
  142. size_t
  143. MessageRenderer::getLengthLimit() const {
  144. return (impl_->msglength_limit_);
  145. }
  146. void
  147. MessageRenderer::setLengthLimit(const size_t len) {
  148. impl_->msglength_limit_ = len;
  149. }
  150. bool
  151. MessageRenderer::isTruncated() const {
  152. return (impl_->truncated_);
  153. }
  154. void
  155. MessageRenderer::setTruncated() {
  156. impl_->truncated_ = true;
  157. }
  158. MessageRenderer::CompressMode
  159. MessageRenderer::getCompressMode() const {
  160. return (impl_->compress_mode_);
  161. }
  162. void
  163. MessageRenderer::setCompressMode(const CompressMode mode) {
  164. impl_->compress_mode_ = mode;
  165. }
  166. void
  167. MessageRenderer::writeName(const Name& name, const bool compress) {
  168. LabelSequence sequence(name);
  169. const size_t nlabels = sequence.getLabelCount();
  170. size_t data_len;
  171. const char* data;
  172. // Find the offset in the offset table whose name gives the longest
  173. // match against the name to be rendered.
  174. size_t nlabels_uncomp;
  175. uint16_t ptr_offset = MessageRendererImpl::NO_OFFSET;
  176. for (nlabels_uncomp = 0; nlabels_uncomp < nlabels; ++nlabels_uncomp) {
  177. if (sequence.getDataLength() == 1) { // trailing dot.
  178. ++nlabels_uncomp;
  179. break;
  180. }
  181. ptr_offset = impl_->findOffset(sequence);
  182. if (ptr_offset != MessageRendererImpl::NO_OFFSET) {
  183. break;
  184. }
  185. sequence.stripLeft(1);
  186. }
  187. // Record the current offset before updating the offset table
  188. size_t offset = getLength();
  189. // Write uncompress part:
  190. if (nlabels_uncomp > 0 || !compress) {
  191. LabelSequence uncomp_sequence(name);
  192. if (compress && nlabels > nlabels_uncomp) {
  193. // If there's compressed part, strip off that part.
  194. uncomp_sequence.stripRight(nlabels - nlabels_uncomp);
  195. }
  196. data = uncomp_sequence.getData(&data_len);
  197. writeData(data, data_len);
  198. }
  199. // And write compression pointer if available:
  200. if (compress && ptr_offset != MessageRendererImpl::NO_OFFSET) {
  201. ptr_offset |= Name::COMPRESS_POINTER_MARK16;
  202. writeUint16(ptr_offset);
  203. }
  204. // Finally, add the newly rendered name and its ancestors that
  205. // have not been in the set. We need to make our copy of name and generate
  206. // sequence(s) from the copied name because it's not guaranteed that
  207. // the caller keeps the name valid after this call.
  208. if (nlabels_uncomp > 0) {
  209. impl_->names_.push_back(name);
  210. LabelSequence saved_sequence(impl_->names_.back());
  211. size_t seqlen = saved_sequence.getDataLength();
  212. while (nlabels_uncomp-- > 0) {
  213. if (seqlen == 1) { // root name doesn't need to be stored.
  214. break;
  215. }
  216. if (offset > Name::MAX_COMPRESS_POINTER) {
  217. break;
  218. }
  219. impl_->addOffset(saved_sequence, offset);
  220. saved_sequence.stripLeft(1);
  221. const size_t new_seqlen = saved_sequence.getDataLength();
  222. offset += (seqlen - new_seqlen);
  223. seqlen = new_seqlen;
  224. }
  225. }
  226. }
  227. AbstractMessageRenderer::AbstractMessageRenderer() :
  228. local_buffer_(0), buffer_(&local_buffer_)
  229. {
  230. }
  231. void
  232. AbstractMessageRenderer::setBuffer(OutputBuffer* buffer) {
  233. if (buffer != NULL && buffer_->getLength() != 0) {
  234. isc_throw(isc::InvalidParameter,
  235. "MessageRenderer buffer cannot be set when in use");
  236. } if (buffer == NULL && buffer_ == &local_buffer_) {
  237. isc_throw(isc::InvalidParameter,
  238. "Default MessageRenderer buffer cannot be reset");
  239. }
  240. if (buffer == NULL) {
  241. // Reset to the default buffer, then clear other internal resources.
  242. // The order is important; we need to keep the used buffer intact.
  243. buffer_ = &local_buffer_;
  244. clear();
  245. } else {
  246. buffer_ = buffer;
  247. }
  248. }
  249. void
  250. AbstractMessageRenderer::clear() {
  251. buffer_->clear();
  252. }
  253. }
  254. }