Coverage Report

Created: 2026-07-14 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea/src/lib/dhcp/option4_client_fqdn.h
Line
Count
Source
1
// Copyright (C) 2013-2025 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
#ifndef OPTION4_CLIENT_FQDN_H
8
#define OPTION4_CLIENT_FQDN_H
9
10
#include <dhcp/option.h>
11
#include <dns/name.h>
12
13
#include <string>
14
#include <utility>
15
16
namespace isc {
17
namespace dhcp {
18
19
/// @brief Exception thrown when invalid flags have been specified for
20
/// DHCPv4 Client FQDN %Option.
21
class InvalidOption4FqdnFlags : public Exception {
22
public:
23
    InvalidOption4FqdnFlags(const char* file, size_t line, const char* what) :
24
123
        isc::Exception(file, line, what) {}
25
};
26
27
/// @brief Exception thrown when invalid domain name is specified.
28
class InvalidOption4FqdnDomainName : public Exception {
29
public:
30
    InvalidOption4FqdnDomainName(const char* file, size_t line,
31
                                 const char* what) :
32
1.67k
        isc::Exception(file, line, what) {}
33
};
34
35
/// Forward declaration to implementation of @c Option4ClientFqdn class.
36
class Option4ClientFqdnImpl;
37
38
/// @brief Represents DHCPv4 Client FQDN %Option (code 81).
39
///
40
/// This option has been defined in the RFC 4702 and it has a following
41
/// structure:
42
/// - Code (1 octet) - option code (always equal to 81).
43
/// - Len (1 octet) - a length of the option.
44
/// - Flags (1 octet) - a field carrying "NEOS" flags described below.
45
/// - RCODE1 (1 octet) - deprecated field which should be set to 0 by the client
46
/// and set to 255 by the server.
47
/// - RCODE2 (1 octet) - deprecated, should be used in the same way as RCODE1.
48
/// - Domain Name - variable length field comprising partial or fully qualified
49
/// domain name.
50
///
51
/// The flags field has the following structure:
52
/// @code
53
///        0 1 2 3 4 5 6 7
54
///       +-+-+-+-+-+-+-+-+
55
///       |  MBZ  |N|E|O|S|
56
///       +-+-+-+-+-+-+-+-+
57
/// @endcode
58
/// where:
59
/// - N flag specifies whether server should (0) or should not (1) perform DNS
60
///  Update,
61
/// - E flag specifies encoding of the Domain Name field. If this flag is set
62
/// to 1 it indicates canonical wire format without compression. 0 indicates
63
/// the deprecated ASCII format.
64
/// - O flag is set by the server to indicate that it has overridden client's
65
/// preference set with the S bit.
66
/// - S flag specifies whether server should (1) or should not (0) perform
67
/// forward (FQDN-to-address) updates.
68
///
69
/// This class exposes a set of functions to modify flags and check their
70
/// correctness.
71
///
72
/// Domain names being carried by DHCPv4 Client Fqdn %Option can be fully
73
/// qualified or partial. Partial domain names are encoded similar to the
74
/// fully qualified domain names, except that they lack terminating zero
75
/// at the end of their wire representation (or lack of dot at the end, in
76
/// case of ASCII encoding). It is also accepted to create an instance of
77
/// this option which has empty domain-name. Clients use empty domain-names
78
/// to indicate that server should generate complete fully qualified
79
/// domain-name.
80
///
81
/// Since domain names are case insensitive (see RFC 4343), this class
82
/// converts them to lower case format regardless if they are received over
83
/// the wire or created from strings.
84
///
85
/// @warning: The RFC4702 section 2.3.1 states that the clients and servers
86
/// should use character sets specified in RFC952, section 2.1 for ASCII-encoded
87
/// domain-names. This class doesn't detect the character set violation for
88
/// ASCII-encoded domain-name. It could be implemented in the future but it is
89
/// not important now for two reasons:
90
/// - ASCII encoding is deprecated
91
/// - clients SHOULD obey restrictions but if they don't, server may still
92
///   process the option
93
///
94
/// RFC 4702 mandates that the DHCP client sets RCODE1 and RCODE2 to 0 and that
95
/// server sets them to 255. This class allows to set the value for these
96
/// fields and both fields are always set to the same value. There is no way
97
/// to set them separately (e.g. set different value for RCODE1 and RCODE2).
98
/// However, there are no use cases which would require it.
99
///
100
/// <b>Design choice:</b> This class uses pimpl idiom to separate the interface
101
/// from implementation specifics. Implementations may use different approaches
102
/// to handle domain names (mostly validation of the domain-names). The existing
103
/// @c isc::dns::Name class is a natural (and the simplest) choice to handle
104
/// domain-names. Use of this class however, implies that libdhcp must be linked
105
/// with libdns. At some point these libraries may need to be separated, i.e. to
106
/// support compilation and use of standalone DHCP server. This will require
107
/// that the part of implementation which deals with domain-names is modified to
108
/// not use classes from libdns. These changes will be transparent for this
109
/// interface.
110
class Option4ClientFqdn : public Option {
111
public:
112
113
    ///
114
    /// @name A set of constants used to identify and set bits in the flags field
115
    //@{
116
    static const uint8_t FLAG_S = 0x01; ///< Bit S
117
    static const uint8_t FLAG_O = 0x02; ///< Bit O
118
    static const uint8_t FLAG_E = 0x04; ///< Bit E
119
    static const uint8_t FLAG_N = 0x08; ///< Bit N
120
    //@}
121
122
    /// @brief Mask which zeroes MBZ flag bits.
123
    static const uint8_t FLAG_MASK = 0xF;
124
125
    /// @brief Represents the value of one of the RCODE1 or RCODE2 fields.
126
    ///
127
    /// Typically, RCODE values are set to 255 by the server and to 0 by the
128
    /// clients (as per RFC 4702).
129
    class Rcode {
130
    public:
131
        Rcode(const uint8_t rcode)
132
13.3k
            : rcode_(rcode) { }
133
134
        /// @brief Returns the value of the RCODE.
135
        ///
136
        /// Returned value can be directly used to create the on-wire format
137
        /// of the DHCPv4 Client FQDN %Option.
138
204
        uint8_t getCode() const {
139
204
            return (rcode_);
140
204
        }
141
142
    private:
143
        uint8_t rcode_;
144
    };
145
146
147
    /// @brief Type of the domain-name: partial or full.
148
    enum DomainNameType : uint16_t {
149
        PARTIAL,
150
        FULL
151
    };
152
153
    /// @brief The size in bytes of the fixed fields within DHCPv4 Client Fqdn
154
    /// %Option.
155
    ///
156
    /// The fixed fields are:
157
    /// - Flags
158
    /// - RCODE1
159
    /// - RCODE2
160
    static const uint16_t FIXED_FIELDS_LEN = 3;
161
162
    /// @brief Constructor, creates option instance using flags and domain name.
163
    ///
164
    /// This constructor is used to create an instance of the option which will
165
    /// be included in outgoing messages.
166
    ///
167
    /// Note that the RCODE values are encapsulated by the Rcode object (not a
168
    /// simple uint8_t value). This helps to prevent a caller from confusing the
169
    /// flags value with rcode value (both are uint8_t values). For example:
170
    /// if caller swaps the two, it will be detected in the compilation time.
171
    /// Also, this API encourages the caller to use two predefined functions:
172
    /// @c RCODE_SERVER and @c RCODE_CLIENT to set the value of RCODE. These
173
    /// functions generate objects which represent the only valid values to be
174
    /// be passed to the constructor (255 and 0 respectively). Other
175
    /// values should not be used. However, it is still possible that the other
176
    /// entity (client or server) sends the option with invalid value. Although,
177
    /// the RCODE values are ignored, there should be a way to represent such
178
    /// invalid RCODE value. The Rcode class is capable of representing it.
179
    ///
180
    /// @param flags a combination of flags to be stored in flags field.
181
    /// @param rcode @c Rcode object representing a value for RCODE1 and RCODE2
182
    /// fields of the option. Both fields are assigned the same value
183
    /// encapsulated by the parameter.
184
    /// @param domain_name a name to be stored in the domain-name field.
185
    /// @param domain_name_type indicates if the domain name is partial
186
    /// or full.
187
    /// @throw InvalidOption4FqdnFlags if value of the flags field is wrong.
188
    /// @throw InvalidOption4FqdnDomainName if the domain-name is invalid.
189
    explicit Option4ClientFqdn(const uint8_t flags,
190
                               const Rcode& rcode,
191
                               const std::string& domain_name,
192
                               const DomainNameType domain_name_type = FULL);
193
194
    /// @brief Constructor, creates option instance with empty domain name.
195
    ///
196
    /// This constructor creates an instance of the option with empty
197
    /// domain-name. This domain-name is marked partial.
198
    ///
199
    /// @param flags a combination of flags to be stored in flags field.
200
    /// @param rcode @c Rcode object representing a value for RCODE1 and RCODE2
201
    /// fields. Both fields are assigned the same value encapsulated by this
202
    /// parameter.
203
    /// @throw InvalidOption4FqdnFlags if value of the flags field is invalid.
204
    Option4ClientFqdn(const uint8_t flags, const Rcode& rcode);
205
206
    /// @brief Constructor, creates an option instance from part of the buffer.
207
    ///
208
    /// This constructor is mainly used to parse options in the received
209
    /// messages. Function parameters specify buffer bounds from which the
210
    /// option should be created. The size of the buffer chunk, specified by
211
    /// the constructor's parameters should be equal or larger than the size
212
    /// of the option. Otherwise, constructor will throw an exception.
213
    ///
214
    /// @param first the lower bound of the buffer to create option from.
215
    /// @param last the upper bound of the buffer to create option from.
216
    /// @throw InvalidOption4FqdnFlags if value of the flags field is invalid.
217
    /// @throw InvalidOption4FqdnDomainName if the domain-name carried by the
218
    /// option is invalid.
219
    /// @throw OutOfRange if the option is truncated.
220
    explicit Option4ClientFqdn(OptionBufferConstIter first,
221
                               OptionBufferConstIter last);
222
223
    /// @brief Copy constructor
224
    Option4ClientFqdn(const Option4ClientFqdn& source);
225
226
    /// @brief Copies this option and returns a pointer to the copy.
227
    virtual OptionPtr clone() const;
228
229
    /// @brief Destructor
230
    virtual ~Option4ClientFqdn();
231
232
    /// @brief Assignment operator
233
    Option4ClientFqdn& operator=(const Option4ClientFqdn& source);
234
235
    /// @brief Checks if the specified flag of the DHCPv4 Client FQDN %Option
236
    /// is set.
237
    ///
238
    /// @param flag A value specifying a bit within flags field to be checked.
239
    /// It must be one of the following @c FLAG_S, @c FLAG_E, @c FLAG_O,
240
    /// @c FLAG_N.
241
    ///
242
    /// @return true if the bit of the specified flags bit is set, false
243
    /// otherwise.
244
    /// @throw InvalidOption4ClientFlags if specified flag which value is to be
245
    /// returned is invalid (is not one of the FLAG_S, FLAG_N, FLAG_O).
246
    bool getFlag(const uint8_t flag) const;
247
248
    /// @brief Modifies the value of the specified DHCPv4 Client Fqdn %Option
249
    /// flag.
250
    ///
251
    /// @param flag A value specifying a bit within flags field to be set. It
252
    /// must be one of the following @c FLAG_S, @c FLAG_E, @c FLAG_O, @c FLAG_N.
253
    /// @param set a boolean value which indicates whether flag should be
254
    /// set (true), or cleared (false).
255
    /// @throw InvalidOption4ClientFlags if specified flag which value is to be
256
    /// set is invalid (is not one of the FLAG_S, FLAG_N, FLAG_O).
257
    void setFlag(const uint8_t flag, const bool set);
258
259
    /// @brief Sets the flag field value to 0.
260
    void resetFlags();
261
262
    /// @brief Returns @c Rcode objects representing value of RCODE1 and RCODE2.
263
    ///
264
    /// @return Pair of Rcode objects of which first is the RCODE1 and the
265
    /// second is RCODE2.
266
    std::pair<Rcode, Rcode> getRcode() const;
267
268
    /// @brief Set Rcode value.
269
    ///
270
    /// @param rcode An @c Rcode object representing value of RCODE1 and RCODE2.
271
    /// Both fields are assigned the same value.
272
    void setRcode(const Rcode& rcode);
273
274
    /// @brief Returns the domain-name in the text format.
275
    ///
276
    /// If domain-name is partial, it lacks the dot at the end (e.g. myhost).
277
    /// If domain-name is fully qualified, it has the dot at the end (e.g.
278
    /// myhost.example.com.).
279
    ///
280
    /// @return domain-name in the text format.
281
    std::string getDomainName() const;
282
283
    /// @brief Writes domain-name in the wire format into a buffer.
284
    ///
285
    /// The data being written are appended at the end of the buffer.
286
    ///
287
    /// @param [out] buf buffer where domain-name will be written.
288
    void packDomainName(isc::util::OutputBuffer& buf) const;
289
290
    /// @brief Set new domain-name.
291
    ///
292
    /// @param domain_name domain name field value in the text format.
293
    /// @param domain_name_type type of the domain name: partial or fully
294
    /// qualified.
295
    /// @throw InvalidOption4FqdnDomainName if the specified domain-name is
296
    /// invalid.
297
    void setDomainName(const std::string& domain_name,
298
                       const DomainNameType domain_name_type);
299
300
    /// @brief Set empty domain-name.
301
    ///
302
    /// This function is equivalent to @c Option6ClientFqdn::setDomainName
303
    /// with empty partial domain-name. It is exception safe.
304
    void resetDomainName();
305
306
    /// @brief Returns enumerator value which indicates whether domain-name is
307
    /// partial or full.
308
    ///
309
    /// @return An enumerator value indicating whether domain-name is partial
310
    /// or full.
311
    DomainNameType getDomainNameType() const;
312
313
    /// @brief Writes option in the wire format into a buffer.
314
    ///
315
    /// @param [out] buf output buffer where option data will be stored.
316
    /// @param check if set to false, allows options larger than 255 for v4
317
    virtual void pack(isc::util::OutputBuffer& buf, bool check = true) const;
318
319
    /// @brief Parses option from the received buffer.
320
    ///
321
    /// Method creates an instance of the DHCPv4 Client FQDN %Option from the
322
    /// wire format. Parameters specify the bounds of the buffer to read option
323
    /// data from. The size of the buffer limited by the specified parameters
324
    /// should be equal or larger than size of the option (including its
325
    /// header). Otherwise exception will be thrown.
326
    ///
327
    /// @param first lower bound of the buffer to parse option from.
328
    /// @param last upper bound of the buffer to parse option from.
329
    virtual void unpack(OptionBufferConstIter first,
330
                        OptionBufferConstIter last);
331
332
    /// @brief Returns string representation of the option.
333
    ///
334
    /// The string returned by the method comprises the bit value of each
335
    /// option flag and the domain-name.
336
    ///
337
    /// @param indent number of spaces before printed text.
338
    ///
339
    /// @return string with text representation.
340
    virtual std::string toText(int indent = 0) const;
341
342
    /// @brief Returns length of the complete option (data length +
343
    /// DHCPv4 option header).
344
    ///
345
    /// @return length of the option.
346
    virtual uint16_t len() const;
347
348
    ///
349
    /// @name Well known Rcode declarations for DHCPv4 Client FQDN %Option
350
    ///
351
    //@{
352
    /// @brief Rcode being set by the server.
353
0
    inline static const Rcode& RCODE_SERVER() {
354
0
        static Rcode rcode(255);
355
0
        return (rcode);
356
0
    }
357
358
    /// @brief Rcode being set by the client.
359
13.3k
    inline static const Rcode& RCODE_CLIENT() {
360
13.3k
        static Rcode rcode(0);
361
13.3k
        return (rcode);
362
13.3k
    }
363
    //@}
364
365
private:
366
367
    /// @brief A pointer to the implementation.
368
    Option4ClientFqdnImpl* impl_;
369
};
370
371
/// A pointer to the @c Option4ClientFqdn object.
372
typedef boost::shared_ptr<Option4ClientFqdn> Option4ClientFqdnPtr;
373
374
} // namespace isc::dhcp
375
} // namespace isc
376
377
#endif // OPTION4_CLIENT_FQDN_H