messagerenderer.cc 14 KB

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