Kea: messagerenderer.cc Source File

| Kea 1.5.0

messagerenderer.cc
Go to the documentation of this file.
1// Copyright (C) 2009-2015 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <exceptions/exceptions.h>
10#include <util/buffer.h>
11#include <dns/name.h>
12#include <dns/name_internal.h>
13#include <dns/labelsequence.h>
14#include <dns/messagerenderer.h>
15
16#include <boost/array.hpp>
17#include <boost/static_assert.hpp>
18
19#include <limits>
20#include <cassert>
21#include <vector>
22
23using namespace std;
24using namespace isc::util;
25using isc::dns::name::internal::maptolower;
26
27namespace isc {
28namespace dns {
29
30namespace { // hide internal-only names from the public namespaces
39struct OffsetItem {
40 OffsetItem(size_t hash, size_t pos, size_t len) :
41 hash_(hash), pos_(pos), len_(len)
42 {}
43
46 size_t hash_;
47
50 uint16_t pos_;
51
53 uint16_t len_;
54};
55
64template <bool CASE_SENSITIVE>
65struct NameCompare {
72 NameCompare(const OutputBuffer& buffer, InputBuffer& name_buf,
73 size_t hash) :
74 buffer_(&buffer), name_buf_(&name_buf), hash_(hash)
75 {}
76
77 bool operator()(const OffsetItem& item) const {
78 // Trivial inequality check. If either the hash or the total length
79 // doesn't match, the names are obviously different.
80 if (item.hash_ != hash_ || item.len_ != name_buf_->getLength()) {
81 return (false);
82 }
83
84 // Compare the name data, character-by-character.
85 // item_pos keeps track of the position in the buffer corresponding to
86 // the character to compare. item_label_len is the number of
87 // characters in the labels where the character pointed by item_pos
88 // belongs. When it reaches zero, nextPosition() identifies the
89 // position for the subsequent label, taking into account name
90 // compression, and resets item_label_len to the length of the new
91 // label.
92 name_buf_->setPosition(0); // buffer can be reused, so reset position
93 uint16_t item_pos = item.pos_;
94 uint16_t item_label_len = 0;
95 for (size_t i = 0; i < item.len_; ++i, ++item_pos) {
96 item_pos = nextPosition(*buffer_, item_pos, item_label_len);
97 const uint8_t ch1 = (*buffer_)[item_pos];
98 const uint8_t ch2 = name_buf_->readUint8();
99 if (CASE_SENSITIVE) {
100 if (ch1 != ch2) {
101 return (false);
102 }
103 } else {
104 if (maptolower[ch1] != maptolower[ch2]) {
105 return (false);
106 }
107 }
108 }
109
110 return (true);
111 }
112
113private:
114 static uint16_t nextPosition(const OutputBuffer& buffer,
115 uint16_t pos, uint16_t& llen)
116 {
117 if (llen == 0) {
118 size_t i = 0;
119
120 while ((buffer[pos] & Name::COMPRESS_POINTER_MARK8) ==
121 Name::COMPRESS_POINTER_MARK8) {
122 pos = (buffer[pos] & ~Name::COMPRESS_POINTER_MARK8) *
123 256 + buffer[pos + 1];
124
125 // This loop should stop as long as the buffer has been
126 // constructed validly and the search/insert argument is based
127 // on a valid name, which is an assumption for this class.
128 // But we'll abort if a bug could cause an infinite loop.
129 i += 2;
130 assert(i < Name::MAX_WIRE);
131 }
132 llen = buffer[pos];
133 } else {
134 --llen;
135 }
136 return (pos);
137 }
138
139 const OutputBuffer* buffer_;
140 InputBuffer* name_buf_;
141 const size_t hash_;
142};
143}
144
155 struct MessageRenderer::MessageRendererImpl {
156 // The size of hash buckets and number of hash entries per bucket for
157 // which space is preallocated and kept reserved for subsequent rendering
158 // to provide better performance. These values are derived from the
159 // BIND 9 implementation that uses a similar hash table.
160 static const size_t BUCKETS = 64;
161 static const size_t RESERVED_ITEMS = 16;
162 static const uint16_t NO_OFFSET = 65535; // used as a marker of 'not found'
163
165 MessageRendererImpl() :
166 msglength_limit_(512), truncated_(false),
167 compress_mode_(MessageRenderer::CASE_INSENSITIVE)
168 {
169 // Reserve some spaces for hash table items.
170 for (size_t i = 0; i < BUCKETS; ++i) {
171 table_[i].reserve(RESERVED_ITEMS);
172 }
173 }
174
175 uint16_t findOffset(const OutputBuffer& buffer, InputBuffer& name_buf,
176 size_t hash, bool case_sensitive) const
177 {
178 // Find a matching entry, if any. We use some heuristics here: often
179 // the same name appears consecutively (like repeating the same owner
180 // name for a single RRset), so in case there's a collision in the
181 // bucket it will be more likely to find it in the tail side of the
182 // bucket.
183 const size_t bucket_id = hash % BUCKETS;
184 vector<OffsetItem>::const_reverse_iterator found;
185 if (case_sensitive) {
186 found = find_if(table_[bucket_id].rbegin(),
187 table_[bucket_id].rend(),
188 NameCompare<true>(buffer, name_buf, hash));
189 } else {
190 found = find_if(table_[bucket_id].rbegin(),
191 table_[bucket_id].rend(),
192 NameCompare<false>(buffer, name_buf, hash));
193 }
194 if (found != table_[bucket_id].rend()) {
195 return (found->pos_);
196 }
197 return (NO_OFFSET);
198 }
199
200 void addOffset(size_t hash, size_t offset, size_t len) {
201 table_[hash % BUCKETS].push_back(OffsetItem(hash, offset, len));
202 }
203
204 // The hash table for the (offset + position in the buffer) entries
205 vector<OffsetItem> table_[BUCKETS];
208 uint16_t msglength_limit_;
211 bool truncated_;
213 CompressMode compress_mode_;
214
215 // Placeholder for hash values as they are calculated in writeName().
216 // Note: we may want to make it a local variable of writeName() if it
217 // works more efficiently.
218 boost::array<size_t, Name::MAX_LABELS> seq_hashes_;
219};
220
221MessageRenderer::MessageRenderer() :
222 AbstractMessageRenderer(),
223 impl_(new MessageRendererImpl)
224{}
225
226MessageRenderer::~MessageRenderer() {
227 delete impl_;
228}
229
230void
231MessageRenderer::clear() {
232 AbstractMessageRenderer::clear();
233 impl_->msglength_limit_ = 512;
234 impl_->truncated_ = false;
235 impl_->compress_mode_ = CASE_INSENSITIVE;
236
237 // Clear the hash table. We reserve the minimum space for possible
238 // subsequent use of the renderer.
239 for (size_t i = 0; i < MessageRendererImpl::BUCKETS; ++i) {
240 if (impl_->table_[i].size() > MessageRendererImpl::RESERVED_ITEMS) {
241 // Trim excessive capacity: swap ensures the new capacity is only
242 // reasonably large for the reserved space.
243 vector<OffsetItem> new_table;
244 new_table.reserve(MessageRendererImpl::RESERVED_ITEMS);
245 new_table.swap(impl_->table_[i]);
246 }
247 impl_->table_[i].clear();
248 }
249}
250
251size_t
252MessageRenderer::getLengthLimit() const {
253 return (impl_->msglength_limit_);
254}
255
256void
257MessageRenderer::setLengthLimit(const size_t len) {
258 impl_->msglength_limit_ = len;
259}
260
261bool
262MessageRenderer::isTruncated() const {
263 return (impl_->truncated_);
264}
265
266void
267MessageRenderer::setTruncated() {
268 impl_->truncated_ = true;
269}
270
271 MessageRenderer::CompressMode
272MessageRenderer::getCompressMode() const {
273 return (impl_->compress_mode_);
274}
275
276void
277MessageRenderer::setCompressMode(const CompressMode mode) {
278 if (getLength() != 0) {
279 isc_throw(isc::InvalidParameter,
280 "compress mode cannot be changed during rendering");
281 }
282 impl_->compress_mode_ = mode;
283}
284
285void
286 MessageRenderer::writeName(const LabelSequence& ls, const bool compress) {
287 LabelSequence sequence(ls);
288 const size_t nlabels = sequence.getLabelCount();
289 size_t data_len;
290 const uint8_t* data;
291
292 // Find the offset in the offset table whose name gives the longest
293 // match against the name to be rendered.
294 size_t nlabels_uncomp;
295 uint16_t ptr_offset = MessageRendererImpl::NO_OFFSET;
296 const bool case_sensitive = (impl_->compress_mode_ ==
297 MessageRenderer::CASE_SENSITIVE);
298 for (nlabels_uncomp = 0; nlabels_uncomp < nlabels; ++nlabels_uncomp) {
299 if (nlabels_uncomp > 0) {
300 sequence.stripLeft(1);
301 }
302
303 data = sequence.getData(&data_len);
304 if (data_len == 1) { // trailing dot.
305 ++nlabels_uncomp;
306 break;
307 }
308 // write with range check for safety
309 impl_->seq_hashes_.at(nlabels_uncomp) =
310 sequence.getHash(impl_->compress_mode_);
311 InputBuffer name_buf(data, data_len);
312 ptr_offset = impl_->findOffset(getBuffer(), name_buf,
313 impl_->seq_hashes_[nlabels_uncomp],
314 case_sensitive);
315 if (ptr_offset != MessageRendererImpl::NO_OFFSET) {
316 break;
317 }
318 }
319
320 // Record the current offset before updating the offset table
321 size_t offset = getLength();
322 // Write uncompress part:
323 if (nlabels_uncomp > 0 || !compress) {
324 LabelSequence uncomp_sequence(ls);
325 if (compress && nlabels > nlabels_uncomp) {
326 // If there's compressed part, strip off that part.
327 uncomp_sequence.stripRight(nlabels - nlabels_uncomp);
328 }
329 data = uncomp_sequence.getData(&data_len);
330 writeData(data, data_len);
331 }
332 // And write compression pointer if available:
333 if (compress && ptr_offset != MessageRendererImpl::NO_OFFSET) {
334 ptr_offset |= Name::COMPRESS_POINTER_MARK16;
335 writeUint16(ptr_offset);
336 }
337
338 // Finally, record the offset and length for each uncompressed sequence
339 // in the hash table. The renderer's buffer has just stored the
340 // corresponding data, so we use the rendered data to get the length
341 // of each label of the names.
342 size_t seqlen = ls.getDataLength();
343 for (size_t i = 0; i < nlabels_uncomp; ++i) {
344 const uint8_t label_len = getBuffer()[offset];
345 if (label_len == 0) { // offset for root doesn't need to be stored.
346 break;
347 }
348 if (offset > Name::MAX_COMPRESS_POINTER) {
349 break;
350 }
351 // Store the tuple of <hash, offset, len> to the table. Note that we
352 // already know the hash value for each name.
353 impl_->addOffset(impl_->seq_hashes_[i], offset, seqlen);
354 offset += (label_len + 1);
355 seqlen -= (label_len + 1);
356 }
357}
358
359void
360MessageRenderer::writeName(const Name& name, const bool compress) {
361 const LabelSequence ls(name);
362 writeName(ls, compress);
363}
364
365AbstractMessageRenderer::AbstractMessageRenderer() :
366 local_buffer_(0), buffer_(&local_buffer_)
367{
368}
369
370void
371AbstractMessageRenderer::setBuffer(OutputBuffer* buffer) {
372 if (buffer != NULL && buffer_->getLength() != 0) {
373 isc_throw(isc::InvalidParameter,
374 "MessageRenderer buffer cannot be set when in use");
375 }
376 if (buffer == NULL && buffer_ == &local_buffer_) {
377 isc_throw(isc::InvalidParameter,
378 "Default MessageRenderer buffer cannot be reset");
379 }
380
381 if (buffer == NULL) {
382 // Reset to the default buffer, then clear other internal resources.
383 // The order is important; we need to keep the used buffer intact.
384 buffer_ = &local_buffer_;
385 clear();
386 } else {
387 buffer_ = buffer;
388 }
389}
390
391void
392AbstractMessageRenderer::clear() {
393 buffer_->clear();
394}
395
396}
397}
buffer.h
isc::InvalidParameter
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
Definition: exceptions/exceptions.h:124
isc::dns::AbstractMessageRenderer
The AbstractMessageRenderer class is an abstract base class that provides common interfaces for rende...
Definition: messagerenderer.h:68
isc::dns::AbstractMessageRenderer::AbstractMessageRenderer
AbstractMessageRenderer()
The default constructor.
Definition: messagerenderer.cc:365
isc::dns::AbstractMessageRenderer::getLength
size_t getLength() const
Return the length of data written in the internal buffer.
Definition: messagerenderer.h:144
isc::dns::AbstractMessageRenderer::clear
virtual void clear()
Clear the internal buffer and other internal resources.
Definition: messagerenderer.cc:392
isc::dns::AbstractMessageRenderer::getBuffer
const isc::util::OutputBuffer & getBuffer() const
Return the output buffer we render into.
Definition: messagerenderer.h:113
isc::dns::AbstractMessageRenderer::writeUint16
void writeUint16(uint16_t data)
Write an unsigned 16-bit integer in host byte order into the internal buffer in network byte order.
Definition: messagerenderer.h:276
isc::dns::AbstractMessageRenderer::CompressMode
CompressMode
Compression mode constants.
Definition: messagerenderer.h:93
isc::dns::AbstractMessageRenderer::setBuffer
void setBuffer(isc::util::OutputBuffer *buffer)
Set or reset a temporary output buffer.
Definition: messagerenderer.cc:371
isc::dns::AbstractMessageRenderer::writeData
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the internal buffer of the renderer object.
Definition: messagerenderer.h:310
isc::dns::LabelSequence
Light-weight Accessor to Name data.
Definition: labelsequence.h:35
isc::dns::LabelSequence::getHash
size_t getHash(bool case_sensitive) const
Calculate a simple hash for the label sequence.
Definition: labelsequence.cc:265
isc::dns::LabelSequence::getLabelCount
size_t getLabelCount() const
Returns the current number of labels for this LabelSequence.
Definition: labelsequence.h:288
isc::dns::LabelSequence::stripLeft
void stripLeft(size_t i)
Remove labels from the front of this LabelSequence.
Definition: labelsequence.cc:242
isc::dns::LabelSequence::getData
const uint8_t * getData(size_t *len) const
Return the wire-format data for this LabelSequence.
Definition: labelsequence.cc:82
isc::dns::LabelSequence::stripRight
void stripRight(size_t i)
Remove labels from the end of this LabelSequence.
Definition: labelsequence.cc:251
isc::dns::LabelSequence::getDataLength
size_t getDataLength() const
Return the length of the wire-format data of this LabelSequence.
Definition: labelsequence.cc:88
isc::dns::MessageRenderer
The MessageRenderer is a concrete derived class of AbstractMessageRenderer as a general purpose imple...
Definition: messagerenderer.h:357
isc::dns::MessageRenderer::CASE_SENSITIVE
@ CASE_SENSITIVE
Compress names case-sensitive manner.
Definition: messagerenderer.h:95
isc::dns::MessageRenderer::getCompressMode
virtual CompressMode getCompressMode() const
Return the compression mode of the renderer class object.
Definition: messagerenderer.cc:272
isc::dns::MessageRenderer::setLengthLimit
virtual void setLengthLimit(size_t len)
Set the maximum length of rendered data that can fit in the corresponding DNS message without truncat...
Definition: messagerenderer.cc:257
isc::dns::MessageRenderer::clear
virtual void clear()
Clear the internal buffer and other internal resources.
Definition: messagerenderer.cc:231
isc::dns::MessageRenderer::~MessageRenderer
virtual ~MessageRenderer()
Definition: messagerenderer.cc:226
isc::dns::MessageRenderer::MessageRenderer
MessageRenderer()
Definition: messagerenderer.cc:221
isc::dns::MessageRenderer::setTruncated
virtual void setTruncated()
Mark the renderer to indicate truncation has occurred while rendering.
Definition: messagerenderer.cc:267
isc::dns::MessageRenderer::isTruncated
virtual bool isTruncated() const
Return whether truncation has occurred while rendering.
Definition: messagerenderer.cc:262
isc::dns::MessageRenderer::writeName
virtual void writeName(const Name &name, bool compress=true)
Write a Name object into the internal buffer in wire format, with or without name compression.
Definition: messagerenderer.cc:360
isc::dns::MessageRenderer::getLengthLimit
virtual size_t getLengthLimit() const
Return the maximum length of rendered data that can fit in the corresponding DNS message without trun...
Definition: messagerenderer.cc:252
isc::dns::MessageRenderer::setCompressMode
virtual void setCompressMode(CompressMode mode)
This implementation does not allow this call in the middle of rendering (i.e.
Definition: messagerenderer.cc:277
isc::dns::MessageRenderer::CASE_INSENSITIVE
@ CASE_INSENSITIVE
Compress names case-insensitive manner (default)
Definition: messagerenderer.h:94
isc::dns::Name
The Name class encapsulates DNS names.
Definition: name.h:223
isc::dns::Name::MAX_COMPRESS_POINTER
static const uint16_t MAX_COMPRESS_POINTER
Max possible pointer value for name compression.
Definition: name.h:715
isc::dns::Name::COMPRESS_POINTER_MARK16
static const uint16_t COMPRESS_POINTER_MARK16
A 16-bit masked value indicating a start of compression pointer.
Definition: name.h:719
isc::dns::Name::MAX_WIRE
static const size_t MAX_WIRE
Max allowable length of domain names.
Definition: name.h:699
isc::dns::Name::COMPRESS_POINTER_MARK8
static const uint16_t COMPRESS_POINTER_MARK8
A 8-bit masked value indicating a start of compression pointer.
Definition: name.h:717
isc::util::InputBuffer
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
isc::util::InputBuffer::setPosition
void setPosition(size_t position)
Set the read position of the buffer to the given value.
Definition: buffer.h:115
isc::util::InputBuffer::readUint8
uint8_t readUint8()
Read an unsigned 8-bit integer from the buffer and return it.
Definition: buffer.h:130
isc::util::InputBuffer::getLength
size_t getLength() const
Return the length of the data stored in the buffer.
Definition: buffer.h:100
isc::util::OutputBuffer
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
isc::util::OutputBuffer::getLength
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
isc::util::OutputBuffer::clear
void clear()
Clear buffer content.
Definition: buffer.h:448
exceptions.h
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
labelsequence.h
hash_
size_t hash_
The hash value for the stored name calculated by LabelSequence.getHash.
Definition: messagerenderer.cc:46
len_
uint16_t len_
The length of the corresponding sequence (which is a domain name).
Definition: messagerenderer.cc:53
pos_
uint16_t pos_
The position (offset from the beginning) in the buffer where the name starts.
Definition: messagerenderer.cc:50
messagerenderer.h
name.h
name_internal.h
isc::dns::name::internal::maptolower
const uint8_t maptolower[]
Definition: name.cc:73
isc::util
Definition: edns.h:19
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::dns::MessageRenderer::MessageRendererImpl
The MessageRendererImpl class is the actual implementation of MessageRenderer.
Definition: messagerenderer.cc:155
isc::dns::MessageRenderer::MessageRendererImpl::seq_hashes_
boost::array< size_t, Name::MAX_LABELS > seq_hashes_
Definition: messagerenderer.cc:218
isc::dns::MessageRenderer::MessageRendererImpl::addOffset
void addOffset(size_t hash, size_t offset, size_t len)
Definition: messagerenderer.cc:200
isc::dns::MessageRenderer::MessageRendererImpl::RESERVED_ITEMS
static const size_t RESERVED_ITEMS
Definition: messagerenderer.cc:161
isc::dns::MessageRenderer::MessageRendererImpl::BUCKETS
static const size_t BUCKETS
Definition: messagerenderer.cc:160
isc::dns::MessageRenderer::MessageRendererImpl::findOffset
uint16_t findOffset(const OutputBuffer &buffer, InputBuffer &name_buf, size_t hash, bool case_sensitive) const
Definition: messagerenderer.cc:175
isc::dns::MessageRenderer::MessageRendererImpl::NO_OFFSET
static const uint16_t NO_OFFSET
Definition: messagerenderer.cc:162
isc::dns::MessageRenderer::MessageRendererImpl::table_
vector< OffsetItem > table_[BUCKETS]
Definition: messagerenderer.cc:205
isc::dns::MessageRenderer::MessageRendererImpl::compress_mode_
CompressMode compress_mode_
The name compression mode.
Definition: messagerenderer.cc:213
isc::dns::MessageRenderer::MessageRendererImpl::truncated_
bool truncated_
A boolean flag that indicates truncation has occurred while rendering the data.
Definition: messagerenderer.cc:211
isc::dns::MessageRenderer::MessageRendererImpl::MessageRendererImpl
MessageRendererImpl()
Constructor.
Definition: messagerenderer.cc:165
isc::dns::MessageRenderer::MessageRendererImpl::msglength_limit_
uint16_t msglength_limit_
The maximum length of rendered data that can fit without truncation.
Definition: messagerenderer.cc:208