Kea: rdata.cc Source File

| Kea 1.5.0

rdata.cc
Go to the documentation of this file.
1 // Copyright (C) 2010-2016 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
11 #include <util/buffer.h>
12 #include <util/encode/hex.h>
13
14 #include <dns/name.h>
15 #include <dns/messagerenderer.h>
16 #include <dns/master_lexer.h>
17 #include <dns/rdata.h>
18 #include <dns/rrparamregistry.h>
19 #include <dns/rrtype.h>
20
21 #include <boost/lexical_cast.hpp>
22 #include <boost/shared_ptr.hpp>
23
24 #include <algorithm>
25 #include <cctype>
26 #include <string>
27 #include <sstream>
28 #include <iomanip>
29 #include <ios>
30 #include <ostream>
31 #include <vector>
32
33 #include <stdint.h>
34 #include <string.h>
35
36 using namespace std;
37 using boost::lexical_cast;
38 using namespace isc::util;
39
40 namespace isc {
41 namespace dns {
42 namespace rdata {
43
44 uint16_t
45 Rdata::getLength() const {
46 OutputBuffer obuffer(0);
47
48 toWire(obuffer);
49
50 return (obuffer.getLength());
51 }
52
53 // XXX: we need to specify std:: for string to help doxygen match the
54 // function signature with that given in the header file.
55 RdataPtr
56 createRdata(const RRType& rrtype, const RRClass& rrclass,
57 const std::string& rdata_string)
58 {
59 return (RRParamRegistry::getRegistry().createRdata(rrtype, rrclass,
60 rdata_string));
61 }
62
63 RdataPtr
64 createRdata(const RRType& rrtype, const RRClass& rrclass,
65 isc::util::InputBuffer& buffer, size_t len)
66 {
67 if (len > MAX_RDLENGTH) {
68 isc_throw(InvalidRdataLength, "RDLENGTH too large");
69 }
70
71 size_t old_pos = buffer.getPosition();
72
73 RdataPtr rdata =
74 RRParamRegistry::getRegistry().createRdata(rrtype, rrclass, buffer,
75 len);
76
77 if (buffer.getPosition() - old_pos != len) {
78 isc_throw(InvalidRdataLength, "RDLENGTH mismatch: " <<
79 buffer.getPosition() - old_pos << " != " << len);
80 }
81
82 return (rdata);
83 }
84
85 RdataPtr
86 createRdata(const RRType& rrtype, const RRClass& rrclass, const Rdata& source)
87 {
88 return (RRParamRegistry::getRegistry().createRdata(rrtype, rrclass,
89 source));
90 }
91
92 namespace {
93 void
94 fromtextError(bool& error_issued, const MasterLexer& lexer,
95 MasterLoaderCallbacks& callbacks,
96 const MasterToken* token, const char* reason)
97 {
98 // Don't be too noisy if there are many issues for single RDATA
99 if (error_issued) {
100 return;
101 }
102 error_issued = true;
103
104 if (token == NULL) {
105 callbacks.error(lexer.getSourceName(), lexer.getSourceLine(),
106 "createRdata from text failed: " + string(reason));
107 return;
108 }
109
110 switch (token->getType()) {
111 case MasterToken::STRING:
112 case MasterToken::QSTRING:
113 callbacks.error(lexer.getSourceName(), lexer.getSourceLine(),
114 "createRdata from text failed near '" +
115 token->getString() + "': " + string(reason));
116 break;
117 case MasterToken::ERROR:
118 callbacks.error(lexer.getSourceName(), lexer.getSourceLine(),
119 "createRdata from text failed: " +
120 token->getErrorText());
121 break;
122 default:
123 // This case shouldn't happen based on how we use MasterLexer in
124 // createRdata(), so we could assert() that here. But since it
125 // depends on detailed behavior of other classes, we treat the case
126 // in a bit less harsh way.
127 isc_throw(Unexpected, "bug: createRdata() saw unexpected token type");
128 }
129 }
130 }
131
132 RdataPtr
133 createRdata(const RRType& rrtype, const RRClass& rrclass,
134 MasterLexer& lexer, const Name* origin,
135 MasterLoader::Options options,
136 MasterLoaderCallbacks& callbacks)
137 {
138 RdataPtr rdata;
139
140 bool error_issued = false;
141 try {
142 rdata = RRParamRegistry::getRegistry().createRdata(
143 rrtype, rrclass, lexer, origin, options, callbacks);
144 } catch (const MasterLexer::LexerError& error) {
145 fromtextError(error_issued, lexer, callbacks, &error.token_, "");
146 } catch (const Exception& ex) {
147 // Catching all isc::Exception is too broad, but right now we don't
148 // have better granularity. When we complete #2518 we can make this
149 // finer.
150 fromtextError(error_issued, lexer, callbacks, NULL, ex.what());
151 }
152 // Other exceptions mean a serious implementation bug or fatal system
153 // error; it doesn't make sense to catch and try to recover from them
154 // here. Just propagate.
155
156 // Consume to end of line / file.
157 // Call callback via fromtextError once if there was an error.
158 do {
159 const MasterToken& token = lexer.getNextToken();
160 switch (token.getType()) {
161 case MasterToken::END_OF_LINE:
162 return (rdata);
163 case MasterToken::END_OF_FILE:
164 callbacks.warning(lexer.getSourceName(), lexer.getSourceLine(),
165 "file does not end with newline");
166 return (rdata);
167 default:
168 rdata.reset(); // we'll return NULL
169 fromtextError(error_issued, lexer, callbacks, &token,
170 "extra input text");
171 // Continue until we see EOL or EOF
172 }
173 } while (true);
174
175 // We shouldn't reach here
176 assert(false);
177 return (RdataPtr()); // add explicit return to silence some compilers
178 }
179
180 int
181 compareNames(const Name& n1, const Name& n2) {
182 size_t len1 = n1.getLength();
183 size_t len2 = n2.getLength();
184 size_t cmplen = min(len1, len2);
185
186 for (size_t i = 0; i < cmplen; ++i) {
187 uint8_t c1 = tolower(n1.at(i));
188 uint8_t c2 = tolower(n2.at(i));
189 if (c1 < c2) {
190 return (-1);
191 } else if (c1 > c2) {
192 return (1);
193 }
194 }
195
196 return ((len1 == len2) ? 0 : (len1 < len2) ? -1 : 1);
197 }
198
199 namespace generic {
200 struct GenericImpl {
201 GenericImpl(const vector<uint8_t>& data) : data_(data) {}
202 vector<uint8_t> data_;
203 };
204
205 Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) {
206 if (rdata_len > MAX_RDLENGTH) {
207 isc_throw(InvalidRdataLength, "RDLENGTH too large");
208 }
209
210 vector<uint8_t> data(rdata_len);
211 if (rdata_len > 0) {
212 buffer.readData(&data[0], rdata_len);
213 }
214
215 impl_ = new GenericImpl(data);
216 }
217
218 GenericImpl*
219 Generic::constructFromLexer(MasterLexer& lexer) {
220 const MasterToken& token = lexer.getNextToken(MasterToken::STRING);
221 if (token.getString() != "\\#") {
222 isc_throw(InvalidRdataText,
223 "Missing the special token (\\#) for "
224 "unknown RDATA encoding");
225 }
226
227 // Initialize with an absurd value.
228 uint32_t rdlen = 65536;
229
230 try {
231 rdlen = lexer.getNextToken(MasterToken::NUMBER).getNumber();
232 } catch (const MasterLexer::LexerError&) {
233 isc_throw(InvalidRdataLength,
234 "Unknown RDATA length is invalid");
235 }
236
237 if (rdlen > 65535) {
238 isc_throw(InvalidRdataLength,
239 "Unknown RDATA length is out of range: " << rdlen);
240 }
241
242 vector<uint8_t> data;
243
244 if (rdlen > 0) {
245 string hex_txt;
246 string hex_part;
247 // Whitespace is allowed within hex data, so read to the end of input.
248 while (true) {
249 const MasterToken& token =
250 lexer.getNextToken(MasterToken::STRING, true);
251 if ((token.getType() == MasterToken::END_OF_FILE) ||
252 (token.getType() == MasterToken::END_OF_LINE)) {
253 // Unget the last read token as createRdata() expects us
254 // to leave it at the end-of-line or end-of-file when we
255 // return.
256 lexer.ungetToken();
257 break;
258 }
259 token.getString(hex_part);
260 hex_txt.append(hex_part);
261 }
262
263 try {
264 isc::util::encode::decodeHex(hex_txt, data);
265 } catch (const isc::BadValue& ex) {
266 isc_throw(InvalidRdataText,
267 "Invalid hex encoding of generic RDATA: " << ex.what());
268 }
269 }
270
271 if (data.size() != rdlen) {
272 isc_throw(InvalidRdataLength,
273 "Size of unknown RDATA hex data doesn't match RDLENGTH: "
274 << data.size() << " vs. " << rdlen);
275 }
276
277 return (new GenericImpl(data));
278 }
279
280 Generic::Generic(const std::string& rdata_string) :
281 impl_(NULL)
282 {
283 // We use unique_ptr here because if there is an exception in this
284 // constructor, the destructor is not called and there could be a
285 // leak of the GenericImpl that constructFromLexer() returns.
286 std::unique_ptr<GenericImpl> impl_ptr;
287
288 try {
289 std::istringstream ss(rdata_string);
290 MasterLexer lexer;
291 lexer.pushSource(ss);
292
293 impl_ptr.reset(constructFromLexer(lexer));
294
295 if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
296 isc_throw(InvalidRdataText, "extra input text for unknown RDATA: "
297 << rdata_string);
298 }
299 } catch (const MasterLexer::LexerError& ex) {
300 isc_throw(InvalidRdataText, "Failed to construct unknown RDATA "
301 "from '" << rdata_string << "': " << ex.what());
302 }
303
304 impl_ = impl_ptr.release();
305 }
306
307 Generic::Generic(MasterLexer& lexer, const Name*,
308 MasterLoader::Options,
309 MasterLoaderCallbacks&) :
310 impl_(constructFromLexer(lexer))
311 {
312 }
313
314 Generic::~Generic() {
315 delete impl_;
316 }
317
318 Generic::Generic(const Generic& source) :
319 Rdata(), impl_(new GenericImpl(*source.impl_))
320 {}
321
322 Generic&
323 // Our check is better than the usual if (this == &source),
324 // but cppcheck doesn't recognize it.
325 // cppcheck-suppress operatorEqToSelf
326 Generic::operator=(const Generic& source) {
327 if (impl_ == source.impl_) {
328 return (*this);
329 }
330
331 GenericImpl* newimpl = new GenericImpl(*source.impl_);
332 delete impl_;
333 impl_ = newimpl;
334
335 return (*this);
336 }
337
338 namespace {
339 class UnknownRdataDumper {
340 public:
341 UnknownRdataDumper(ostringstream& oss) : oss_(&oss) {}
342 void operator()(const unsigned char d)
343 {
344 *oss_ << setw(2) << static_cast<unsigned int>(d);
345 }
346 private:
347 ostringstream* oss_;
348 };
349 }
350
351 string
352 Generic::toText() const {
353 ostringstream oss;
354
355 oss << "\\# " << impl_->data_.size() << " ";
356 oss.fill('0');
357 oss << right << hex;
358 for_each(impl_->data_.begin(), impl_->data_.end(), UnknownRdataDumper(oss));
359
360 return (oss.str());
361 }
362
363 void
364 Generic::toWire(isc::util::OutputBuffer& buffer) const {
365 buffer.writeData(&impl_->data_[0], impl_->data_.size());
366 }
367
368 void
369 Generic::toWire(AbstractMessageRenderer& renderer) const {
370 renderer.writeData(&impl_->data_[0], impl_->data_.size());
371 }
372
373 namespace {
374 inline int
375 compare_internal(const GenericImpl& lhs, const GenericImpl& rhs) {
376 size_t this_len = lhs.data_.size();
377 size_t other_len = rhs.data_.size();
378 size_t len = (this_len < other_len) ? this_len : other_len;
379 int cmp;
380
381 // TODO: is there a need to check len - should we just assert?
382 // (Depends if it is possible for rdata to have zero length)
383 if ((len != 0) &&
384 ((cmp = memcmp(&lhs.data_[0], &rhs.data_[0], len)) != 0)) {
385 return (cmp);
386 } else {
387 return ((this_len == other_len) ? 0 :
388 (this_len < other_len) ? -1 : 1);
389 }
390 }
391 }
392
393 int
394 Generic::compare(const Rdata& other) const {
395 const Generic& other_rdata = dynamic_cast<const Generic&>(other);
396
397 return (compare_internal(*impl_, *other_rdata.impl_));
398 }
399
400 std::ostream&
401 operator<<(std::ostream& os, const Generic& rdata) {
402 return (os << rdata.toText());
403 }
404 } // end of namespace generic
405
406 } // end of namespace rdata
407 }
408 }
isc::dns::MasterToken
Tokens for MasterLexer.
Definition: master_lexer.h:39
rdata.h
isc::log::ERROR
@ ERROR
Definition: logger_level.h:28
isc::dns::MasterLoaderCallbacks::warning
void warning(const std::string &source_name, size_t source_line, const std::string &reason) const
Call callback for potential problems.
Definition: master_loader_callbacks.h:111
isc::dns::rdata::generic::GenericImpl
Definition: rdata.cc:200
isc::dns::Name::at
uint8_t at(size_t pos) const
Provides one-byte name data in wire format at the specified position.
Definition: name.h:346
isc::dns::MasterLexer
Tokenizer for parsing DNS master files.
Definition: master_lexer.h:301
isc::dns::MasterToken::END_OF_FILE
@ END_OF_FILE
End of file detected.
Definition: master_lexer.h:49
isc::dns::MasterLexer::getSourceLine
size_t getSourceLine() const
Return the input source line number.
isc::dns::rdata::generic::GenericImpl::data_
vector< uint8_t > data_
Definition: rdata.cc:202
isc::dns::rdata::compareNames
int compareNames(const Name &n1, const Name &n2)
Gives relative ordering of two names in terms of DNSSEC RDATA ordering.
Definition: rdata.cc:181
name.h
isc::dns::MasterToken::getType
Type getType() const
Return the token type.
Definition: master_lexer.h:157
isc::dns::rdata::createRdata
RdataPtr createRdata(const RRType &rrtype, const RRClass &rrclass, MasterLexer &lexer, const Name *origin, MasterLoader::Options options, MasterLoaderCallbacks &callbacks)
Create RDATA of a given pair of RR type and class using the master lexer.
Definition: rdata.cc:133
isc::dns::rdata::Rdata
The Rdata class is an abstract base class that provides a set of common interfaces to manipulate conc...
Definition: rdata.h:123
isc::dns::MasterToken::getString
std::string getString() const
Return the value of a string-variant token as a string object.
Definition: master_lexer.h:183
isc::dns::MasterLexer::LexerError::token_
const MasterToken token_
Definition: master_lexer.h:326
isc::dns::MasterLexer::ungetToken
void ungetToken()
Return the last token back to the lexer.
isc::dns::AbstractMessageRenderer
The AbstractMessageRenderer class is an abstract base class that provides common interfaces for rende...
Definition: messagerenderer.h:68
isc::dns::rdata::generic::Generic::~Generic
virtual ~Generic()
The destructor.
Definition: rdata.cc:314
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::MasterToken::getNumber
uint32_t getNumber() const
Return the value of a string-variant token as a string object.
Definition: master_lexer.h:218
isc::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition: exceptions/exceptions.cc:32
isc::dns::MasterLexer::LexerError
Exception thrown from a wrapper version of MasterLexer::getNextToken() for non fatal errors.
Definition: master_lexer.h:320
hex.h
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
isc::dns::MasterLexer::getNextToken
const MasterToken & getNextToken(Options options=NONE)
Parse and return another token from the input.
isc::dns::MasterLoaderCallbacks
Set of issue callbacks for a loader.
Definition: master_loader_callbacks.h:49
isc::dns::MasterLexer::getSourceName
std::string getSourceName() const
Return the name of the current input source name.
isc::dns::rdata::generic::Generic::toWire
virtual void toWire(isc::util::OutputBuffer &buffer) const
Render the generic::Generic in the wire format into a buffer.
Definition: rdata.cc:364
isc::dns::MasterToken::getErrorText
std::string getErrorText() const
Return a textual description of the error of a error type token.
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: exceptions/exceptions.h:132
isc::util::InputBuffer::readData
void readData(void *data, size_t len)
Read data of the specified length from the buffer and copy it to the caller supplied buffer.
Definition: buffer.h:186
isc::dns::Name
The Name class encapsulates DNS names.
Definition: name.h:223
isc::dns::rdata::InvalidRdataLength
A standard DNS module exception that is thrown if RDATA parser encounters an invalid or inconsistent ...
Definition: rdata.h:37
isc::util::OutputBuffer::getLength
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
isc::dns::Name::getLength
size_t getLength() const
Gets the length of the Name in its wire format.
Definition: name.h:360
isc::dns::rdata::generic::GenericImpl::GenericImpl
GenericImpl(const vector< uint8_t > &data)
Definition: rdata.cc:201
isc::util::OutputBuffer
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
isc::dns::Exception
Definition: dns/exceptions.h:25
isc::dns::rdata::generic::Generic::operator=
Generic & operator=(const Generic &source)
The assignment operator.
Definition: rdata.cc:326
rrtype.h
isc::dns::rdata::generic::Generic::compare
virtual int compare(const Rdata &other) const
Compare two instances of generic::Generic objects.
Definition: rdata.cc:394
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::rdata::generic::Generic
The generic::Generic class represents generic "unknown" RDATA.
Definition: rdata.h:249
buffer.h
isc::util::InputBuffer
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
rrparamregistry.h
isc::dns::rdata::generic::operator<<
std::ostream & operator<<(std::ostream &os, const Generic &rdata)
Insert the name as a string into stream.
Definition: rdata.cc:401
exceptions.h
isc::dns::MasterLexer::pushSource
bool pushSource(const char *filename, std::string *error=NULL)
Open a file and make it the current input source of MasterLexer.
isc::util::InputBuffer::getPosition
size_t getPosition() const
Return the current read position.
Definition: buffer.h:102
isc::dns::rdata::RdataPtr
boost::shared_ptr< Rdata > RdataPtr
The RdataPtr type is a pointer-like type, pointing to an object of some concrete derived class of Rda...
Definition: master_loader_callbacks.h:23
isc::dns::rdata::InvalidRdataText
A standard DNS module exception that is thrown if RDATA parser fails to recognize a given textual rep...
Definition: rdata.h:47
master_lexer.h
isc::dns::rdata::generic::Generic::toText
virtual std::string toText() const
Convert an generic::Generic object to a string.
Definition: rdata.cc:352
isc::util::OutputBuffer::writeData
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:547
isc::dns::rdata::MAX_RDLENGTH
const size_t MAX_RDLENGTH
Possible maximum length of RDATA, which is the maximum unsigned 16 bit value.
Definition: rdata.h:76
isc::dns::RRType
The RRType class encapsulates DNS resource record types.
Definition: rrtype.h:106
isc::dns::rdata::generic::Generic::Generic
Generic(const std::string &rdata_string)
Constructor from a string.
Definition: rdata.cc:280
isc::dns::MasterLoaderCallbacks::error
void error(const std::string &source_name, size_t source_line, const std::string &reason) const
Call callback for serious errors.
Definition: master_loader_callbacks.h:94
isc::dns::RRClass
The RRClass class encapsulates DNS resource record classes.
Definition: rrclass.h:98
isc::util::encode::decodeHex
void decodeHex(const string &input, vector< uint8_t > &result)
Decode a text encoded in the base16 ('hex') format into the original data.
Definition: base_n.cc:466
messagerenderer.h
isc::dns::MasterLoader::Options
Options
Options how the parsing should work.
Definition: master_loader.h:39