Kea  1.5.0
option6_client_fqdn.cc
Go to the documentation of this file.
1 // Copyright (C) 2013-2018 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_(),
133  domain_name_type_(source.domain_name_type_) {
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)
279  : Option(Option::V6, D6O_CLIENT_FQDN),
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
isc::dhcp::Option6ClientFqdn::toText
virtual std::string toText(int indent=0) const
Returns string representation of the option.
Definition: option6_client_fqdn.cc:427
isc::dhcp::Option6ClientFqdn::FLAG_MASK
static const uint8_t FLAG_MASK
Mask which zeroes MBZ flag bits.
Definition: option6_client_fqdn.h:99
isc::dhcp::OptionBuffer
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
isc::dhcp::Option6ClientFqdn::FLAG_S
static const uint8_t FLAG_S
S bit.
Definition: option6_client_fqdn.h:93
isc::dhcp::InvalidOption6FqdnFlags
Exception thrown when invalid flags have been specified for DHCPv6 Client Fqdn Option.
Definition: option6_client_fqdn.h:20
isc::dhcp::Option6ClientFqdnImpl::checkFlags
static void checkFlags(const uint8_t flags, const bool check_mbz)
Check if flags are valid.
Definition: option6_client_fqdn.cc:198
isc::dhcp::Option::type_
uint16_t type_
option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
Definition: option.h:515
isc::dhcp::Option6ClientFqdnImpl::setDomainName
void setDomainName(const std::string &domain_name, const Option6ClientFqdn::DomainNameType name_type)
Set a new domain name for the option.
Definition: option6_client_fqdn.cc:161
isc::dhcp::Option6ClientFqdn::operator=
Option6ClientFqdn & operator=(const Option6ClientFqdn &source)
Assignment operator.
Definition: option6_client_fqdn.cc:307
isc::util::str::trim
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
option6_client_fqdn.h
isc::dhcp::Option6ClientFqdn::~Option6ClientFqdn
virtual ~Option6ClientFqdn()
Destructor.
Definition: option6_client_fqdn.cc:289
isc::dhcp::Option6ClientFqdn
Represents DHCPv6 Client FQDN Option (code 39).
Definition: option6_client_fqdn.h:87
isc::dhcp::Option6ClientFqdn::FLAG_FIELD_LEN
static const uint16_t FLAG_FIELD_LEN
The length of the flag field within DHCPv6 Client Fqdn Option.
Definition: option6_client_fqdn.h:102
isc::dhcp::Option6ClientFqdn::resetDomainName
void resetDomainName()
Set empty domain-name.
Definition: option6_client_fqdn.cc:396
isc::dns::LabelSequence::getDataLength
size_t getDataLength() const
Return the length of the wire-format data of this LabelSequence.
Definition: labelsequence.cc:88
isc::dhcp::Option6ClientFqdn::getFlag
bool getFlag(const uint8_t flag) const
Checks if the specified flag of the DHCPv6 Client FQDN Option is set.
Definition: option6_client_fqdn.cc:316
labelsequence.h
isc::Exception
This is a base class for exceptions thrown from the DNS library module.
Definition: exceptions/exceptions.h:23
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::dhcp::Option6ClientFqdn::Option6ClientFqdn
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.
Definition: option6_client_fqdn.cc:276
strutil.h
isc::dhcp::Option6ClientFqdn::getDomainName
std::string getDomainName() const
Returns the domain-name in the text format.
Definition: option6_client_fqdn.cc:360
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
isc::dhcp::Option6ClientFqdnImpl
Implements the logic for the Option6ClientFqdn class.
Definition: option6_client_fqdn.cc:30
isc::dhcp::Option6ClientFqdn::pack
virtual void pack(isc::util::OutputBuffer &buf) const
Writes option in the wire format into a buffer.
Definition: option6_client_fqdn.cc:406
isc::dhcp::Option6ClientFqdnImpl::domain_name_type_
Option6ClientFqdn::DomainNameType domain_name_type_
Indicates whether domain name is partial or fully qualified.
Definition: option6_client_fqdn.cc:37
isc::util::OutputBuffer::writeUint8
void writeUint8(uint8_t data)
Write an unsigned 8-bit integer into the buffer.
Definition: buffer.h:463
isc::dns::Name
The Name class encapsulates DNS names.
Definition: name.h:223
isc::dhcp::Option6ClientFqdnImpl::flags_
uint8_t flags_
Holds flags carried by the option.
Definition: option6_client_fqdn.cc:33
isc::dhcp::Option::packHeader
void packHeader(isc::util::OutputBuffer &buf) const
Store option's header in a buffer.
Definition: option.cc:117
isc::util::OutputBuffer
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
isc::dhcp::Option6ClientFqdn::getDomainNameType
DomainNameType getDomainNameType() const
Returns enumerator value which indicates whether domain-name is partial or full.
Definition: option6_client_fqdn.cc:401
isc::dhcp::Option6ClientFqdnImpl::parseWireData
void parseWireData(OptionBufferConstIter first, OptionBufferConstIter last)
Parse the Option provided in the wire format.
Definition: option6_client_fqdn.cc:218
dhcp6.h
buffer.h
isc::dhcp::Option6ClientFqdn::DomainNameType
DomainNameType
Type of the domain-name: partial or full.
Definition: option6_client_fqdn.h:105
isc::dhcp::Option6ClientFqdn::FLAG_N
static const uint8_t FLAG_N
N bit.
Definition: option6_client_fqdn.h:95
isc::util::InputBuffer
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
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::dhcp::Option::setData
void setData(InputIterator first, InputIterator last)
Sets content of this option from buffer.
Definition: option.h:366
isc::dhcp::Option6ClientFqdn::clone
virtual OptionPtr clone() const
Copies this option and returns a pointer to the copy.
Definition: option6_client_fqdn.cc:299
isc::dhcp::Option6ClientFqdn::FLAG_O
static const uint8_t FLAG_O
O bit.
Definition: option6_client_fqdn.h:94
isc::dhcp::Option6ClientFqdn::unpack
virtual void unpack(OptionBufferConstIter first, OptionBufferConstIter last)
Parses option from the received buffer.
Definition: option6_client_fqdn.cc:416
io_utilities.h
isc::OutOfRange
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
Definition: exceptions/exceptions.h:115
isc::dhcp::Option6ClientFqdnImpl::Option6ClientFqdnImpl
Option6ClientFqdnImpl(const uint8_t flags, const std::string &domain_name, const Option6ClientFqdn::DomainNameType name_type)
Constructor, from domain name.
Definition: option6_client_fqdn.cc:102
isc::dhcp::OptionPtr
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
isc::dhcp::Option::operator=
Option & operator=(const Option &rhs)
Assignment operator.
Definition: option.cc:65
isc::dhcp::Option6ClientFqdn::resetFlags
void resetFlags()
Sets the flag field value to 0.
Definition: option6_client_fqdn.cc:355
isc::dhcp::Option6ClientFqdn::len
virtual uint16_t len() const
Returns length of the complete option (data length + DHCPv6 option header).
Definition: option6_client_fqdn.cc:443
isc::dhcp::Option6ClientFqdnImpl::domain_name_
boost::shared_ptr< isc::dns::Name > domain_name_
Holds the pointer to a domain name carried in the option.
Definition: option6_client_fqdn.cc:35
isc::dhcp::Option6ClientFqdnImpl::operator=
Option6ClientFqdnImpl & operator=(const Option6ClientFqdnImpl &source)
Assignment operator.
Definition: option6_client_fqdn.cc:143
isc::dhcp::Option6ClientFqdn::setDomainName
void setDomainName(const std::string &domain_name, const DomainNameType domain_name_type)
Set new domain-name.
Definition: option6_client_fqdn.cc:390
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::dhcp::Option6ClientFqdn::FULL
@ FULL
Definition: option6_client_fqdn.h:107
isc::dhcp::InvalidOption6FqdnDomainName
Exception thrown when invalid domain name is specified.
Definition: option6_client_fqdn.h:27
isc::dhcp::Option6ClientFqdn::packDomainName
void packDomainName(isc::util::OutputBuffer &buf) const
Writes domain-name in the wire format into a buffer.
Definition: option6_client_fqdn.cc:371
isc::dhcp::Option
Definition: option.h:58
isc::dhcp::Option::getHeaderLen
virtual uint16_t getHeaderLen() const
Returns length of header (2 for v4, 4 for v6)
Definition: option.cc:328
isc::dhcp::OptionBufferConstIter
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition: option.h:31
D6O_CLIENT_FQDN
@ D6O_CLIENT_FQDN
Definition: dhcp6.h:59
isc::dhcp::Option6ClientFqdn::PARTIAL
@ PARTIAL
Definition: option6_client_fqdn.h:106
isc::dhcp::Option6ClientFqdn::setFlag
void setFlag(const uint8_t flag, const bool set)
Modifies the value of the specified DHCPv6 Client Fqdn Option flag.
Definition: option6_client_fqdn.cc:328
isc::dns::LabelSequence
Light-weight Accessor to Name data.
Definition: labelsequence.h:35