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