Kea  1.9.9-git
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 
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.
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 
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 
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 
219 Generic::constructFromLexer(MasterLexer& lexer) {
220  const MasterToken& token = lexer.getNextToken(MasterToken::STRING);
221  if (token.getString() != "\\#") {
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 
310  impl_(constructFromLexer(lexer))
311 {
312 }
313 
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
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
365  buffer.writeData(&impl_->data_[0], impl_->data_.size());
366 }
367 
368 void
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 }
The Name class encapsulates DNS names.
Definition: name.h:223
const size_t MAX_RDLENGTH
Possible maximum length of RDATA, which is the maximum unsigned 16 bit value.
Definition: rdata.h:76
bool pushSource(const char *filename, std::string *error=NULL)
Open a file and make it the current input source of MasterLexer.
virtual ~Generic()
The destructor.
Definition: rdata.cc:314
virtual std::string toText() const
Convert an generic::Generic object to a string.
Definition: rdata.cc:352
size_t getSourceLine() const
Return the input source line number.
Generic & operator=(const Generic &source)
The assignment operator.
Definition: rdata.cc:326
Exception thrown from a wrapper version of MasterLexer::getNextToken() for non fatal errors...
Definition: master_lexer.h:320
The Rdata class is an abstract base class that provides a set of common interfaces to manipulate conc...
Definition: rdata.h:123
uint8_t at(size_t pos) const
Provides one-byte name data in wire format at the specified position.
Definition: name.h:346
STL namespace.
The generic::Generic class represents generic "unknown" RDATA.
Definition: rdata.h:249
std::ostream & operator<<(std::ostream &os, const Generic &rdata)
Insert the name as a string into stream.
Definition: rdata.cc:401
GenericImpl(const vector< uint8_t > &data)
Definition: rdata.cc:201
The RRClass class encapsulates DNS resource record classes.
Definition: rrclass.h:98
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:39
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:550
End of file detected.
Definition: master_lexer.h:49
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: edns.h:19
The AbstractMessageRenderer class is an abstract base class that provides common interfaces for rende...
A standard DNS module exception that is thrown if RDATA parser fails to recognize a given textual rep...
Definition: rdata.h:47
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:474
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the internal buffer of the renderer object. ...
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
std::string getSourceName() const
Return the name of the current input source name.
A standard DNS module exception that is thrown if RDATA parser encounters an invalid or inconsistent ...
Definition: rdata.h:37
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
const MasterToken & getNextToken(Options options=NONE)
Parse and return another token from the input.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
Generic(const std::string &rdata_string)
Constructor from a string.
Definition: rdata.cc:280
std::string getErrorText() const
Return a textual description of the error of a error type token.
Defines the logger used by the top-level component of kea-dhcp-ddns.
size_t getPosition() const
Return the current read position.
Definition: buffer.h:102
void ungetToken()
Return the last token back to the lexer.
virtual int compare(const Rdata &other) const
Compare two instances of generic::Generic objects.
Definition: rdata.cc:394
int compareNames(const Name &n1, const Name &n2)
Gives relative ordering of two names in terms of DNSSEC RDATA ordering.
Definition: rdata.cc:181
ostringstream * oss_
Definition: rdata.cc:347
size_t getLength() const
Gets the length of the Name in its wire format.
Definition: name.h:360
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
The RRType class encapsulates DNS resource record types.
Definition: rrtype.h:106
uint32_t getNumber() const
Return the value of a string-variant token as a string object.
Definition: master_lexer.h:218
Tokens for MasterLexer.
Definition: master_lexer.h:39
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
Type getType() const
Return the token type.
Definition: master_lexer.h:157
virtual void toWire(isc::util::OutputBuffer &buffer) const
Render the generic::Generic in the wire format into a buffer.
Definition: rdata.cc:364
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
std::string getString() const
Return the value of a string-variant token as a string object.
Definition: master_lexer.h:183
Tokenizer for parsing DNS master files.
Definition: master_lexer.h:301
Set of issue callbacks for a loader.
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 error(const std::string &source_name, size_t source_line, const std::string &reason) const
Call callback for serious errors.