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