/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 |