Kea  2.3.3-git
option6_client_fqdn.cc
Go to the documentation of this file.
1 // Copyright (C) 2013-2022 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 <dhcp/dhcp6.h>
11 #include <dns/labelsequence.h>
12 #include <util/buffer.h>
13 #include <util/io_utilities.h>
14 #include <util/strutil.h>
15 #include <sstream>
16 
17 namespace isc {
18 namespace dhcp {
19 
31 public:
33  uint8_t flags_;
35  boost::shared_ptr<isc::dns::Name> domain_name_;
38 
46  Option6ClientFqdnImpl(const uint8_t flags,
47  const std::string& domain_name,
48  const Option6ClientFqdn::DomainNameType name_type);
49 
58 
63 
68 
74  void setDomainName(const std::string& domain_name,
75  const Option6ClientFqdn::DomainNameType name_type);
76 
88  static void checkFlags(const uint8_t flags, const bool check_mbz);
89 
98 
99 };
100 
102 Option6ClientFqdnImpl(const uint8_t flags,
103  const std::string& domain_name,
104  // cppcheck 1.57 complains that const enum value is not
105  // passed by reference. Note that it accepts the non-const
106  // enum to be passed by value. In both cases it is
107  // unnecessary to pass the enum by reference.
108  // cppcheck-suppress passedByValue
109  const Option6ClientFqdn::DomainNameType name_type)
110  : flags_(flags),
111  domain_name_(),
112  domain_name_type_(name_type) {
113 
114  // Check if flags are correct. Also check if MBZ bits are set.
115  checkFlags(flags_, true);
116  // Set domain name. It may throw an exception if domain name has wrong
117  // format.
118  setDomainName(domain_name, name_type);
119 }
120 
122  OptionBufferConstIter last) {
123  parseWireData(first, last);
124  // Verify that flags value was correct. Do not check if MBZ bits are
125  // set because we should ignore those bits in received message.
126  checkFlags(flags_, false);
127 }
128 
131  : flags_(source.flags_),
132  domain_name_(),
134  if (source.domain_name_) {
135  domain_name_.reset(new isc::dns::Name(*source.domain_name_));
136  }
137 }
138 
140 // This assignment operator handles assignment to self, it copies all
141 // required values.
142 // cppcheck-suppress operatorEqToSelf
144  if (source.domain_name_) {
145  domain_name_.reset(new isc::dns::Name(*source.domain_name_));
146 
147  } else {
148  domain_name_.reset();
149 
150  }
151 
152  // This assignment should be exception safe.
153  flags_ = source.flags_;
155 
156  return (*this);
157 }
158 
159 void
161 setDomainName(const std::string& domain_name,
162  // cppcheck 1.57 complains that const enum value is not
163  // passed by reference. Note that it accepts the non-const
164  // enum to be passed by value. In both cases it is
165  // unnecessary to pass the enum by reference.
166  // cppcheck-suppress passedByValue
167  const Option6ClientFqdn::DomainNameType name_type) {
168  // domain-name must be trimmed. Otherwise, string comprising spaces only
169  // would be treated as a fully qualified name.
170  std::string name = isc::util::str::trim(domain_name);
171  if (name.empty()) {
172  if (name_type == Option6ClientFqdn::FULL) {
174  "fully qualified domain-name must not be empty"
175  << " when setting new domain-name for DHCPv6 Client"
176  << " FQDN Option");
177  }
178  // The special case when domain-name is empty is marked by setting the
179  // pointer to the domain-name object to NULL.
180  domain_name_.reset();
181 
182  } else {
183  try {
184  domain_name_.reset(new isc::dns::Name(name, true));
185 
186  } catch (const Exception&) {
187  isc_throw(InvalidOption6FqdnDomainName, "invalid domain-name value '"
188  << domain_name << "' when setting new domain-name for"
189  << " DHCPv6 Client FQDN Option");
190 
191  }
192  }
193 
194  domain_name_type_ = name_type;
195 }
196 
197 void
198 Option6ClientFqdnImpl::checkFlags(const uint8_t flags, const bool check_mbz) {
199  // The Must Be Zero (MBZ) bits must not be set.
200  if (check_mbz && ((flags & ~Option6ClientFqdn::FLAG_MASK) != 0)) {
202  "invalid DHCPv6 Client FQDN Option flags: 0x"
203  << std::hex << static_cast<int>(flags) << std::dec);
204  }
205 
206  // According to RFC 4704, section 4.1. if the N bit is 1, the S bit
207  // MUST be 0. Checking it here.
211  "both N and S flag of the DHCPv6 Client FQDN Option are set."
212  << " According to RFC 4704, if the N bit is 1 the S bit"
213  << " MUST be 0");
214  }
215 }
216 
217 void
219  OptionBufferConstIter last) {
220 
221  // Buffer must comprise at least one byte with the flags.
222  // The domain-name may be empty.
223  if (std::distance(first, last) < Option6ClientFqdn::FLAG_FIELD_LEN) {
224  isc_throw(OutOfRange, "DHCPv6 Client FQDN Option ("
225  << D6O_CLIENT_FQDN << ") is truncated. Minimal option"
226  << " size is " << Option6ClientFqdn::FLAG_FIELD_LEN
227  << ", got option with size " << std::distance(first, last));
228  }
229 
230  // Parse flags
231  flags_ = *(first++);
232 
233  // Parse domain-name if any.
234  if (std::distance(first, last) > 0) {
235  // The FQDN may comprise a partial domain-name. In this case it lacks
236  // terminating 0. If this is the case, we will need to add zero at
237  // the end because Name object constructor requires it.
238  if (*(last - 1) != 0) {
239  // Create temporary buffer and add terminating zero.
240  OptionBuffer buf(first, last);
241  buf.push_back(0);
242  // Reset domain name.
243  isc::util::InputBuffer name_buf(&buf[0], buf.size());
244  try {
245  domain_name_.reset(new isc::dns::Name(name_buf, true));
246  } catch (const Exception&) {
247  isc_throw(InvalidOption6FqdnDomainName, "failed to parse "
248  "partial domain-name from wire format");
249  }
250  // Terminating zero was missing, so set the domain-name type
251  // to partial.
253  } else {
254  // We are dealing with fully qualified domain name so there is
255  // no need to add terminating zero. Simply pass the buffer to
256  // Name object constructor.
257  isc::util::InputBuffer name_buf(&(*first),
258  std::distance(first, last));
259  try {
260  domain_name_.reset(new isc::dns::Name(name_buf, true));
261  } catch (const Exception&) {
262  isc_throw(InvalidOption6FqdnDomainName, "failed to parse "
263  "fully qualified domain-name from wire format");
264  }
265  // Set the domain-type to fully qualified domain name.
267  }
268  }
269 }
270 
272  : Option(Option::V6, D6O_CLIENT_FQDN),
273  impl_(new Option6ClientFqdnImpl(flag, "", PARTIAL)) {
274 }
275 
277  const std::string& domain_name,
278  const DomainNameType domain_name_type)
280  impl_(new Option6ClientFqdnImpl(flag, domain_name, domain_name_type)) {
281 }
282 
285  : Option(Option::V6, D6O_CLIENT_FQDN, first, last),
286  impl_(new Option6ClientFqdnImpl(first, last)) {
287 }
288 
290  delete(impl_);
291 }
292 
294  : Option(source),
295  impl_(new Option6ClientFqdnImpl(*source.impl_)) {
296 }
297 
298 OptionPtr
300  return (cloneInternal<Option6ClientFqdn>());
301 }
302 
304 // This assignment operator handles assignment to self, it uses copy
305 // constructor of Option6ClientFqdnImpl to copy all required values.
306 // cppcheck-suppress operatorEqToSelf
308  Option::operator=(source);
309  Option6ClientFqdnImpl* old_impl = impl_;
310  impl_ = new Option6ClientFqdnImpl(*source.impl_);
311  delete(old_impl);
312  return (*this);
313 }
314 
315 bool
316 Option6ClientFqdn::getFlag(const uint8_t flag) const {
317  // Caller should query for one of the: N, S or O flags. Any other
318  // value is invalid.
319  if (flag != FLAG_S && flag != FLAG_O && flag != FLAG_N) {
320  isc_throw(InvalidOption6FqdnFlags, "invalid DHCPv6 Client FQDN"
321  << " Option flag specified, expected N, S or O");
322  }
323 
324  return ((impl_->flags_ & flag) != 0);
325 }
326 
327 void
328 Option6ClientFqdn::setFlag(const uint8_t flag, const bool set_flag) {
329  // Check that flag is in range between 0x1 and 0x7. Note that this
330  // allows to set or clear multiple flags concurrently. Setting
331  // concurrent bits is discouraged (see header file) but it is not
332  // checked here so it will work.
333  if (((flag & ~FLAG_MASK) != 0) || (flag == 0)) {
334  isc_throw(InvalidOption6FqdnFlags, "invalid DHCPv6 Client FQDN"
335  << " Option flag 0x" << std::hex
336  << static_cast<int>(flag) << std::dec
337  << " is being set. Expected: N, S or O");
338  }
339 
340  // Copy the current flags into local variable. That way we will be able
341  // to test new flags settings before applying them.
342  uint8_t new_flag = impl_->flags_;
343  if (set_flag) {
344  new_flag |= flag;
345  } else {
346  new_flag &= ~flag;
347  }
348 
349  // Check new flags. If they are valid, apply them.
350  Option6ClientFqdnImpl::checkFlags(new_flag, true);
351  impl_->flags_ = new_flag;
352 }
353 
354 void
356  impl_->flags_ = 0;
357 }
358 
359 std::string
361  if (impl_->domain_name_) {
362  return (impl_->domain_name_->toText(impl_->domain_name_type_ ==
363  PARTIAL));
364  }
365  // If an object holding domain-name is NULL it means that the domain-name
366  // is empty.
367  return ("");
368 }
369 
370 void
372  // There is nothing to do if domain-name is empty.
373  if (!impl_->domain_name_) {
374  return;
375  }
376 
377  // Domain name, encoded as a set of labels.
378  isc::dns::LabelSequence labels(*impl_->domain_name_);
379  if (labels.getDataLength() > 0) {
380  size_t read_len = 0;
381  const uint8_t* data = labels.getData(&read_len);
382  if (impl_->domain_name_type_ == PARTIAL) {
383  --read_len;
384  }
385  buf.writeData(data, read_len);
386  }
387 }
388 
389 void
390 Option6ClientFqdn::setDomainName(const std::string& domain_name,
391  const DomainNameType domain_name_type) {
392  impl_->setDomainName(domain_name, domain_name_type);
393 }
394 
395 void
397  setDomainName("", PARTIAL);
398 }
399 
402  return (impl_->domain_name_type_);
403 }
404 
405 void
407  // Header = option code and length.
408  packHeader(buf);
409  // Flags field.
410  buf.writeUint8(impl_->flags_);
411  // Domain name.
412  packDomainName(buf);
413 }
414 
415 void
417  OptionBufferConstIter last) {
418  setData(first, last);
419  impl_->parseWireData(first, last);
420  // Check that the flags in the received option are valid. Ignore MBZ bits
421  // because we don't want to discard the whole option because of MBZ bits
422  // being set.
423  impl_->checkFlags(impl_->flags_, false);
424 }
425 
426 std::string
427 Option6ClientFqdn::toText(int indent) const {
428  std::ostringstream stream;
429  std::string in(indent, ' '); // base indentation
430  stream << in << "type=" << type_ << "(CLIENT_FQDN), "
431  << "flags: ("
432  << "N=" << (getFlag(FLAG_N) ? "1" : "0") << ", "
433  << "O=" << (getFlag(FLAG_O) ? "1" : "0") << ", "
434  << "S=" << (getFlag(FLAG_S) ? "1" : "0") << "), "
435  << "domain-name='" << getDomainName() << "' ("
436  << (getDomainNameType() == PARTIAL ? "partial" : "full")
437  << ")";
438 
439  return (stream.str());
440 }
441 
442 uint16_t
444  uint16_t domain_name_length = 0;
445  if (impl_->domain_name_) {
446  // If domain name is partial, the NULL terminating character
447  // is not included and the option. Length has to be adjusted.
448  domain_name_length = impl_->domain_name_type_ == FULL ?
449  impl_->domain_name_->getLength() :
450  impl_->domain_name_->getLength() - 1;
451  }
452  return (getHeaderLen() + FLAG_FIELD_LEN + domain_name_length);
453 }
454 
455 } // end of isc::dhcp namespace
456 } // end of isc namespace
static const uint8_t FLAG_N
N bit.
The Name class encapsulates DNS names.
Definition: name.h:223
virtual OptionPtr clone() const
Copies this option and returns a pointer to the copy.
Option6ClientFqdnImpl(const uint8_t flags, const std::string &domain_name, const Option6ClientFqdn::DomainNameType name_type)
Constructor, from domain name.
Option6ClientFqdn(const uint8_t flags, const std::string &domain_name, const DomainNameType domain_name_type=FULL)
Constructor, creates option instance using flags and domain name.
virtual void unpack(OptionBufferConstIter first, OptionBufferConstIter last)
Parses option from the received buffer.
Represents DHCPv6 Client FQDN Option (code 39).
void parseWireData(OptionBufferConstIter first, OptionBufferConstIter last)
Parse the Option provided in the wire format.
static const uint8_t FLAG_O
O bit.
static const uint16_t FLAG_FIELD_LEN
The length of the flag field within DHCPv6 Client Fqdn Option.
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
void packHeader(isc::util::OutputBuffer &buf, bool check=true) const
Store option&#39;s header in a buffer.
Definition: option.cc:119
size_t getDataLength() const
Return the length of the wire-format data of this LabelSequence.
virtual void pack(isc::util::OutputBuffer &buf, bool check=true) const
Writes option in the wire format into a buffer.
virtual uint16_t getHeaderLen() const
Returns length of header (2 for v4, 4 for v6)
Definition: option.cc:321
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
static const uint8_t FLAG_MASK
Mask which zeroes MBZ flag bits.
bool getFlag(const uint8_t flag) const
Checks if the specified flag of the DHCPv6 Client FQDN Option is set.
void setDomainName(const std::string &domain_name, const DomainNameType domain_name_type)
Set new domain-name.
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:550
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Option6ClientFqdn & operator=(const Option6ClientFqdn &source)
Assignment operator.
boost::shared_ptr< isc::dns::Name > domain_name_
Holds the pointer to a domain name carried in the option.
Option6ClientFqdnImpl & operator=(const Option6ClientFqdnImpl &source)
Assignment operator.
virtual std::string toText(int indent=0) const
Returns string representation of the option.
const uint8_t * getData(size_t *len) const
Return the wire-format data for this LabelSequence.
void setDomainName(const std::string &domain_name, const Option6ClientFqdn::DomainNameType name_type)
Set a new domain name for the option.
virtual uint16_t len() const
Returns length of the complete option (data length + DHCPv6 option header).
static void checkFlags(const uint8_t flags, const bool check_mbz)
Check if flags are valid.
Implements the logic for the Option6ClientFqdn class.
Exception thrown when invalid flags have been specified for DHCPv6 Client Fqdn Option.
void packDomainName(isc::util::OutputBuffer &buf) const
Writes domain-name in the wire format into a buffer.
void resetFlags()
Sets the flag field value to 0.
virtual ~Option6ClientFqdn()
Destructor.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition: option.h:30
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-lfc.
Option6ClientFqdn::DomainNameType domain_name_type_
Indicates whether domain name is partial or fully qualified.
uint8_t flags_
Holds flags carried by the option.
DomainNameType getDomainNameType() const
Returns enumerator value which indicates whether domain-name is partial or full.
Option & operator=(const Option &rhs)
Assignment operator.
Definition: option.cc:73
std::string getDomainName() const
Returns the domain-name in the text format.
static const uint8_t FLAG_S
S bit.
void writeUint8(uint8_t data)
Write an unsigned 8-bit integer into the buffer.
Definition: buffer.h:466
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
DomainNameType
Type of the domain-name: partial or full.
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
uint16_t type_
option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
Definition: option.h:590
void setData(InputIterator first, InputIterator last)
Sets content of this option from buffer.
Definition: option.h:427
void resetDomainName()
Set empty domain-name.
void setFlag(const uint8_t flag, const bool set)
Modifies the value of the specified DHCPv6 Client Fqdn Option flag.
Exception thrown when invalid domain name is specified.
Light-weight Accessor to Name data.
Definition: labelsequence.h:35