Kea: messagerenderer.cc Source File

| Kea 3.2.0-git

messagerenderer.cc
Go to the documentation of this file.
1// Copyright (C) 2009-2026 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 <exceptions/isc_assert.h>
11#include <dns/name.h>
12#include <dns/name_internal.h>
13#include <dns/labelsequence.h>
14#include <dns/messagerenderer.h>
15#include <util/buffer.h>
16
17#include <boost/array.hpp>
18#include <limits>
19#include <cassert>
20#include <vector>
21
22using namespace isc::util;
23using isc::dns::name::internal::maptolower;
24
25using namespace std;
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 if (llen == 0) {
117 size_t i = 0;
118
119 while ((buffer[pos] & Name::COMPRESS_POINTER_MARK8) ==
120 Name::COMPRESS_POINTER_MARK8) {
121 pos = (buffer[pos] & ~Name::COMPRESS_POINTER_MARK8) *
122 256 + buffer[pos + 1];
123
124 // This loop should stop as long as the buffer has been
125 // constructed validly and the search/insert argument is based
126 // on a valid name, which is an assumption for this class.
127 // But we'll abort if a bug could cause an infinite loop.
128 i += 2;
129 isc_throw_assert(i < Name::MAX_WIRE);
130 }
131 llen = buffer[pos];
132 } else {
133 --llen;
134 }
135 return (pos);
136 }
137
138 const OutputBuffer* buffer_;
139 InputBuffer* name_buf_;
140 const size_t hash_;
141};
142}
143
154 struct MessageRenderer::MessageRendererImpl {
155 // The size of hash buckets and number of hash entries per bucket for
156 // which space is preallocated and kept reserved for subsequent rendering
157 // to provide better performance. These values are derived from the
158 // BIND 9 implementation that uses a similar hash table.
159 static const size_t BUCKETS = 64;
160 static const size_t RESERVED_ITEMS = 16;
161 static const uint16_t NO_OFFSET = 65535; // used as a marker of 'not found'
162
164 MessageRendererImpl() :
165 msglength_limit_(512), truncated_(false),
166 compress_mode_(MessageRenderer::CASE_INSENSITIVE),
167 seq_hashes_() {
168 // Reserve some spaces for hash table items.
169 for (size_t i = 0; i < BUCKETS; ++i) {
170 table_[i].reserve(RESERVED_ITEMS);
171 }
172 }
173
174 uint16_t findOffset(const OutputBuffer& buffer, InputBuffer& name_buf,
175 size_t hash, bool case_sensitive) const {
176 // Find a matching entry, if any. We use some heuristics here: often
177 // the same name appears consecutively (like repeating the same owner
178 // name for a single RRset), so in case there's a collision in the
179 // bucket it will be more likely to find it in the tail side of the
180 // bucket.
181 const size_t bucket_id = hash % BUCKETS;
182 vector<OffsetItem>::const_reverse_iterator found;
183 if (case_sensitive) {
184 found = find_if(table_[bucket_id].rbegin(),
185 table_[bucket_id].rend(),
186 NameCompare<true>(buffer, name_buf, hash));
187 } else {
188 found = find_if(table_[bucket_id].rbegin(),
189 table_[bucket_id].rend(),
190 NameCompare<false>(buffer, name_buf, hash));
191 }
192 if (found != table_[bucket_id].rend()) {
193 return (found->pos_);
194 }
195 return (NO_OFFSET);
196 }
197
198 void addOffset(size_t hash, size_t offset, size_t len) {
199 table_[hash % BUCKETS].push_back(OffsetItem(hash, offset, len));
200 }
201
202 // The hash table for the (offset + position in the buffer) entries
203 vector<OffsetItem> table_[BUCKETS];
206 uint16_t msglength_limit_;
209 bool truncated_;
211 CompressMode compress_mode_;
212
213 // Placeholder for hash values as they are calculated in writeName().
214 // Note: we may want to make it a local variable of writeName() if it
215 // works more efficiently.
216 boost::array<size_t, Name::MAX_LABELS> seq_hashes_;
217};
218
219MessageRenderer::MessageRenderer() :
220 AbstractMessageRenderer(),
221 impl_(new MessageRendererImpl()) {
222}
223
224MessageRenderer::~MessageRenderer() {
225}
226
227void
228MessageRenderer::clear() {
229 AbstractMessageRenderer::clear();
230 impl_->msglength_limit_ = 512;
231 impl_->truncated_ = false;
232 impl_->compress_mode_ = CASE_INSENSITIVE;
233
234 // Clear the hash table. We reserve the minimum space for possible
235 // subsequent use of the renderer.
236 for (size_t i = 0; i < MessageRendererImpl::BUCKETS; ++i) {
237 if (impl_->table_[i].size() > MessageRendererImpl::RESERVED_ITEMS) {
238 // Trim excessive capacity: swap ensures the new capacity is only
239 // reasonably large for the reserved space.
240 vector<OffsetItem> new_table;
241 new_table.reserve(MessageRendererImpl::RESERVED_ITEMS);
242 new_table.swap(impl_->table_[i]);
243 }
244 impl_->table_[i].clear();
245 }
246}
247
248size_t
249MessageRenderer::getLengthLimit() const {
250 return (impl_->msglength_limit_);
251}
252
253void
254MessageRenderer::setLengthLimit(const size_t len) {
255 impl_->msglength_limit_ = len;
256}
257
258bool
259MessageRenderer::isTruncated() const {
260 return (impl_->truncated_);
261}
262
263void
264MessageRenderer::setTruncated() {
265 impl_->truncated_ = true;
266}
267
268 MessageRenderer::CompressMode
269MessageRenderer::getCompressMode() const {
270 return (impl_->compress_mode_);
271}
272
273void
274MessageRenderer::setCompressMode(const CompressMode mode) {
275 if (getLength() != 0) {
276 isc_throw(isc::InvalidParameter,
277 "compress mode cannot be changed during rendering");
278 }
279 impl_->compress_mode_ = mode;
280}
281
282void
283 MessageRenderer::writeName(const LabelSequence& ls, const bool compress) {
284 LabelSequence sequence(ls);
285 const size_t nlabels = sequence.getLabelCount();
286 size_t data_len;
287 const uint8_t* data;
288
289 // Find the offset in the offset table whose name gives the longest
290 // match against the name to be rendered.
291 size_t nlabels_uncomp;
292 uint16_t ptr_offset = MessageRendererImpl::NO_OFFSET;
293 const bool case_sensitive = (impl_->compress_mode_ ==
294 MessageRenderer::CASE_SENSITIVE);
295 for (nlabels_uncomp = 0; nlabels_uncomp < nlabels; ++nlabels_uncomp) {
296 if (nlabels_uncomp > 0) {
297 sequence.stripLeft(1);
298 }
299
300 data = sequence.getData(&data_len);
301 if (data_len == 1) { // trailing dot.
302 ++nlabels_uncomp;
303 break;
304 }
305 // write with range check for safety
306 impl_->seq_hashes_.at(nlabels_uncomp) =
307 sequence.getHash(impl_->compress_mode_);
308 InputBuffer name_buf(data, data_len);
309 ptr_offset = impl_->findOffset(getBuffer(), name_buf,
310 impl_->seq_hashes_[nlabels_uncomp],
311 case_sensitive);
312 if (ptr_offset != MessageRendererImpl::NO_OFFSET) {
313 break;
314 }
315 }
316
317 // Record the current offset before updating the offset table
318 size_t offset = getLength();
319 // Write uncompress part:
320 if (nlabels_uncomp > 0 || !compress) {
321 LabelSequence uncomp_sequence(ls);
322 if (compress && nlabels > nlabels_uncomp) {
323 // If there's compressed part, strip off that part.
324 uncomp_sequence.stripRight(nlabels - nlabels_uncomp);
325 }
326 data = uncomp_sequence.getData(&data_len);
327 writeData(data, data_len);
328 }
329 // And write compression pointer if available:
330 if (compress && ptr_offset != MessageRendererImpl::NO_OFFSET) {
331 ptr_offset |= Name::COMPRESS_POINTER_MARK16;
332 writeUint16(ptr_offset);
333 }
334
335 // Finally, record the offset and length for each uncompressed sequence
336 // in the hash table. The renderer's buffer has just stored the
337 // corresponding data, so we use the rendered data to get the length
338 // of each label of the names.
339 size_t seqlen = ls.getDataLength();
340 for (size_t i = 0; i < nlabels_uncomp; ++i) {
341 const uint8_t label_len = getBuffer()[offset];
342 if (label_len == 0) { // offset for root doesn't need to be stored.
343 break;
344 }
345 if (offset > Name::MAX_COMPRESS_POINTER) {
346 break;
347 }
348 // Store the tuple of <hash, offset, len> to the table. Note that we
349 // already know the hash value for each name.
350 impl_->addOffset(impl_->seq_hashes_[i], offset, seqlen);
351 offset += (label_len + 1);
352 seqlen -= (label_len + 1);
353 }
354}
355
356void
357MessageRenderer::writeName(const Name& name, const bool compress) {
358 const LabelSequence ls(name);
359 writeName(ls, compress);
360}
361
362AbstractMessageRenderer::AbstractMessageRenderer() :
363 local_buffer_(0), buffer_(&local_buffer_) {
364}
365
366void
367AbstractMessageRenderer::setBuffer(OutputBuffer* buffer) {
368 if (buffer && buffer_->getLength() != 0) {
369 isc_throw(isc::InvalidParameter,
370 "MessageRenderer buffer cannot be set when in use");
371 }
372 if (!buffer && buffer_ == &local_buffer_) {
373 isc_throw(isc::InvalidParameter,
374 "Default MessageRenderer buffer cannot be reset");
375 }
376
377 if (!buffer) {
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 buffer_ = &local_buffer_;
381 clear();
382 } else {
383 buffer_ = buffer;
384 }
385}
386
387void
388AbstractMessageRenderer::clear() {
389 buffer_->clear();
390}
391
392}
393}
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::AbstractMessageRenderer
AbstractMessageRenderer()
The default constructor.
Definition messagerenderer.cc:362
isc::dns::AbstractMessageRenderer::getLength
size_t getLength() const
Return the length of data written in the internal buffer.
Definition messagerenderer.h:145
isc::dns::AbstractMessageRenderer::clear
virtual void clear()
Clear the internal buffer and other internal resources.
Definition messagerenderer.cc:388
isc::dns::AbstractMessageRenderer::getBuffer
const isc::util::OutputBuffer & getBuffer() const
Return the output buffer we render into.
Definition messagerenderer.h:114
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:277
isc::dns::AbstractMessageRenderer::CompressMode
CompressMode
Compression mode constants.
Definition messagerenderer.h:94
isc::dns::AbstractMessageRenderer::setBuffer
void setBuffer(isc::util::OutputBuffer *buffer)
Set or reset a temporary output buffer.
Definition messagerenderer.cc:367
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:264
isc::dns::LabelSequence::getLabelCount
size_t getLabelCount() const
Returns the current number of labels for this LabelSequence.
Definition labelsequence.h:308
isc::dns::LabelSequence::stripLeft
void stripLeft(size_t i)
Remove labels from the front of this LabelSequence.
Definition labelsequence.cc:241
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:250
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::CASE_SENSITIVE
@ CASE_SENSITIVE
Compress names case-sensitive manner.
Definition messagerenderer.h:96
isc::dns::MessageRenderer::getCompressMode
virtual CompressMode getCompressMode() const
Return the compression mode of the renderer class object.
Definition messagerenderer.cc:269
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:254
isc::dns::MessageRenderer::clear
virtual void clear()
Clear the internal buffer and other internal resources.
Definition messagerenderer.cc:228
isc::dns::MessageRenderer::~MessageRenderer
virtual ~MessageRenderer()
Definition messagerenderer.cc:224
isc::dns::MessageRenderer::MessageRenderer
MessageRenderer()
Definition messagerenderer.cc:219
isc::dns::MessageRenderer::setTruncated
virtual void setTruncated()
Mark the renderer to indicate truncation has occurred while rendering.
Definition messagerenderer.cc:264
isc::dns::MessageRenderer::isTruncated
virtual bool isTruncated() const
Return whether truncation has occurred while rendering.
Definition messagerenderer.cc:259
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:357
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:249
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:274
isc::dns::MessageRenderer::CASE_INSENSITIVE
@ CASE_INSENSITIVE
Compress names case-insensitive manner (default).
Definition messagerenderer.h:95
isc::dns::Name
The Name class encapsulates DNS names.
Definition name.h:219
isc::dns::Name::MAX_COMPRESS_POINTER
static const uint16_t MAX_COMPRESS_POINTER
Max possible pointer value for name compression.
Definition name.h:711
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:715
isc::dns::Name::MAX_WIRE
static const size_t MAX_WIRE
Max allowable length of domain names.
Definition name.h:695
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:713
isc::util::InputBuffer
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition buffer.h:81
isc::util::OutputBuffer
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:346
isc::util::OutputBuffer::getLength
size_t getLength() const
Return the length of data written in the buffer.
Definition buffer.h:412
vector::const_reverse_iterator
STL iterator class.
exceptions.h
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
isc_assert.h
isc_throw_assert
#define isc_throw_assert(expr)
Replacement for assert() that throws if the expression is false.
Definition isc_assert.h:18
labelsequence.h
maptolower
const uint8_t maptolower[]
Definition name.cc:71
messagerenderer.h
name.h
name_internal.h
isc::data
Definition base_stamped_element.cc:12
isc::dns::name::internal::maptolower
const uint8_t maptolower[]
Definition name.cc:71
isc::dns::name
Definition name.cc:69
isc::dns
Definition char_string.cc:26
isc::util
Definition time_utils.cc:43
isc
Defines the logger used by the top-level component of kea-lfc.
Definition check_exists_add.cc:18
isc::dns::MessageRenderer::MessageRendererImpl
The MessageRendererImpl class is the actual implementation of MessageRenderer.
Definition messagerenderer.cc:154
isc::dns::MessageRenderer::MessageRendererImpl::seq_hashes_
boost::array< size_t, Name::MAX_LABELS > seq_hashes_
Definition messagerenderer.cc:216
isc::dns::MessageRenderer::MessageRendererImpl::addOffset
void addOffset(size_t hash, size_t offset, size_t len)
Definition messagerenderer.cc:198
isc::dns::MessageRenderer::MessageRendererImpl::RESERVED_ITEMS
static const size_t RESERVED_ITEMS
Definition messagerenderer.cc:160
isc::dns::MessageRenderer::MessageRendererImpl::BUCKETS
static const size_t BUCKETS
Definition messagerenderer.cc:159
isc::dns::MessageRenderer::MessageRendererImpl::findOffset
uint16_t findOffset(const OutputBuffer &buffer, InputBuffer &name_buf, size_t hash, bool case_sensitive) const
Definition messagerenderer.cc:174
isc::dns::MessageRenderer::MessageRendererImpl::NO_OFFSET
static const uint16_t NO_OFFSET
Definition messagerenderer.cc:161
isc::dns::MessageRenderer::MessageRendererImpl::table_
vector< OffsetItem > table_[BUCKETS]
Definition messagerenderer.cc:203
isc::dns::MessageRenderer::MessageRendererImpl::compress_mode_
CompressMode compress_mode_
The name compression mode.
Definition messagerenderer.cc:211
isc::dns::MessageRenderer::MessageRendererImpl::truncated_
bool truncated_
A boolean flag that indicates truncation has occurred while rendering the data.
Definition messagerenderer.cc:209
isc::dns::MessageRenderer::MessageRendererImpl::MessageRendererImpl
MessageRendererImpl()
Constructor.
Definition messagerenderer.cc:164
isc::dns::MessageRenderer::MessageRendererImpl::msglength_limit_
uint16_t msglength_limit_
The maximum length of rendered data that can fit without truncation.
Definition messagerenderer.cc:206