Kea 2.5.5
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
17namespace isc {
18namespace dhcp {
19
31public:
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
102Option6ClientFqdnImpl(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
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
159void
161setDomainName(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
197void
198Option6ClientFqdnImpl::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
217void
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
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
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
315bool
316Option6ClientFqdn::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
327void
328Option6ClientFqdn::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
354void
356 impl_->flags_ = 0;
357}
358
359std::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
370void
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.
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
389void
390Option6ClientFqdn::setDomainName(const std::string& domain_name,
391 const DomainNameType domain_name_type) {
392 impl_->setDomainName(domain_name, domain_name_type);
393}
394
395void
398}
399
402 return (impl_->domain_name_type_);
403}
404
405void
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
415void
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
426std::string
427Option6ClientFqdn::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
442uint16_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
This is a base class for exceptions thrown from the DNS library module.
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
Exception thrown when invalid domain name is specified.
Exception thrown when invalid flags have been specified for DHCPv6 Client Fqdn Option.
Implements the logic for the Option6ClientFqdn class.
Option6ClientFqdnImpl & operator=(const Option6ClientFqdnImpl &source)
Assignment operator.
boost::shared_ptr< isc::dns::Name > domain_name_
Holds the pointer to a domain name carried in the option.
uint8_t flags_
Holds flags carried by the option.
Option6ClientFqdn::DomainNameType domain_name_type_
Indicates whether domain name is partial or fully qualified.
void setDomainName(const std::string &domain_name, const Option6ClientFqdn::DomainNameType name_type)
Set a new domain name for the option.
void parseWireData(OptionBufferConstIter first, OptionBufferConstIter last)
Parse the Option provided in the wire format.
Option6ClientFqdnImpl(const uint8_t flags, const std::string &domain_name, const Option6ClientFqdn::DomainNameType name_type)
Constructor, from domain name.
static void checkFlags(const uint8_t flags, const bool check_mbz)
Check if flags are valid.
Represents DHCPv6 Client FQDN Option (code 39).
void resetFlags()
Sets the flag field value to 0.
static const uint8_t FLAG_S
S bit.
static const uint8_t FLAG_N
N bit.
DomainNameType getDomainNameType() const
Returns enumerator value which indicates whether domain-name is partial or full.
virtual OptionPtr clone() const
Copies this option and returns a pointer to the copy.
static const uint16_t FLAG_FIELD_LEN
The length of the flag field within DHCPv6 Client Fqdn Option.
void setDomainName(const std::string &domain_name, const DomainNameType domain_name_type)
Set new domain-name.
void resetDomainName()
Set empty domain-name.
static const uint8_t FLAG_O
O bit.
static const uint8_t FLAG_MASK
Mask which zeroes MBZ flag bits.
virtual uint16_t len() const
Returns length of the complete option (data length + DHCPv6 option header).
virtual void unpack(OptionBufferConstIter first, OptionBufferConstIter last)
Parses option from the received buffer.
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.
std::string getDomainName() const
Returns the domain-name in the text format.
DomainNameType
Type of the domain-name: partial or full.
virtual void pack(isc::util::OutputBuffer &buf, bool check=true) const
Writes option in the wire format into a buffer.
Option6ClientFqdn & operator=(const Option6ClientFqdn &source)
Assignment operator.
void packDomainName(isc::util::OutputBuffer &buf) const
Writes domain-name in the wire format into a buffer.
virtual std::string toText(int indent=0) const
Returns string representation of the option.
bool getFlag(const uint8_t flag) const
Checks if the specified flag of the DHCPv6 Client FQDN Option is set.
virtual ~Option6ClientFqdn()
Destructor.
void setFlag(const uint8_t flag, const bool set)
Modifies the value of the specified DHCPv6 Client Fqdn Option flag.
uint16_t type_
option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
Definition: option.h:590
virtual uint16_t getHeaderLen() const
Returns length of header (2 for v4, 4 for v6)
Definition: option.cc:321
Option & operator=(const Option &rhs)
Assignment operator.
Definition: option.cc:73
void setData(InputIterator first, InputIterator last)
Sets content of this option from buffer.
Definition: option.h:427
void packHeader(isc::util::OutputBuffer &buf, bool check=true) const
Store option's header in a buffer.
Definition: option.cc:119
Light-weight Accessor to Name data.
Definition: labelsequence.h:35
const uint8_t * getData(size_t *len) const
Return the wire-format data for this LabelSequence.
size_t getDataLength() const
Return the length of the wire-format data of this LabelSequence.
The Name class encapsulates DNS names.
Definition: name.h:223
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
void writeUint8(uint8_t data)
Write an unsigned 8-bit integer into the buffer.
Definition: buffer.h:466
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:550
@ D6O_CLIENT_FQDN
Definition: dhcp6.h:59
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition: option.h:30
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
Defines the logger used by the top-level component of kea-lfc.