Kea 2.5.4
option4_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/dhcp4.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_;
38 boost::shared_ptr<isc::dns::Name> domain_name_;
41
50 Option4ClientFqdnImpl(const uint8_t flags,
51 const Option4ClientFqdn::Rcode& rcode,
52 const std::string& domain_name,
53 const Option4ClientFqdn::DomainNameType name_type);
54
63
68
73
79 void setDomainName(const std::string& domain_name,
80 const Option4ClientFqdn::DomainNameType name_type);
81
93 static void checkFlags(const uint8_t flags, const bool check_mbz);
94
103
108
117
118};
119
121Option4ClientFqdnImpl(const uint8_t flags,
122 const Option4ClientFqdn::Rcode& rcode,
123 const std::string& domain_name,
124 // cppcheck 1.57 complains that const enum value is not passed
125 // by reference. Note that, it accepts the non-const enum value
126 // to be passed by value. In both cases it is unnecessary to
127 // pass the enum by reference.
128 // cppcheck-suppress passedByValue
129 const Option4ClientFqdn::DomainNameType name_type)
130 : flags_(flags),
131 rcode1_(rcode),
132 rcode2_(rcode),
133 domain_name_(),
134 domain_name_type_(name_type) {
135
136 // Check if flags are correct. Also, check that MBZ bits are not set. If
137 // they are, throw exception.
138 checkFlags(flags_, true);
139 // Set domain name. It may throw an exception if domain name has wrong
140 // format.
141 setDomainName(domain_name, name_type);
142}
143
146 : rcode1_(Option4ClientFqdn::RCODE_CLIENT()),
147 rcode2_(Option4ClientFqdn::RCODE_CLIENT()) {
148 parseWireData(first, last);
149 // Verify that flags value was correct. This constructor is used to parse
150 // incoming packet, so don't check MBZ bits. They are ignored because we
151 // don't want to discard the whole option because MBZ bits are set.
152 checkFlags(flags_, false);
153}
154
157 : flags_(source.flags_),
158 rcode1_(source.rcode1_),
159 rcode2_(source.rcode2_),
160 domain_name_(),
161 domain_name_type_(source.domain_name_type_) {
162 if (source.domain_name_) {
163 domain_name_.reset(new isc::dns::Name(*source.domain_name_));
164 }
165}
166
168// This assignment operator handles assignment to self, it copies all
169// required values.
170// cppcheck-suppress operatorEqToSelf
172 if (source.domain_name_) {
173 domain_name_.reset(new isc::dns::Name(*source.domain_name_));
174
175 } else {
176 domain_name_.reset();
177 }
178
179 // Assignment is exception safe.
180 flags_ = source.flags_;
181 rcode1_ = source.rcode1_;
182 rcode2_ = source.rcode2_;
184
185 return (*this);
186}
187
188void
190setDomainName(const std::string& domain_name,
191 // cppcheck 1.57 complains that const enum value is not passed
192 // by reference. Note that, it accepts the non-const enum
193 // to be passed by value. In both cases it is unnecessary to
194 // pass the enum by reference.
195 // cppcheck-suppress passedByValue
196 const Option4ClientFqdn::DomainNameType name_type) {
197 // domain-name must be trimmed. Otherwise, string comprising spaces only
198 // would be treated as a fully qualified name.
199 std::string name = isc::util::str::trim(domain_name);
200 if (name.empty()) {
201 if (name_type == Option4ClientFqdn::FULL) {
203 "fully qualified domain-name must not be empty"
204 << " when setting new domain-name for DHCPv4 Client"
205 << " FQDN Option");
206 }
207 // The special case when domain-name is empty is marked by setting the
208 // pointer to the domain-name object to NULL.
209 domain_name_.reset();
210
211 } else {
212 try {
213 // The second argument indicates that the name should be converted
214 // to lower case.
215 domain_name_.reset(new isc::dns::Name(name, true));
216
217 } catch (const Exception&) {
219 "invalid domain-name value '"
220 << domain_name << "' when setting new domain-name for"
221 << " DHCPv4 Client FQDN Option");
222
223 }
224 }
225
226 domain_name_type_ = name_type;
227}
228
229void
230Option4ClientFqdnImpl::checkFlags(const uint8_t flags, const bool check_mbz) {
231 // The Must Be Zero (MBZ) bits must not be set.
232 if (check_mbz && ((flags & ~Option4ClientFqdn::FLAG_MASK) != 0)) {
234 "invalid DHCPv4 Client FQDN Option flags: 0x"
235 << std::hex << static_cast<int>(flags) << std::dec);
236 }
237
238 // According to RFC 4702, section 2.1. if the N bit is 1, the S bit
239 // MUST be 0. Checking it here.
243 "both N and S flag of the DHCPv4 Client FQDN Option are set."
244 << " According to RFC 4702, if the N bit is 1 the S bit"
245 << " MUST be 0");
246 }
247}
248
249void
252
253 // Buffer must comprise at least one byte with the flags.
254 // The domain-name may be empty.
255 if (std::distance(first, last) < Option4ClientFqdn::FIXED_FIELDS_LEN) {
256 isc_throw(OutOfRange, "DHCPv4 Client FQDN Option ("
257 << DHO_FQDN << ") is truncated");
258 }
259
260 // Parse flags
261 flags_ = *(first++);
262
263 // Parse RCODE1 and RCODE2.
264 rcode1_ = Option4ClientFqdn::Rcode(*(first++));
265 rcode2_ = Option4ClientFqdn::Rcode(*(first++));
266
267 try {
268 if ((flags_ & Option4ClientFqdn::FLAG_E) != 0) {
269 parseCanonicalDomainName(first, last);
270
271 } else {
272 parseASCIIDomainName(first, last);
273
274 }
275 } catch (const Exception& ex) {
277 "failed to parse the domain-name in DHCPv4 Client FQDN"
278 << " Option: " << ex.what());
279 }
280}
281
282void
285 // Parse domain-name if any.
286 if (std::distance(first, last) > 0) {
287 // The FQDN may comprise a partial domain-name. In this case it lacks
288 // terminating 0. If this is the case, we will need to add zero at
289 // the end because Name object constructor requires it.
290 if (*(last - 1) != 0) {
291 // Create temporary buffer and add terminating zero.
292 OptionBuffer buf(first, last);
293 buf.push_back(0);
294 // Reset domain name.
295 isc::util::InputBuffer name_buf(&buf[0], buf.size());
296 // The second argument indicates that the name should be converted
297 // to lower case.
298 domain_name_.reset(new isc::dns::Name(name_buf, true));
299 // Terminating zero was missing, so set the domain-name type
300 // to partial.
302 } else {
303 // We are dealing with fully qualified domain name so there is
304 // no need to add terminating zero. Simply pass the buffer to
305 // Name object constructor.
306 isc::util::InputBuffer name_buf(&(*first),
307 std::distance(first, last));
308 // The second argument indicates that the name should be converted
309 // to lower case.
310 domain_name_.reset(new isc::dns::Name(name_buf, true));
311 // Set the domain-type to fully qualified domain name.
313 }
314 }
315}
316
317void
320 if (std::distance(first, last) > 0) {
321 std::string domain_name(first, last);
322 // The second argument indicates that the name should be converted
323 // to lower case.
324 domain_name_.reset(new isc::dns::Name(domain_name, true));
325 domain_name_type_ = domain_name[domain_name.length() - 1] == '.' ?
327 }
328}
329
330Option4ClientFqdn::Option4ClientFqdn(const uint8_t flag, const Rcode& rcode)
331 : Option(Option::V4, DHO_FQDN),
332 impl_(new Option4ClientFqdnImpl(flag, rcode, "", PARTIAL)) {
333}
334
336 const Rcode& rcode,
337 const std::string& domain_name,
338 const DomainNameType domain_name_type)
339 : Option(Option::V4, DHO_FQDN),
340 impl_(new Option4ClientFqdnImpl(flag, rcode, domain_name,
341 domain_name_type)) {
342}
343
346 : Option(Option::V4, DHO_FQDN, first, last),
347 impl_(new Option4ClientFqdnImpl(first, last)) {
348}
349
351 delete (impl_);
352}
353
355 : Option(source),
356 impl_(new Option4ClientFqdnImpl(*source.impl_)) {
357}
358
361 return (cloneInternal<Option4ClientFqdn>());
362}
363
365// This assignment operator handles assignment to self, it uses copy
366// constructor of Option4ClientFqdnImpl to copy all required values.
367// cppcheck-suppress operatorEqToSelf
369 Option::operator=(source);
370 Option4ClientFqdnImpl* old_impl = impl_;
371 impl_ = new Option4ClientFqdnImpl(*source.impl_);
372 delete(old_impl);
373 return (*this);
374}
375
376bool
377Option4ClientFqdn::getFlag(const uint8_t flag) const {
378 // Caller should query for one of the: E, N, S or O flags. Any other value
380 if (flag != FLAG_S && flag != FLAG_O && flag != FLAG_N && flag != FLAG_E) {
381 isc_throw(InvalidOption4FqdnFlags, "invalid DHCPv4 Client FQDN"
382 << " Option flag specified, expected E, N, S or O");
383 }
384
385 return ((impl_->flags_ & flag) != 0);
386}
387
388void
389Option4ClientFqdn::setFlag(const uint8_t flag, const bool set_flag) {
390 // Check that flag is in range between 0x1 and 0x7. Although it is
391 // discouraged this check doesn't preclude the caller from setting
392 // multiple flags concurrently.
393 if (((flag & ~FLAG_MASK) != 0) || (flag == 0)) {
394 isc_throw(InvalidOption4FqdnFlags, "invalid DHCPv4 Client FQDN"
395 << " Option flag 0x" << std::hex
396 << static_cast<int>(flag) << std::dec
397 << " is being set. Expected combination of E, N, S and O");
398 }
399
400 // Copy the current flags into local variable. That way we will be able
401 // to test new flags settings before applying them.
402 uint8_t new_flag = impl_->flags_;
403 if (set_flag) {
404 new_flag |= flag;
405 } else {
406 new_flag &= ~flag;
407 }
408
409 // Check new flags. If they are valid, apply them. Also, check that MBZ
410 // bits are not set.
411 Option4ClientFqdnImpl::checkFlags(new_flag, true);
412 impl_->flags_ = new_flag;
413}
414
415std::pair<Option4ClientFqdn::Rcode, Option4ClientFqdn::Rcode>
417 return (std::make_pair(impl_->rcode1_, impl_->rcode2_));
418}
419
420void
422 impl_->rcode1_ = rcode;
423 impl_->rcode2_ = rcode;
424}
425
426void
428 impl_->flags_ = 0;
429}
430
431std::string
433 if (impl_->domain_name_) {
434 return (impl_->domain_name_->toText(impl_->domain_name_type_ ==
435 PARTIAL));
436 }
437 // If an object holding domain-name is NULL it means that the domain-name
438 // is empty.
439 return ("");
440}
441
442void
444 // If domain-name is empty, do nothing.
445 if (!impl_->domain_name_) {
446 return;
447 }
448
449 if (getFlag(FLAG_E)) {
450 // Domain name, encoded as a set of labels.
452 if (labels.getDataLength() > 0) {
453 size_t read_len = 0;
454 const uint8_t* data = labels.getData(&read_len);
455 if (impl_->domain_name_type_ == PARTIAL) {
456 --read_len;
457 }
458 buf.writeData(data, read_len);
459 }
460
461 } else {
462 std::string domain_name = getDomainName();
463 if (!domain_name.empty()) {
464 buf.writeData(&domain_name[0], domain_name.size());
465 }
466
467 }
468}
469
470void
471Option4ClientFqdn::setDomainName(const std::string& domain_name,
472 const DomainNameType domain_name_type) {
473 impl_->setDomainName(domain_name, domain_name_type);
474}
475
476void
479}
480
483 return (impl_->domain_name_type_);
484}
485
486void
488 // Header = option code and length.
489 packHeader(buf, check);
490 // Flags field.
491 buf.writeUint8(impl_->flags_);
492 // RCODE1 and RCODE2
493 buf.writeUint8(impl_->rcode1_.getCode());
494 buf.writeUint8(impl_->rcode2_.getCode());
495 // Domain name.
496 packDomainName(buf);
497}
498
499void
502 setData(first, last);
503 impl_->parseWireData(first, last);
504 // Check that the flags in the received option are valid. Ignore MBZ bits,
505 // because we don't want to discard the whole option because of MBZ bits
506 // being set.
507 impl_->checkFlags(impl_->flags_, false);
508}
509
510std::string
511Option4ClientFqdn::toText(int indent) const {
512 std::ostringstream stream;
513 std::string in(indent, ' '); // base indentation
514 stream << in << "type=" << type_ << " (CLIENT_FQDN), "
515 << "flags: ("
516 << "N=" << (getFlag(FLAG_N) ? "1" : "0") << ", "
517 << "E=" << (getFlag(FLAG_E) ? "1" : "0") << ", "
518 << "O=" << (getFlag(FLAG_O) ? "1" : "0") << ", "
519 << "S=" << (getFlag(FLAG_S) ? "1" : "0") << "), "
520 << "domain-name='" << getDomainName() << "' ("
521 << (getDomainNameType() == PARTIAL ? "partial" : "full")
522 << ")";
523
524 return (stream.str());
525}
526
527uint16_t
529 uint16_t domain_name_length = 0;
530 // Try to calculate the length of the domain name only if there is
531 // any domain name specified.
532 if (impl_->domain_name_) {
533 // If the E flag is specified, the domain name is encoded in the
534 // canonical format. The length of the domain name depends on
535 // whether it is a partial or fully qualified names. For the
536 // former the length is 1 octet lesser because it lacks terminating
537 // zero.
538 if (getFlag(FLAG_E)) {
539 domain_name_length = impl_->domain_name_type_ == FULL ?
540 impl_->domain_name_->getLength() :
541 impl_->domain_name_->getLength() - 1;
542
543 // ASCII encoded domain name. Its length is just a length of the
544 // string holding the name.
545 } else {
546 domain_name_length = getDomainName().length();
547 }
548 }
549
550 return (getHeaderLen() + FIXED_FIELDS_LEN + domain_name_length);
551}
552
553} // end of isc::dhcp namespace
554} // end of isc namespace
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
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 DHCPv4 Client FQDN Option.
Implements the logic for the Option6ClientFqdn class.
Option4ClientFqdn::DomainNameType domain_name_type_
Indicates whether domain name is partial or fully qualified.
Option4ClientFqdn::Rcode rcode1_
Holds RCODE1 and RCODE2 values.
Option4ClientFqdnImpl(const uint8_t flags, const Option4ClientFqdn::Rcode &rcode, const std::string &domain_name, const Option4ClientFqdn::DomainNameType name_type)
Constructor, from domain name.
static void checkFlags(const uint8_t flags, const bool check_mbz)
Check if flags are valid.
void setDomainName(const std::string &domain_name, const Option4ClientFqdn::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.
boost::shared_ptr< isc::dns::Name > domain_name_
Holds the pointer to a domain name carried in the option.
void parseCanonicalDomainName(OptionBufferConstIter first, OptionBufferConstIter last)
Parse domain-name encoded in the canonical format.
Option4ClientFqdn::Rcode rcode2_
Option4ClientFqdnImpl & operator=(const Option4ClientFqdnImpl &source)
Assignment operator.
uint8_t flags_
Holds flags carried by the option.
void parseASCIIDomainName(OptionBufferConstIter first, OptionBufferConstIter last)
Parse domain-name encoded in the deprecated ASCII format.
Represents the value of one of the RCODE1 or RCODE2 fields.
uint8_t getCode() const
Returns the value of the RCODE.
Represents DHCPv4 Client FQDN Option (code 81).
static const uint16_t FIXED_FIELDS_LEN
The size in bytes of the fixed fields within DHCPv4 Client Fqdn Option.
void setRcode(const Rcode &rcode)
Set Rcode value.
virtual uint16_t len() const
Returns length of the complete option (data length + DHCPv4 option header).
static const uint8_t FLAG_N
Bit N.
bool getFlag(const uint8_t flag) const
Checks if the specified flag of the DHCPv4 Client FQDN Option is set.
static const uint8_t FLAG_S
Bit S.
DomainNameType getDomainNameType() const
Returns enumerator value which indicates whether domain-name is partial or full.
virtual void unpack(OptionBufferConstIter first, OptionBufferConstIter last)
Parses option from the received buffer.
void packDomainName(isc::util::OutputBuffer &buf) const
Writes domain-name in the wire format into a buffer.
Option4ClientFqdn & operator=(const Option4ClientFqdn &source)
Assignment operator.
void setDomainName(const std::string &domain_name, const DomainNameType domain_name_type)
Set new domain-name.
static const uint8_t FLAG_MASK
Mask which zeroes MBZ flag bits.
Option4ClientFqdn(const uint8_t flags, const Rcode &rcode, const std::string &domain_name, const DomainNameType domain_name_type=FULL)
Constructor, creates option instance using flags and domain name.
virtual ~Option4ClientFqdn()
Destructor.
std::pair< Rcode, Rcode > getRcode() const
Returns Rcode objects representing value of RCODE1 and RCODE2.
void resetDomainName()
Set empty domain-name.
void setFlag(const uint8_t flag, const bool set)
Modifies the value of the specified DHCPv4 Client Fqdn Option flag.
static const uint8_t FLAG_O
Bit O.
virtual void pack(isc::util::OutputBuffer &buf, bool check=true) const
Writes option in the wire format into a buffer.
virtual OptionPtr clone() const
Copies this option and returns a pointer to the copy.
DomainNameType
Type of the domain-name: partial or full.
void resetFlags()
Sets the flag field value to 0.
static const uint8_t FLAG_E
Bit E.
std::string getDomainName() const
Returns the domain-name in the text format.
virtual std::string toText(int indent=0) const
Returns string representation of the option.
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
void check() const
A protected method used for option correctness.
Definition: option.cc:90
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
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
@ DHO_FQDN
Definition: dhcp4.h:150
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.